import FreeCAD as App import uuid import os import json from typing import List, Dict, Any, TypeVar, Callable, Type, cast class FreeCadRepository: _solids = [] def getAllSolids(self): if (self._solids.__len__() == 0): for part in App.ActiveDocument.Objects: if (self.is_object_solid(part)): self._solids.append(part) return self._solids def is_object_solid(self, obj): if not isinstance(obj, App.DocumentObject): return False if hasattr(obj, 'Group'): return False if not hasattr(obj, 'Shape'): return False if not hasattr(obj.Shape, 'Mass'): return False if not hasattr(obj.Shape, 'Solids'): return False if len(obj.Shape.Solids) == 0: return False return True T = TypeVar("T") def from_list(f: Callable[[Any], T], x: Any) -> List[T]: assert isinstance(x, list) return [f(y) for y in x] def from_str(x: Any) -> str: assert isinstance(x, str) return x def from_dict(f: Callable[[Any], T], x: Any) -> Dict[str, T]: assert isinstance(x, dict) return {k: f(v) for (k, v) in x.items()} def to_class(c: Type[T], x: Any) -> dict: assert isinstance(x, c) return cast(Any, x).to_dict() class AdjacencyMatrix: matrixError: Dict[str,str] = {} all_parts: List[str] first_detail: str matrix: Dict[str, List[str]] def __init__(self, all_parts: List[str], first_detail: str, matrix: Dict[str, List[str]]) -> None: self.all_parts = all_parts self.first_detail = first_detail self.matrix = matrix self.validateMatrix() def whatPlaceLeadingPartIndex(self): i = 0 for el in self.matrix: if el == self.first_detail: return i i = +1 def validateMatrix(self): for el in self.all_parts: if(self.matrix.get(el) == None): self.matrixError[el] = 'Not found adjacency ' + el @staticmethod def from_dict(obj: Any) -> 'AdjacencyMatrix': assert isinstance(obj, dict) all_pars = from_list(from_str, obj.get("allPars")) first_detail = from_str(obj.get("firstDetail")) matrix = from_dict(lambda x: from_list(from_str, x), obj.get("matrix")) return AdjacencyMatrix(all_pars, first_detail, matrix) def to_dict(self) -> dict: result: dict = {} result["allPars"] = from_list(from_str, self.all_parts) result["firstDetail"] = from_str(self.first_detail) result["matrix"] = from_dict( lambda x: from_list(from_str, x), self.matrix) if(self.matrixError.values().__len__() == 0): result['matrixError'] = None else: result['matrixError'] = self.matrixError return result def getDictMatrix(self) -> dict: result = {} for k, v in self.matrix.items(): result[k] = {} for el in v: result[k][el] = el return result def adjacency_matrix_from_dict(s: Any) -> AdjacencyMatrix: return AdjacencyMatrix.from_dict(s) def adjacency_matrix_to_dict(x: AdjacencyMatrix) -> Any: return to_class(AdjacencyMatrix, x) class FreeCadMetaModel(object): def __init__(self, label, vertex) -> None: self.label = label self.vertex = vertex collision_squares_labels = [] class MeshGeometryCoordinateModel(object): # Получение геометрии мешей def __init__(self, x, y, z, label,): self.x = x self.y = y self.z = z self.label = label self.cadLabel = '' def initializePrimitivesByCoordinate(self, detailSquares): uuidDoc = str(uuid.uuid1()) App.ActiveDocument.addObject("Part::Box", "Box") App.ActiveDocument.ActiveObject.Label = uuidDoc App.ActiveDocument.recompute() part = App.ActiveDocument.getObjectsByLabel(uuidDoc)[0] collision_squares_labels.append(uuidDoc) part.Width = 2 part.Height = 2 part.Length = 2 part.Placement = App.Placement( App.Vector(self.x - 1, self.y - 1, self.z - 1), App.Rotation(App.Vector(0.00, 0.00, 1.00), 0.00)) if (detailSquares.get(self.label) is None): detailSquares[self.label] = [] detailSquares[self.label].append(self) self.cadLabel = uuidDoc App.ActiveDocument.recompute() class FS: def readJSON(path: str): return json.loads((open(path)).read()) def writeFile(data, filePath, fileName): file_to_open = filePath + fileName f = open(file_to_open, 'w', encoding='utf8') f.write(data) def readFile(path: str): return open(path).read() def readFilesTypeFolder(pathFolder: str, fileType='.json'): filesJson = list( filter(lambda x: x[-fileType.__len__():] == fileType, os.listdir(pathFolder))) return filesJson class GetAllPartsLabelsUseCase: # Получение всех названий деталей def call(self): parts = [] for part in FreeCadRepository().getAllSolids(): parts.append(part.Label) return parts def isUnique(array, element): for i in array: if i == element: return False return True class GetCollisionAtPrimitiveUseCase(object): # Получение колизий примитивов def call(self, freeCadMetaModels, detailSquares) -> Dict[str, List[str]]: matrix: Dict[str, List[str]] = {} for model in freeCadMetaModels: activePart = App.ActiveDocument.getObjectsByLabel(model.label)[0] for key in detailSquares: if (model.label != key): for renderPrimitive in detailSquares[key]: primitivePart = App.ActiveDocument.getObjectsByLabel( renderPrimitive.cadLabel)[0] collisionResult: int = int( activePart.Shape.distToShape(primitivePart.Shape)[0]) if (collisionResult == 0): if matrix.get(model.label) == None: matrix[model.label] = [renderPrimitive.label] else: if isUnique(matrix[model.label], renderPrimitive.label): matrix[model.label].append( renderPrimitive.label ) return matrix class GetFirstDetailUseCase: # Получение первой детали def call(self): return FreeCadRepository().getAllSolids()[0].Label class GetPartPrimitiveCoordinatesUseCase(object): # Получение координат примитивов def call(self, freeCadMetaModels): meshCoordinates: list[MeshGeometryCoordinateModel] = [] for model in freeCadMetaModels: vertexesDetail = model.vertex labelDetail = model.label for coords in vertexesDetail: detailVertex = MeshGeometryCoordinateModel( coords.X, coords.Y, coords.Z, labelDetail, ) meshCoordinates.append(detailVertex) return meshCoordinates class InitPartsParseUseCase(): # Инициализация парсинга def call(self): product_details = [] for part in FreeCadRepository().getAllSolids(): if part is not None: model = FreeCadMetaModel(part.Label, part.Shape.Vertexes) if (model is not None): product_details.append(model) return product_details class RenderPrimitiveUseCase(object): # Рендеринг премитивов def call(self, meshModels: list[MeshGeometryCoordinateModel], detailSquares) -> None: for mesh in meshModels: mesh.initializePrimitivesByCoordinate(detailSquares) class ClearWorkSpaceDocumentUseCase(object): # Очистка рабочего простарнства def call(self, detailSquares): for key in detailSquares: for renderPrimitive in detailSquares[key]: primitivePart = App.ActiveDocument.getObjectsByLabel( renderPrimitive.cadLabel)[0] App.ActiveDocument.removeObject(primitivePart.Name) class RenderPrimitivesScenario(object): def __init__( self, initPartsParseUseCase: InitPartsParseUseCase, getPartPrimitiveCoordinatesUseCase: GetPartPrimitiveCoordinatesUseCase, renderPrimitiveUseCase: RenderPrimitiveUseCase, getCollisionAtPrimitives: GetCollisionAtPrimitiveUseCase, clearWorkSpaceDocument: ClearWorkSpaceDocumentUseCase, ) -> None: self.initPartsParseUseCase = initPartsParseUseCase self.getPartPrimitiveCoordinatesUseCase = getPartPrimitiveCoordinatesUseCase self.renderPrimitiveUseCase = renderPrimitiveUseCase self.getCollisionAtPrimitives = getCollisionAtPrimitives self.clearWorkSpaceDocument = clearWorkSpaceDocument def call(self) -> None: meshCoordinates = [] detailSquares = {} parts = self.initPartsParseUseCase.call() meshCoordinates = self.getPartPrimitiveCoordinatesUseCase.call(parts) self.renderPrimitiveUseCase.call(meshCoordinates, detailSquares) matrix = self.getCollisionAtPrimitives.call(parts, detailSquares) self.clearWorkSpaceDocument.call(detailSquares) return matrix class ClearWorkSpaceDocumentUseCase(object): # Очистака рабочего пространства def call(self, detailSquares): for key in detailSquares: for renderPrimitive in detailSquares[key]: primitivePart = App.ActiveDocument.getObjectsByLabel( renderPrimitive.cadLabel)[0] App.ActiveDocument.removeObject(primitivePart.Name) class CadAdjacencyMatrix: # Матрица основанная на соприкосновении примитива с обьектами def primitiveMatrix(self): # Получение матрицы matrix = RenderPrimitivesScenario( InitPartsParseUseCase(), GetPartPrimitiveCoordinatesUseCase(), RenderPrimitiveUseCase(), GetCollisionAtPrimitiveUseCase(), ClearWorkSpaceDocumentUseCase(), ).call() return AdjacencyMatrix( all_parts=GetAllPartsLabelsUseCase().call(), first_detail=GetFirstDetailUseCase().call(), matrix=matrix, ) # Матрица основанная на соприкосновении обьектов def matrixBySurfaces(self): matrix = {} for part in FreeCadRepository().getAllSolids(): matrix[part.Label] = [] for nextPart in FreeCadRepository().getAllSolids(): if part.Label != nextPart.Label: # Вычисление соприконсоновения площади деталей collisionResult: int = int( part.Shape.distToShape(nextPart.Shape)[0]) if (collisionResult == 0): matrix[part.Label].append(nextPart.Label) return AdjacencyMatrix(all_parts=GetAllPartsLabelsUseCase( ).call(), first_detail=GetFirstDetailUseCase().call(), matrix=matrix ) def main(): env = FS.readJSON('env.json') cadFile = env['cadFilePath'] outPath = env['outPath'] if (cadFile == None): return TypeError('CadFile not found env.json') App.open(u'' + cadFile) # Получение матрицы matrixOut = CadAdjacencyMatrix().primitiveMatrix().to_dict() import json # Запись результата FS.writeFile(json.dumps(matrixOut, ensure_ascii=False, indent=4), outPath,'out.json') main()