Add PDDL, 3D-assets & SDF-URDF generator from Blender Scene Config

This commit is contained in:
IDONTSUDO 2023-12-17 13:58:43 +00:00 committed by Igor Brylyov
parent b77687ea14
commit e305d486f2
41 changed files with 2793 additions and 664 deletions

View file

@ -6,6 +6,16 @@ from typing import List, Dict, Any, TypeVar, Callable, Type, cast
from itertools import repeat
class CoreList(List):
# the list contains only True
def onlyTrue(self) -> bool:
print(self)
for el in self:
if el is not True:
return False
return True
def isInListRange(listIn, index):
try:
listIn[index]
@ -31,8 +41,12 @@ class AllSequences:
inc = 0
for matrix in self.all_sequences:
for index in range(len(matrix)):
result[inc][index] = list(filter(lambda el: el.get(
'number') == matrix[index]+1, self.topologyIds))[0].get('name')
result[inc][index] = list(
filter(
lambda el: el.get("number") == matrix[index] + 1,
self.topologyIds,
)
)[0].get("name")
inc += 1
self.adj_matrix_names = result
pass
@ -59,11 +73,12 @@ class AllSequences:
def findId(self, listMatrix, id):
def filter_odd_num(in_num):
if in_num['name'] == id:
if in_num["name"] == id:
return True
else:
return False
return list(filter(filter_odd_num, listMatrix))[0]['number']
return list(filter(filter_odd_num, listMatrix))[0]["number"]
def iter_paths(self, adj, min_length=6, path=None):
if not path:
@ -94,7 +109,8 @@ class AllSequences:
for k, v in matrix.items():
inc += 1
topologyMatrixNumber[inc] = list(
map(lambda el: self.findId(topologyIds, el), v))
map(lambda el: self.findId(topologyIds, el), v)
)
self.topologyIds = topologyIds
adj = []
matrixSize = matrix.keys().__len__()
@ -102,18 +118,121 @@ class AllSequences:
for k, v in topologyMatrixNumber.items():
adj.append(list(repeat(0, matrixSize)))
for el in v:
adj[inc][el-1] = 1
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)
class FreeCadRepository:
_solids = []
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) -> bool:
for solid in self.getAllSolids():
if solid.Label != part.Label:
collisionResult: int = int(part.Shape.distToShape(solid.Shape)[0])
if collisionResult == 0:
return True
return False
def objectHasTouches(self, part, objectIndentation: float) -> List[str]:
positionVector = self.objectGetPosition(part)
result = CoreList()
result.append(self.isObjectIntersections(part=part))
if objectIndentation != 0 and objectIndentation != None:
result.append(
self.axis_movement_and_intersections_observer(
positionVector=positionVector,
alongAxis="x",
objectIndentation=objectIndentation,
part=part,
)
)
result.append(
self.axis_movement_and_intersections_observer(
positionVector=positionVector,
alongAxis="y",
objectIndentation=objectIndentation,
part=part,
)
)
result.append(
self.axis_movement_and_intersections_observer(
positionVector=positionVector,
alongAxis="z",
objectIndentation=objectIndentation,
part=part,
)
)
return result.onlyTrue()
def axis_movement_and_intersections_observer(
self,
positionVector: VectorModel,
alongAxis: str,
objectIndentation: float,
part,
) -> bool:
# UP
positionVector.__setattr__(
alongAxis, positionVector.__getattribute__(alongAxis) + objectIndentation
)
self.objectSetPosition(part, positionVector.toFreeCadVector())
result = self.isObjectIntersections(part=part)
if result:
return True
# DOWN
positionVector.__setattr__(
alongAxis, positionVector.__getattribute__(alongAxis) - objectIndentation
)
positionVector.__setattr__(
alongAxis, positionVector.__getattribute__(alongAxis) - objectIndentation
)
result = self.isObjectIntersections(part=part)
if result:
return True
self.isObjectIntersections(part=part)
# ROLLBACK
positionVector.__setattr__(
alongAxis, positionVector.__getattribute__(alongAxis) + objectIndentation
)
self.objectSetPosition(part, positionVector.toFreeCadVector())
return False
def getAllSolids(self):
if (self._solids.__len__() == 0):
if self._solids.__len__() == 0:
for part in App.ActiveDocument.Objects:
if (self.is_object_solid(part)):
if self.is_object_solid(part):
self._solids.append(part)
return self._solids
@ -121,14 +240,12 @@ class FreeCadRepository:
def is_object_solid(self, obj):
if not isinstance(obj, App.DocumentObject):
return False
if hasattr(obj, 'Group'):
if hasattr(obj, "Group"):
return False
if not hasattr(obj, 'Shape'):
if not hasattr(obj, "Shape"):
return False
if not hasattr(obj.Shape, 'Mass'):
return False
if not hasattr(obj.Shape, 'Solids'):
if not hasattr(obj.Shape, "Solids"):
return False
if len(obj.Shape.Solids) == 0:
@ -159,6 +276,7 @@ def to_class(c: Type[T], x: Any) -> dict:
assert isinstance(x, c)
return cast(Any, x).to_dict()
# Вспомогательный класс который делает генрацию JSON на основе пайтон обьектов
@ -168,7 +286,9 @@ class AdjacencyMatrix:
first_detail: str
matrix: Dict[str, List[str]]
def __init__(self, all_parts: List[str], first_detail: str, matrix: Dict[str, List[str]]) -> None:
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
@ -183,11 +303,11 @@ class AdjacencyMatrix:
def validateMatrix(self):
for el in self.all_parts:
if (self.matrix.get(el) == None):
self.matrixError[el] = 'Not found adjacency ' + el
if self.matrix.get(el) == None:
self.matrixError[el] = "Not found adjacency " + el
@staticmethod
def from_dict(obj: Any) -> 'AdjacencyMatrix':
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"))
@ -199,12 +319,11 @@ class AdjacencyMatrix:
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
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
result["matrixError"] = self.matrixError
return result
def getDictMatrix(self) -> dict:
@ -225,11 +344,11 @@ def adjacency_matrix_from_dict(s: Any) -> AdjacencyMatrix:
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
@ -240,12 +359,18 @@ collision_squares_labels = []
class MeshGeometryCoordinateModel(object):
# Получение геометрии мешей
def __init__(self, x, y, z, label,):
def __init__(
self,
x,
y,
z,
label,
):
self.x = x
self.y = y
self.z = z
self.label = label
self.cadLabel = ''
self.cadLabel = ""
def initializePrimitivesByCoordinate(self, detailSquares):
uuidDoc = str(uuid.uuid1())
@ -259,8 +384,9 @@ class MeshGeometryCoordinateModel(object):
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):
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
@ -272,19 +398,21 @@ class FS:
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 = open(file_to_open, "w", encoding="utf8")
f.write(data)
def readFile(path: str):
return open(path).read()
def readFilesTypeFolder(pathFolder: str, fileType='.json'):
def readFilesTypeFolder(pathFolder: str, fileType=".json"):
filesJson = list(
filter(lambda x: x[-fileType.__len__():] == fileType, os.listdir(pathFolder)))
filter(
lambda x: x[-fileType.__len__() :] == fileType, os.listdir(pathFolder)
)
)
return filesJson
@ -312,20 +440,20 @@ class GetCollisionAtPrimitiveUseCase(object):
for model in freeCadMetaModels:
activePart = App.ActiveDocument.getObjectsByLabel(model.label)[0]
for key in detailSquares:
if (model.label != key):
if model.label != key:
for renderPrimitive in detailSquares[key]:
primitivePart = App.ActiveDocument.getObjectsByLabel(
renderPrimitive.cadLabel)[0]
renderPrimitive.cadLabel
)[0]
collisionResult: int = int(
activePart.Shape.distToShape(primitivePart.Shape)[0])
if (collisionResult == 0):
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
)
matrix[model.label].append(renderPrimitive.label)
return matrix
@ -354,21 +482,23 @@ class GetPartPrimitiveCoordinatesUseCase(object):
return meshCoordinates
class InitPartsParseUseCase():
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):
if model is not None:
product_details.append(model)
return product_details
class RenderPrimitiveUseCase(object):
# Рендеринг премитивов
def call(self, meshModels: list[MeshGeometryCoordinateModel], detailSquares) -> None:
def call(
self, meshModels: list[MeshGeometryCoordinateModel], detailSquares
) -> None:
for mesh in meshModels:
mesh.initializePrimitivesByCoordinate(detailSquares)
@ -379,12 +509,12 @@ class ClearWorkSpaceDocumentUseCase(object):
for key in detailSquares:
for renderPrimitive in detailSquares[key]:
primitivePart = App.ActiveDocument.getObjectsByLabel(
renderPrimitive.cadLabel)[0]
renderPrimitive.cadLabel
)[0]
App.ActiveDocument.removeObject(primitivePart.Name)
class RenderPrimitivesScenario(object):
def __init__(
self,
initPartsParseUseCase: InitPartsParseUseCase,
@ -416,7 +546,8 @@ class ClearWorkSpaceDocumentUseCase(object):
for key in detailSquares:
for renderPrimitive in detailSquares[key]:
primitivePart = App.ActiveDocument.getObjectsByLabel(
renderPrimitive.cadLabel)[0]
renderPrimitive.cadLabel
)[0]
App.ActiveDocument.removeObject(primitivePart.Name)
@ -436,6 +567,7 @@ class CadAdjacencyMatrix:
first_detail=GetFirstDetailUseCase().call(),
matrix=matrix,
)
# Матрица основанная на соприкосновении обьектов
def matrixBySurfaces(self):
@ -446,12 +578,17 @@ class CadAdjacencyMatrix:
if part.Label != nextPart.Label:
# Вычисление соприконсоновения площади деталей
collisionResult: int = int(
part.Shape.distToShape(nextPart.Shape)[0])
if (collisionResult == 0):
part.Shape.distToShape(nextPart.Shape)[0]
)
print(collisionResult)
print("collisionResult")
if collisionResult == 0:
matrix[part.Label].append(nextPart.Label)
return AdjacencyMatrix(all_parts=GetAllPartsLabelsUseCase(
).call(), first_detail=GetFirstDetailUseCase().call(),
matrix=matrix
return AdjacencyMatrix(
all_parts=GetAllPartsLabelsUseCase().call(),
first_detail=GetFirstDetailUseCase().call(),
matrix=matrix,
)
@ -472,67 +609,166 @@ def to_ascii_hash(text):
def matrixGetUniqueContact(matrix):
detailsToCheck = []
detailsHashCheck = {}
for k, v in matrix.items():
for el in v:
if (el != k):
if el != k:
hash = to_ascii_hash(k + el)
if (detailsHashCheck.get(hash) == None):
if detailsHashCheck.get(hash) == None:
detailsHashCheck[hash] = hash
detailsToCheck.append({
'child': el,
'parent': k
})
detailsToCheck.append({"child": el, "parent": k})
return detailsToCheck
def intersectionComputed(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 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,
)
class IsAllObjectSolidsCheckUseCase:
def call() -> ErrorStringModel:
result = FreeCadRepository().isAllObjectsSolids()
if result.__len__() == 0:
return None
return ErrorStringModel(error="Is not solid objects: " + ",".join(result))
class CheckObjectHasTouchesUseCase:
def call(objectIndentation: float) -> ErrorStringModel:
result = []
for part in FreeCadRepository().getAllSolids():
if (
FreeCadRepository().objectHasTouches(
part=part, objectIndentation=objectIndentation
)
is False
):
result.append(part.Label)
if result.__len__() == 0:
return None
return ErrorStringModel(
error="Solids bodies have no recounts: " + ",".join(result)
)
class CheckCadIntersectionObjects:
report = []
def call() -> bool:
FreeCadRepository().getAllSolids()
return False
class ExitFreeCadUseCase:
def call():
import FreeCADGui as Gui
freecadQTWindow = Gui.getMainWindow()
freecadQTWindow.close()
# class CheckValidIntersectionUseCase:
# def call() -> ErrorStringModel:
# for part in FreeCadRepository().getAllSolids():
# print(part)
# FreeCadRepository().obj
# pass
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)
env = FS.readJSON("env.json")
cadFilePath = str(env["cadFilePath"])
outPath = str(env["outPath"])
objectIndentation = float(env["objectIndentation"])
if cadFilePath == None:
return TypeError("CadFile not found env.json")
App.open("" + cadFilePath)
# isAllObjectSolidsCheckUseCase = IsAllObjectSolidsCheckUseCase.call()
# if isAllObjectSolidsCheckUseCase != None:
# FS.writeFile(isAllObjectSolidsCheckUseCase.toString(), outPath, 'error.json')
# ExitFreeCadUseCase.call()
# return
# checkObjectHasTouchesUseCase = CheckObjectHasTouchesUseCase.call(objectIndentation)
# if checkObjectHasTouchesUseCase != None:
# FS.writeFile(checkObjectHasTouchesUseCase.toString(), outPath, 'error.json')
# ExitFreeCadUseCase.call()
# return
topologyMatrix = CadAdjacencyMatrix().matrixBySurfaces()
import json
sequences = json.dumps({"sequences": AllSequences(
topologyMatrix.matrix).adj_matrix_names}, ensure_ascii=False, indent=4)
sequences = json.dumps(
{"sequences": AllSequences(topologyMatrix.matrix).adj_matrix_names},
ensure_ascii=False,
indent=4,
)
matrix = topologyMatrix.matrix
contacts = matrixGetUniqueContact(matrix)
intersection_geometry = {
'status':True,
'recalculations':None
}
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 = intersectionComputed([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')
})
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")}
)
FS.writeFile(
json.dumps(intersection_geometry, ensure_ascii=False, indent=4),
outPath,
"intersection_geometry.json",
)
FS.writeFile(sequences, outPath, "sequences.json")
FS.writeFile(
json.dumps(topologyMatrix.to_dict(), ensure_ascii=False, indent=4),
outPath,
"adjacency_matrix.json",
)
ExitFreeCadUseCase.call()
FS.writeFile(json.dumps(intersection_geometry, ensure_ascii=False, indent=4), outPath, 'intersection_geometry.json')
FS.writeFile(sequences, outPath, 'sequences.json')
FS.writeFile(json.dumps(topologyMatrix.to_dict(),
ensure_ascii=False, indent=4), outPath, 'adjacency_matrix.json')
main()