ASP refactoring, sequence generation via clusterization

This commit is contained in:
Mark Voltov 2024-02-02 14:22:21 +00:00 committed by Igor Brylyov
parent d2ab856d64
commit fd59ab9e26
45 changed files with 1579 additions and 1267 deletions

View file

@ -0,0 +1,50 @@
from helpers.either import Either, Left, Right
from models.adjacency_matrix_model import (
AdjacencyMatrixModel,
)
from models.error_string_model import ErrorStringModel
from repository.freecad_repository import (
FreeCadRepository,
)
from usecases.get_first_detail_use_case import (
GetFirstDetailUseCase,
)
class CheckObjectHasTouchesUseCase:
"""Check touches between objects and returns matrix
Returns:
dict: adjacency matrix
"""
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(
AdjacencyMatrixModel(
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"))

View file

@ -0,0 +1,11 @@
from extensions.list import CoreList
class CheckSequenceUsecase(list):
def isCorrectByParts(self, checkList) -> bool:
parts = self[len(self) - 1]
for part in parts:
part = str(part)
part = part[1:-1]
return CoreList(self).equal(checkList)

View file

@ -0,0 +1,16 @@
class ClearWorkSpaceDocumentUseCase(object):
"""Clear of the workspace
Args:
object : active CAD-model
"""
def call(self, detailSquares):
import FreeCAD as App
for key in detailSquares:
for renderPrimitive in detailSquares[key]:
primitivePart = App.ActiveDocument.getObjectsByLabel(
renderPrimitive.cadLabel
)[0]
App.ActiveDocument.removeObject(primitivePart.Name)

View file

@ -0,0 +1,66 @@
import os
import json
import networkx as nx
from repository.file_system_repository import FileSystemRepository
class GraphProcessor:
file_path: str
graph = None
def __init__(self, file_path: str):
self.file_path = file_path
self.graph = self.load_graph_from_json()
def load_graph_from_json(self):
with open(self.file_path, "r") as file:
data = json.load(file)
G = nx.Graph()
if "matrix" in data:
matrix = data["matrix"]
for part1, neighbors in matrix.items():
for neighbor in neighbors:
G.add_edge(part1, neighbor)
return G
class EdgeBetweensClustering:
def __init__(self, graph):
self.graph = graph.copy()
self.clusters = []
def cluster(self):
while self.graph.number_of_edges() > 0:
edge_betweens = nx.edge_betweenness_centrality(self.graph)
max_betweens_edge = max(edge_betweens, key=edge_betweens.get)
self.graph.remove_edge(*max_betweens_edge)
components = list(nx.connected_components(self.graph))
if components not in self.clusters:
self.clusters.append(components)
return []
def get_clusters(self):
return self.clusters
class ClusterisationSequenceUseCase:
def call(self, file_path: str):
graph_processor = GraphProcessor(file_path + "adjacency_matrix.json")
G = graph_processor.load_graph_from_json()
ebc = EdgeBetweensClustering(G)
ebc.cluster()
clusters = ebc.get_clusters()
for i in range(len(clusters)):
for j in range(len(clusters[i])):
clusters[i][j] = list(clusters[i][j])
FileSystemRepository.writeFile(
json.dumps(clusters, ensure_ascii=False, indent=2),
file_path,
"assembly_sequence.json",
)
return clusters

View file

@ -0,0 +1,12 @@
from repository.file_system_repository import FileSystemRepository
from helpers.either import Either, Left, Right
from models.env_model import EnvModel
class EnvReaderUseCase:
def call() -> Either:
try:
return Left(EnvModel.from_dict(FileSystemRepository.readJSON("env.json")))
except:
print("env reader error")
return Right(None)

View file

@ -0,0 +1,6 @@
from repository.freecad_repository import FreeCadRepository
class ExitFreeCadUseCase:
def call():
FreeCadRepository().closeIfOpenDocument()

View file

@ -0,0 +1,13 @@
from repository.freecad_repository import (
FreeCadRepository,
)
class GetAllPartsLabelsUseCase:
"""Get all parts label in assembly"""
def call(self):
parts = []
for part in FreeCadRepository().getAllSolids():
parts.append(part.Label)
return parts

View file

@ -0,0 +1,44 @@
from typing import List, Dict
def isUnique(array, element):
for i in array:
if i == element:
return False
return True
class GetCollisionAtPrimitiveUseCase(object):
"""Get collisions between primitives
Args:
object: cad-model
Returns:
dict: collision matrix
"""
# Получение колизий примитивов
def call(self, freeCadMetaModels, detailSquares) -> Dict[str, List[str]]:
import FreeCAD as App
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

View file

@ -0,0 +1,8 @@
from repository.freecad_repository import FreeCadRepository
class GetFirstDetailUseCase:
"""Get label of first part in tree assembly"""
def call(self):
return FreeCadRepository().getAllSolids()[0].Label

View file

@ -0,0 +1,27 @@
from models.mesh_geometry_coordinate_model import (
MeshGeometryCoordinateModel,
)
class GetPartPrimitiveCoordinatesUseCase(object):
"""Get positions of parts in assembly
Args:
object : cad-model
"""
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

View file

@ -0,0 +1,17 @@
from models.freecad_meta_model import FreeCadMetaModel
from repository.freecad_repository import (
FreeCadRepository,
)
class InitPartsParseUseCase:
"""Initialisation of parsing geometry models info"""
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

View file

@ -0,0 +1,20 @@
class IntersectionComputedUseCase:
def call(parts):
import FreeCAD as App
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

View file

@ -0,0 +1,31 @@
from repository.file_system_repository import FileSystemRepository
from usecases.intersection_computed_use_case import (
IntersectionComputedUseCase,
)
import json
class IntersectionGeometryUseCase:
"""A class that checks bodies in an assembly for interference and returns the result of the check to a file"""
def call(contacts, path):
import FreeCAD as App
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",
)

View file

@ -0,0 +1,14 @@
from helpers.either import Either, Left, Right
from models.error_string_model import ErrorStringModel
from repository import freecad_repository
class IsAllObjectSolidsCheckUseCase:
def call() -> Either:
result = freecad_repository().isAllObjectsSolids()
if result.__len__() == 0:
return Left(None)
return Right(
ErrorStringModel(error="Is not solid objects: " + ",".join(result))
)

View file

@ -0,0 +1,18 @@
from helpers.either import Either, Left, Right
from repository.freecad_repository import (
FreeCadRepository,
)
class OpenFreeCadDocumentUseCase:
"""A class that checks open documents, closes them and opens the one with which the program will work"""
def call(path: str) -> Either:
try:
FreeCadRepository().closeIfOpenDocument()
FreeCadRepository().openDocument(path)
return Left(None)
except Exception as e:
print(e)
print("OpenFreeCadDocumentUseCase error")
return Right(None)

View file

@ -0,0 +1,21 @@
from repository.file_system_repository import FileSystemRepository
from helpers.either import Left, Right, Either
class ReadFileSystemAndGetInstanceModelUseCase:
def call(self, model, path) -> Either:
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 Exception as e:
print(e)
error = str(model) + " " + "from dict error " + "path: " + path
print("ReadFileSystemAndGetInstanceModelUseCase error" + error)
return Right(error)
pass

View file

@ -0,0 +1,15 @@
from models.mesh_geometry_coordinate_model import MeshGeometryCoordinateModel
class RenderPrimitiveUseCase(object):
"""Rendering primitives
Args:
object: CAD-model
"""
def call(
self, meshModels: list[MeshGeometryCoordinateModel], detailSquares
) -> None:
for mesh in meshModels:
mesh.initializePrimitivesByCoordinate(detailSquares)

View file

@ -0,0 +1,41 @@
from usecases.clear_work_space_document_use_case import (
ClearWorkSpaceDocumentUseCase,
)
from geometric_feasibility_predicate.usecases.get_collision_at_primitive_use_case import (
GetCollisionAtPrimitiveUseCase,
)
from usecases.get_part_primitive_coordinates_use_case import (
GetPartPrimitiveCoordinatesUseCase,
)
from usecases.init_parts_parse_use_case import (
InitPartsParseUseCase,
)
from usecases.render_primitive_use_case import (
RenderPrimitiveUseCase,
)
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