import FreeCAD as App import uuid import os import json from typing import List, Dict, Any, TypeVar, Callable, Type, cast from itertools import repeat from abc import ABC, abstractmethod from dataclasses import dataclass from typing import Any, TypeVar, Type, cast T = TypeVar("T") class BackgroundConsoleColors: HEADER = "\033[95m" OKBLUE = "\033[94m" OKCYAN = "\033[96m" OKGREEN = "\033[92m" WARNING = "\033[93m" FAIL = "\033[91m" ENDC = "\033[0m" BOLD = "\033[1m" UNDERLINE = "\033[4m" def from_float(x: Any) -> float: assert isinstance(x, (float, int)) and not isinstance(x, bool) return float(x) def to_float(x: Any) -> float: assert isinstance(x, float) return x def from_str(x: Any) -> str: assert isinstance(x, str) return x def from_int(x: Any) -> int: assert isinstance(x, int) and not isinstance(x, bool) return x def to_class(c: Type[T], x: Any) -> dict: assert isinstance(x, c) return cast(Any, x).to_dict() @dataclass class Env: cadFilePath: str outPath: str solidBodyPadding: float firstDetail: str sequencesFixed: list[list[str]] @staticmethod def from_dict(obj: Any) -> "Env": assert isinstance(obj, dict) cadFilePath = from_str(obj.get("cadFilePath")) outPath = from_str(obj.get("outPath")) solidBodyPadding = from_float(obj.get("solidBodyPadding")) firstDetail = from_str(obj.get("firstDetail")) sequencesFixed = [] sequencesFixedParse = CoreList(obj.get("sequencesFixed")) for el in sequencesFixedParse: sequencesFixed.append(el) restrictionsOnFasteners = CoreList(obj.get("restrictionsOnFasteners")) for el in restrictionsOnFasteners: for part in el["parts"]: sequencesFixed.append([part, el["fastener"]]) return Env( cadFilePath, outPath, solidBodyPadding, firstDetail, sequencesFixed, ) def to_dict(self) -> dict: result: dict = {} result["cadFilePath"] = from_str(self.cadFilePath) result["outPath"] = from_str(self.outPath) result["solidBodyPadding"] = from_float(self.solidBodyPadding) result["firstDetail"] = from_str(self.firstDetail) result["sequencesFixed"] = self.sequencesFixed return result class Either(ABC): @abstractmethod def isLeft(self): pass @abstractmethod def isRight(self): pass @abstractmethod def getLeft(self): pass @abstractmethod def getRight(self): pass @abstractmethod def mapLeft(self, lf): pass @abstractmethod def mapRight(self, rf): pass @abstractmethod def either(self, leftF, rightF): pass class Left(Either): def __init__(self, lvalue): self.lvalue = lvalue def isLeft(self): return True def isRight(self): return False def getLeft(self): return self.lvalue def getRight(self): raise Error("Cannot get a right value out of Left.") def mapLeft(self, lf): return Left(lf(self.lvalue)) def mapRight(self, rf): return Left(self.lvalue) def either(self, leftF, rightF): return leftF(self.lvalue) class Right(Either): def __init__(self, rvalue): self.rvalue = rvalue def isLeft(self): return False def isRight(self): return True def getLeft(self): raise Error("Cannot get a left value out of Right.") def getRight(self): return self.rvalue def mapLeft(self, lf): return Right(self.rvalue) def mapRight(self, rf): return Right(rf(self.rvalue)) def either(self, leftF, rightF): return rightF(self.rvalue) class CoreDict(dict): def isEquivalentByKeys(self, checkDict) -> bool: print(checkDict) for key in self: value = checkDict.get(key) if value is None: return False return True def isMatchByKeys(self, checkList): if len(self) != len(checkList): return False sorted_dict_keys = sorted(self.keys()) sorted_list_elements = sorted(checkList) if sorted_dict_keys != sorted_list_elements: missing_keys = [ key for key in sorted_list_elements if key not in sorted_dict_keys ] print(f"Отсутствующие ключи в словаре: {missing_keys}") return False return True class CoreList(List): def onlyTrue(self) -> bool: for el in self: if el is True: return True return False def onlyUniqueElementAppend(self, el): if el is None: return if self.is_element_in_array(el) is False: self.append(el) pass def is_element_in_array(self, element): return element in self def equal(self, array: list) -> bool: if len(self) != len(array): return False return self.sort() == array.sort() def isConstrainsString(self) -> bool: for el in self: if isinstance(el, str): return True return False def indexedPriorities(self, lowerPriority, highPriority) -> bool: try: lowerIndex = self.index(lowerPriority) highPriorityIndex = self.index(highPriority) return lowerIndex < highPriorityIndex except: return False def getAllString(self) -> list[str]: return list(filter(lambda x: isinstance(x, str), self)) def onlyUnique(self) -> list: result = [] [result.append(x) for x in self if x not in self] return result def spreadArray(self) -> "CoreList": unpacked_array = CoreList([]) for el in self: if isinstance(el, list): unpacked_array.extend(el) else: unpacked_array.append(el) return unpacked_array def isInListRange(listIn, index): try: listIn[index] return False except: return True class AllSequences: all_sequences = None adj_matrix = None topologyIds = None adj_matrix_names = None def __init__(self, adj_matrix, restrictions: list[str]) -> None: self.adj_matrix = adj_matrix self.all_possible_sequences(self.adj_matrix) self.matrix_by_name() if restrictions.__len__() != 0: self.restrictionsValidate(restrictions) pass def restrictionsValidate(self, restrictions: CoreList[str]): filterMatrix = CoreList() for el in self.adj_matrix_names: result = False for restraint in restrictions: result = CoreList(el).indexedPriorities(restraint[0], restraint[1]) if result: filterMatrix.onlyUniqueElementAppend(el) self.adj_matrix_names = filterMatrix pass def matrix_by_name(self): result = self.all_sequences inc = 0 for matrix in self.all_sequences: for index in range(len(matrix)): result[inc][index] = CoreList( filter( lambda el: el.get("number") == matrix[index] + 1, self.topologyIds, ) )[0].get("name") inc += 1 self.adj_matrix_names = result pass def find_all_sequences(self, adj_matrix): sequences = [] num_vertices = len(adj_matrix) def dfs(vertex, sequence): sequence.append(vertex) if len(sequence) == num_vertices: sequences.append(sequence) return for i in range(num_vertices): if adj_matrix[vertex][i] == 1 and i not in sequence: dfs(i, sequence.copy()) for i in range(num_vertices): dfs(i, []) self.all_sequences = sequences def findId(self, listMatrix, id): def filter_odd_num(in_num): if in_num["name"] == id: return True else: return False return list(filter(filter_odd_num, listMatrix))[0]["number"] def iter_paths(self, adj, min_length=6, path=None): if not path: for start_node in range(len(adj)): yield from self.iter_paths(adj, min_length, [start_node]) else: if len(path) >= min_length: yield path if path[-1] in path[:-1]: return current_node = path[-1] for next_node in range(len(adj[current_node])): if adj[current_node][next_node] == 1: yield from self.iter_paths(adj, min_length, path + [next_node]) def allUnique(self, x): seen = set() return not any(i in seen or seen.add(i) for i in x) def all_possible_sequences(self, matrix): topologyIds = [] topologyMatrixNumber = {} inc = 0 for k, v in matrix.items(): inc += 1 topologyIds.append({"name": k, "number": inc}) inc = 0 for k, v in matrix.items(): inc += 1 topologyMatrixNumber[inc] = list( map(lambda el: self.findId(topologyIds, el), v) ) self.topologyIds = topologyIds adj = [] matrixSize = matrix.keys().__len__() inc = 0 for k, v in topologyMatrixNumber.items(): adj.append(list(repeat(0, matrixSize))) for el in v: adj[inc][el - 1] = 1 inc += 1 return self.find_all_sequences(adj) class VectorModel: x: float y: float z: float def __init__(self, cadVector) -> None: self.x = cadVector[0] self.y = cadVector[1] self.z = cadVector[2] pass def toFreeCadVector(self): return App.Vector(self.x, self.y, self.z) def toString(self): return str("x:" + str(self.x) + "y:" + str(self.y) + "z:" + str(self.z)) class FreeCadRepository: _solids = [] def openDocument(self, path: str): App.open("" + path) def closeIfOpenDocument(self): # print('Проверка на документ') try: if App.ActiveDocument is not None: # print(App.ActiveDocument.name + "закрыт") # App.closeDocument(App.ActiveDocument.name) App.ActiveDocument.clearDocument() except Exception as e: print(e) def getAllLabelsSolids(self) -> List[str]: return list(map(lambda el: el.Label, self.getAllSolids())) def isAllObjectsSolids(self) -> List[str]: result = [] for part in App.ActiveDocument.Objects: if self.is_object_solid(part) is False: result.append(part.Label) return result def objectSetPosition(self, solid, cadVector): solid.Placement.Base = cadVector pass def objectGetPosition(self, solid) -> VectorModel: return VectorModel(cadVector=solid.Placement.Base) def isObjectIntersections(self, part) -> str: result = [] for solid in self.getAllSolids(): if solid.ID != part.ID: collisionResult: int = int(part.Shape.distToShape(solid.Shape)[0]) if collisionResult == 0: result.append(solid.Label) if result.__len__() == 0: return None return result def objectHasTouches(self, part, solidBodyPadding: float) -> List[str]: try: positionVector = self.objectGetPosition(part) result = CoreList() result.append(self.isObjectIntersections(part=part)) if solidBodyPadding != 0 and solidBodyPadding != None: result.append( self.axis_movement_and_intersections_observer( positionVector=positionVector, alongAxis="x", solidBodyPadding=solidBodyPadding, part=part, ) ) result.append( self.axis_movement_and_intersections_observer( positionVector=positionVector, alongAxis="y", solidBodyPadding=solidBodyPadding, part=part, ) ) result.append( self.axis_movement_and_intersections_observer( positionVector=positionVector, alongAxis="z", solidBodyPadding=solidBodyPadding, part=part, ) ) spreadArr = result.spreadArray() if spreadArr.isConstrainsString(): return spreadArr.getAllString() return None except Exception as error: print(error) return None def axis_movement_and_intersections_observer( self, positionVector: VectorModel, alongAxis: str, solidBodyPadding: float, part, ) -> bool: result = CoreList() # UP positionVector.__setattr__( alongAxis, positionVector.__getattribute__(alongAxis) + solidBodyPadding, ) self.objectSetPosition(part, positionVector.toFreeCadVector()) # result.onlyUniqueElementAppend(self.isObjectIntersections(part=part)) result.extend(self.isObjectIntersections(part=part)) # RESET UP CHANGES positionVector.__setattr__( alongAxis, positionVector.__getattribute__(alongAxis) - solidBodyPadding, ) self.objectSetPosition(part, positionVector.toFreeCadVector()) # DOWN positionVector.__setattr__( alongAxis, positionVector.__getattribute__(alongAxis) - solidBodyPadding, ) self.objectSetPosition(part, positionVector.toFreeCadVector()) # CHECK DOWN INTERSECTIONS # result.onlyUniqueElementAppend(self.isObjectIntersections(part=part)) result.extend(self.isObjectIntersections(part=part)) # RESET DOWN CHANGES positionVector.__setattr__( alongAxis, positionVector.__getattribute__(alongAxis) + solidBodyPadding, ) self.objectSetPosition(part, positionVector.toFreeCadVector()) if result.__len__() == 0: return None return result.onlyUnique() 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, "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() # Вспомогательный класс который делает генрацию JSON на основе пайтон обьектов class AdjacencyMatrix: matrixError: Dict[str, str] = {} all_parts: List[str] first_detail: str matrix: Dict[str, List[str]] fileName = "adjacency_matrix.json" 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 matrixToFileSystem(self, path: str): FileSystemRepository.writeFile( json.dumps(self.to_dict(), ensure_ascii=False, indent=4), path, AdjacencyMatrix.fileName, ) pass def sequencesToFileSystem(self, path: str, restrictions: list[str]): FileSystemRepository.writeFile( json.dumps( {"sequences": AllSequences(self.matrix, restrictions).adj_matrix_names}, ensure_ascii=False, indent=4, ), path, "sequences.json", ), pass def matrixGetUniqueContact(self): detailsToCheck = [] detailsHashCheck = {} for k, v in self.matrix.items(): for el in v: if el != k: hash = to_ascii_hash(k + el) if detailsHashCheck.get(hash) == None: detailsHashCheck[hash] = hash detailsToCheck.append({"child": el, "parent": k}) return detailsToCheck 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("allParts")) 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["allParts"] = 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) # Вспомогательный класс для работы с Freecad 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 FileSystemRepository: 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 reduce(function, iterable, initializer=None): it = iter(iterable) if initializer is None: value = next(it) else: value = initializer for element in it: value = function(value, element) return value def to_ascii_hash(text): ascii_values = [ord(character) for character in text] return reduce(lambda x, y: x + y, ascii_values) class IntersectionComputedUseCase: def call(parts): App.activeDocument().addObject("Part::MultiCommon", "Common") App.activeDocument().Common.Shapes = [parts[0], parts[1]] App.activeDocument().getObject("Common").ViewObject.ShapeColor = getattr( parts[0].getLinkedObject(True).ViewObject, "ShapeColor", App.activeDocument().getObject("Common").ViewObject.ShapeColor, ) App.activeDocument().getObject("Common").ViewObject.DisplayMode = getattr( parts[0].getLinkedObject(True).ViewObject, "DisplayMode", App.activeDocument().getObject("Common").ViewObject.DisplayMode, ) App.ActiveDocument.recompute() area = App.activeDocument().getObject("Common").Shape.Area App.ActiveDocument.removeObject("Common") return area class ErrorStringModel: def __init__(self, error: str) -> None: self.error = error pass error: str def toString(self) -> str: return json.dumps( { "error": self.error, }, ensure_ascii=False, indent=4, ) def toFileSystem(self, path: str): return ( FileSystemRepository.writeFile(self.toString(), path, "error.json"), ExitFreeCadUseCase.call(), ) class IsAllObjectSolidsCheckUseCase: def call() -> Either: result = FreeCadRepository().isAllObjectsSolids() if result.__len__() == 0: return Left(None) return Right( ErrorStringModel(error="Is not solid objects: " + ",".join(result)) ) class CheckObjectHasTouchesUseCase: freeCadRepository = FreeCadRepository() def call(self, solidBodyPadding: float) -> Either: try: errorResult = [] matrix = {} for part in self.freeCadRepository.getAllSolids(): matrix[part.Label] = [] touches = FreeCadRepository().objectHasTouches( part=part, solidBodyPadding=solidBodyPadding ) matrix[part.Label].extend(touches) if errorResult.__len__() == 0: return Left( AdjacencyMatrix( all_parts=self.freeCadRepository.getAllLabelsSolids(), first_detail=GetFirstDetailUseCase().call(), matrix=matrix, ) ) else: return Right( ErrorStringModel( error="Solids bodies have no recounts: " + ",".join(errorResult) ) ) except Exception as error: print(error) print("CheckObjectHasTouchesUseCase error") return Right(ErrorStringModel(error="CheckObjectHasTouchesUseCase error")) class CheckCadIntersectionObjects: report = [] def call() -> bool: FreeCadRepository().getAllSolids() return False class ExitFreeCadUseCase: def call(): import FreeCADGui as Gui App.ActiveDocument.clearDocument() freecadQTWindow = Gui.getMainWindow() freecadQTWindow.close() # class CheckValidIntersectionUseCase: # def call() -> ErrorStringModel: # for part in FreeCadRepository().getAllSolids(): # print(part) # FreeCadRepository().obj # pass class EnvReaderUseCase: def call() -> Either: try: return Left(Env.from_dict(FileSystemRepository.readJSON("env.json"))) except: print("env reader error") return Right(None) class OpenFreeCadDocumentUseCase: def call(path: str) -> Either: try: FreeCadRepository().closeIfOpenDocument() FreeCadRepository().openDocument(path) return Left(None) except: print("OpenFreeCadDocumentUseCase error") return Right(None) class IntersectionGeometryUseCase: def call(contacts, path): intersection_geometry = {"status": True, "recalculations": None} for el in contacts: child = App.ActiveDocument.getObjectsByLabel(el.get("child"))[0] parent = App.ActiveDocument.getObjectsByLabel(el.get("parent"))[0] area = IntersectionComputedUseCase.call([child, parent]) if area != 0.0: if intersection_geometry.get("recalculations") == None: intersection_geometry["status"] = False intersection_geometry["recalculations"] = [] intersection_geometry["recalculations"].append( {"area": area, "connect": el.get("child") + " " + el.get("parent")} ) FileSystemRepository.writeFile( json.dumps(intersection_geometry, ensure_ascii=False, indent=4), path, "intersection_geometry.json", ) def main(): try: EnvReaderUseCase.call().either( leftF=lambda environment: ( OpenFreeCadDocumentUseCase.call(environment.cadFilePath).either( leftF=lambda _: ( IsAllObjectSolidsCheckUseCase.call().either( leftF=lambda _: ( CheckObjectHasTouchesUseCase() .call(environment.solidBodyPadding) .either( leftF=lambda adjaxedMatrix: ( adjaxedMatrix.sequencesToFileSystem( environment.outPath, environment.sequencesFixed, ), IntersectionGeometryUseCase.call( adjaxedMatrix.matrixGetUniqueContact(), environment.outPath, ), adjaxedMatrix.matrixToFileSystem( environment.outPath, ), ExitFreeCadUseCase.call(), ), rightF=lambda error: error.toFileSystem( environment.outPath ), ), ), rightF=lambda error: error.toFileSystem( environment.outPath ), ), ), rightF=lambda error: print(error), ), ), rightF=lambda error: print(error), ) except Exception as error: print(error) ExitFreeCadUseCase.call() # main() class ReadFileSystemAndGetInstanceModelUseCase: def call(self, model, path): if hasattr(model, "from_dict") is False: return Right( "ReadFileSystemAndGetInstanceModelUseCase error:" + model + "is not have method" + "from_dict()" ) try: return Left(model.from_dict(FileSystemRepository.readJSON(path))) except: error = str(model) + " " + "from dict error " + "path: " + path print("ReadFileSystemAndGetInstanceModelUseCase error" + error) return Right(error) pass class FreeCadTest: testName: str def testHelper(self, testResult): if isinstance(testResult, bool) is False: print( BackgroundConsoleColors.WARNING, self.testName + " expected a value of type Boolean, returned a value of type below ", ) print(testResult) return if testResult: print(BackgroundConsoleColors.OKGREEN, self.testName + "is complete!") else: print(BackgroundConsoleColors.FAIL, self.testName + " is Error!") pass class FreeCadASPGenerationTestController(FreeCadTest): testName: str def __init__(self, testName: str) -> None: self.testName = testName pass def test( self, assertFn, documentPath: str, modelName: str, model, execComposition, outPath=os.path.dirname(__file__) + "/out/", ): try: OpenFreeCadDocumentUseCase.call(documentPath).either( leftF=lambda _: ( execComposition(""), ReadFileSystemAndGetInstanceModelUseCase() .call(model=model, path=outPath + modelName) .either( leftF=lambda model: ( self.testHelper( assertFn(model), ) ), rightF=lambda error: print(error), ), ), rightF=lambda error: print(error), ) except Exception as inst: print("FreeCadASPGenerationTestController error") print(inst) pass def test(): try: mocksFolder = os.path.dirname(__file__) + "/mocks/" outFolder = os.path.dirname(__file__) + "/out/" # FreeCadASPGenerationTestController("test adjaxed matrix simple cube").test( # assertFn=lambda model: CoreList(model.all_parts).equal(["Куб", "Куб001"]), # execComposition=lambda _: ( # CheckObjectHasTouchesUseCase() # .call(0) # .either( # leftF=lambda matrix: matrix.matrixToFileSystem(outFolder), # rightF=lambda error: print(error), # ) # ), # documentPath=mocksFolder + "simple_assembly_with_two_cubes.FCStd", # modelName=AdjacencyMatrix.fileName, # model=AdjacencyMatrix, # ) FreeCadASPGenerationTestController( "test adjaxed matrix vs structure of document" ).test( assertFn=lambda model: CoreDict(model.matrix).isEquivalentByKeys( { "Бутылочный домкрат 4т_Гильза": [ "Бутылочный домкрат 4т_Тяга", "Бутылочный домкрат 4т_Тяга001", "Бутылочный домкрат 4т_Шток насоса", "Бутылочный домкрат 4т_Шток", "Бутылочный домкрат 4т_Вентиль", ], "Бутылочный домкрат 4т_Тяга": [ "Бутылочный домкрат 4т_Гильза", "Бутылочный домкрат 4т_Тяга001", "Бутылочный домкрат 4т_Коромысло", ], "Бутылочный домкрат 4т_Тяга001": [ "Бутылочный домкрат 4т_Гильза", "Бутылочный домкрат 4т_Тяга", "Бутылочный домкрат 4т_Коромысло", ], "Бутылочный домкрат 4т_Шток насоса": [ "Бутылочный домкрат 4т_Гильза", "Бутылочный домкрат 4т_Коромысло", ], "Бутылочный домкрат 4т_Коромысло": [ "Бутылочный домкрат 4т_Тяга", "Бутылочный домкрат 4т_Тяга001", "Бутылочный домкрат 4т_Шток насоса", ], "Бутылочный домкрат 4т_Шток": [ "Бутылочный домкрат 4т_Гильза", "Бутылочный домкрат 4т_Винт штока", ], "Бутылочный домкрат 4т_Винт штока": ["Бутылочный домкрат 4т_Шток"], "Бутылочный домкрат 4т_Вентиль": ["Бутылочный домкрат 4т_Гильза"], } ), execComposition=lambda _: ( CheckObjectHasTouchesUseCase() .call(0) .either( leftF=lambda matrix: matrix.matrixToFileSystem(outFolder), rightF=lambda error: print(error), ) ), documentPath=mocksFolder + "bottle_jack.FCStd", modelName=AdjacencyMatrix.fileName, model=AdjacencyMatrix, ) FreeCadASPGenerationTestController( "test adjacency matrix keys vs allparts" ).test( assertFn=lambda model: CoreDict(model.matrix).isMatchByKeys( ["Куб", "Куб001"] ), execComposition=lambda _: ( CheckObjectHasTouchesUseCase() .call(0) .either( leftF=lambda matrix: matrix.matrixToFileSystem(outFolder), rightF=lambda error: print(error), ) ), documentPath=mocksFolder + "simple_assembly_with_two_cubes.FCStd", modelName=AdjacencyMatrix.fileName, model=AdjacencyMatrix, ) ExitFreeCadUseCase.call() except: print("test error") ExitFreeCadUseCase.call() pass test() # TODO: # 1.