framework/geometric_feasibility_predicate/main.py

1317 lines
42 KiB
Python
Raw Normal View History

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()
2023-07-05 14:14:36 +00:00
# Вспомогательный класс который делает генрацию 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:
2023-07-05 16:03:51 +03:00
# Инициализация парсинга
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):
2023-07-05 16:03:51 +03:00
# Рендеринг премитивов
def call(
self, meshModels: list[MeshGeometryCoordinateModel], detailSquares
) -> None:
for mesh in meshModels:
mesh.initializePrimitivesByCoordinate(detailSquares)
class ClearWorkSpaceDocumentUseCase(object):
2023-07-05 16:03:51 +03:00
# Очистка рабочего простарнства
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:
2023-07-05 16:03:51 +03:00
# Матрица основанная на соприкосновении примитива с обьектами
def primitiveMatrix(self):
2023-07-05 16:03:51 +03:00
# Получение матрицы
matrix = RenderPrimitivesScenario(
InitPartsParseUseCase(),
GetPartPrimitiveCoordinatesUseCase(),
RenderPrimitiveUseCase(),
GetCollisionAtPrimitiveUseCase(),
ClearWorkSpaceDocumentUseCase(),
).call()
return AdjacencyMatrix(
all_parts=GetAllPartsLabelsUseCase().call(),
first_detail=GetFirstDetailUseCase().call(),
matrix=matrix,
)
# Матрица основанная на соприкосновении обьектов
2023-07-05 16:03:51 +03:00
def matrixBySurfaces(self):
matrix = {}
for part in FreeCadRepository().getAllSolids():
2023-07-05 16:03:51 +03:00
matrix[part.Label] = []
for nextPart in FreeCadRepository().getAllSolids():
if part.Label != nextPart.Label:
2023-07-05 16:03:51 +03:00
# Вычисление соприконсоновения площади деталей
collisionResult: int = int(
part.Shape.distToShape(nextPart.Shape)[0]
)
if collisionResult == 0:
2023-07-05 16:03:51 +03:00
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.