ASP refactoring, sequence generation via clusterization
This commit is contained in:
parent
d2ab856d64
commit
fd59ab9e26
45 changed files with 1579 additions and 1267 deletions
0
geometric_feasibility_predicate/models/__init__.py
Normal file
0
geometric_feasibility_predicate/models/__init__.py
Normal file
118
geometric_feasibility_predicate/models/adjacency_matrix_model.py
Normal file
118
geometric_feasibility_predicate/models/adjacency_matrix_model.py
Normal file
|
@ -0,0 +1,118 @@
|
|||
from models.all_sequences_model import AllSequencesModel
|
||||
from repository.file_system_repository import FileSystemRepository
|
||||
from typing import List, Dict, Any
|
||||
import json
|
||||
|
||||
from models.var import from_str, from_list, from_dict
|
||||
|
||||
|
||||
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 AdjacencyMatrixModel:
|
||||
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,
|
||||
AdjacencyMatrixModel.fileName,
|
||||
)
|
||||
pass
|
||||
|
||||
def sequencesToFileSystem(self, path: str, restrictions: list[str]):
|
||||
FileSystemRepository.writeFile(
|
||||
json.dumps(
|
||||
{
|
||||
"sequences": AllSequencesModel(
|
||||
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) -> "AdjacencyMatrixModel":
|
||||
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 AdjacencyMatrixModel(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
|
130
geometric_feasibility_predicate/models/all_sequences_model.py
Normal file
130
geometric_feasibility_predicate/models/all_sequences_model.py
Normal file
|
@ -0,0 +1,130 @@
|
|||
from extensions.list import CoreList
|
||||
from itertools import repeat
|
||||
|
||||
|
||||
class AllSequencesModel:
|
||||
"""
|
||||
A class that processes information from the assembly and creates the objects necessary to create the assembly sequence
|
||||
|
||||
"""
|
||||
|
||||
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]):
|
||||
"""_summary_
|
||||
|
||||
Args:
|
||||
restrictions (CoreList[str]): _description_
|
||||
"""
|
||||
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):
|
||||
"""Find all assembly sequences with dfs
|
||||
|
||||
Args:
|
||||
adj_matrix (dict): Assembly ajacency 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)
|
|
@ -0,0 +1,71 @@
|
|||
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.render_primitive_use_case import RenderPrimitiveUseCase
|
||||
from usecases.get_part_primitive_coordinates_use_case import (
|
||||
GetPartPrimitiveCoordinatesUseCase,
|
||||
)
|
||||
from usecases.init_parts_parse_use_case import (
|
||||
InitPartsParseUseCase,
|
||||
)
|
||||
from usecases.render_primitives_scenario import RenderPrimitivesScenario
|
||||
from usecases.get_first_detail_use_case import GetFirstDetailUseCase
|
||||
from geometric_feasibility_predicate.usecases.get_all_parts_labels_use_case import (
|
||||
GetAllPartsLabelsUseCase,
|
||||
)
|
||||
from models.adjacency_matrix_model import AdjacencyMatrixModel
|
||||
from repository.freecad_repository import FreeCadRepository
|
||||
|
||||
|
||||
class CadAdjacencyMatrixModel:
|
||||
"""Class for handling adjacency matrix creation scenario
|
||||
|
||||
Returns:
|
||||
dict: adjacency matrix
|
||||
"""
|
||||
|
||||
def primitiveMatrix(self):
|
||||
"""get matrix of primitives"""
|
||||
# Получение матрицы
|
||||
matrix = RenderPrimitivesScenario(
|
||||
InitPartsParseUseCase(),
|
||||
GetPartPrimitiveCoordinatesUseCase(),
|
||||
RenderPrimitiveUseCase(),
|
||||
GetCollisionAtPrimitiveUseCase(),
|
||||
ClearWorkSpaceDocumentUseCase(),
|
||||
).call()
|
||||
return AdjacencyMatrixModel(
|
||||
all_parts=GetAllPartsLabelsUseCase().call(),
|
||||
first_detail=GetFirstDetailUseCase().call(),
|
||||
matrix=matrix,
|
||||
)
|
||||
|
||||
# Матрица основанная на соприкосновении обьектов
|
||||
|
||||
def matrixBySurfaces(self):
|
||||
"""Adjacency matrix by touches between parts
|
||||
|
||||
Returns:
|
||||
dict: adjacency matrix
|
||||
"""
|
||||
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 AdjacencyMatrixModel(
|
||||
all_parts=GetAllPartsLabelsUseCase().call(),
|
||||
first_detail=GetFirstDetailUseCase().call(),
|
||||
matrix=matrix,
|
||||
)
|
48
geometric_feasibility_predicate/models/env_model.py
Normal file
48
geometric_feasibility_predicate/models/env_model.py
Normal file
|
@ -0,0 +1,48 @@
|
|||
from dataclasses import dataclass
|
||||
from typing import List, Dict, Any, TypeVar, Callable, Type, cast
|
||||
from extensions.list import CoreList
|
||||
|
||||
from models.var import from_str, from_float
|
||||
|
||||
|
||||
@dataclass
|
||||
class EnvModel:
|
||||
cadFilePath: str
|
||||
outPath: str
|
||||
solidBodyPadding: float
|
||||
firstDetail: str
|
||||
sequencesFixed: list[list[str]]
|
||||
|
||||
@staticmethod
|
||||
def from_dict(obj: Any) -> "EnvModel":
|
||||
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 EnvModel(
|
||||
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
|
28
geometric_feasibility_predicate/models/error_string_model.py
Normal file
28
geometric_feasibility_predicate/models/error_string_model.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
import json
|
||||
from repository.file_system_repository import FileSystemRepository
|
||||
from usecases.exit_freecad_use_case import (
|
||||
ExitFreeCadUseCase,
|
||||
)
|
||||
|
||||
|
||||
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(),
|
||||
)
|
|
@ -0,0 +1,4 @@
|
|||
class FreeCadMetaModel(object):
|
||||
def __init__(self, label, vertex) -> None:
|
||||
self.label = label
|
||||
self.vertex = vertex
|
|
@ -0,0 +1,41 @@
|
|||
import uuid
|
||||
|
||||
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):
|
||||
import FreeCAD as App
|
||||
|
||||
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()
|
51
geometric_feasibility_predicate/models/var.py
Normal file
51
geometric_feasibility_predicate/models/var.py
Normal file
|
@ -0,0 +1,51 @@
|
|||
# Data Conversion Helpers:
|
||||
|
||||
|
||||
from typing import List, Dict, Any, TypeVar, Callable, Type, cast
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
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()
|
||||
|
||||
|
||||
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()
|
18
geometric_feasibility_predicate/models/vector_model.py
Normal file
18
geometric_feasibility_predicate/models/vector_model.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
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):
|
||||
import FreeCAD as App
|
||||
|
||||
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))
|
Loading…
Add table
Add a link
Reference in a new issue