framework/geometric_feasibility_predicate/main.py

366 lines
12 KiB
Python
Raw Normal View History

2023-07-05 14:14:36 +00:00
# Алгоритм генерации графа И/ИЛИ c помощью вычисления матрицы смежности
import FreeCAD as App
import uuid
import os
import json
from typing import List, Dict, Any, TypeVar, Callable, Type, cast
2023-07-05 14:14:36 +00:00
# вспомогательный класс для работы с FreeCad API
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()
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]]
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)
2023-07-05 14:14:36 +00:00
# Вспомогательный класс для работы с 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 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():
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(),
2023-07-05 16:03:51 +03:00
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)
2023-07-05 16:03:51 +03:00
# Получение матрицы
matrixOut = CadAdjacencyMatrix().primitiveMatrix().to_dict()
import json
2023-07-05 16:03:51 +03:00
# Запись результата
FS.writeFile(json.dumps(matrixOut, ensure_ascii=False, indent=4), outPath,'out.json')
main()