работа над валидатором геометрии
This commit is contained in:
parent
bd0a9865a1
commit
3e7bfba178
10 changed files with 225 additions and 155 deletions
|
@ -4,7 +4,7 @@ from . import ICONPATH, TRANSLATIONSPATH
|
|||
from .Tools import spawnClassCommand, placement2axisvec, describeSubObject, getPrimitiveInfo #, RobossemblerFreeCadExportScenario
|
||||
from .pddl import freecad2pddl
|
||||
from .BoMList import run_BoM_list
|
||||
from .ImportExportEntities import export_coordinate_systems
|
||||
from .export_entities import export_coordinate_systems
|
||||
from .utils.freecad_processor import process_file
|
||||
from .usecases.asm4parser_usecase import Asm4StructureParseUseCase
|
||||
|
||||
|
@ -296,11 +296,6 @@ spawnClassCommand("FrameCommand",
|
|||
"MenuText": "Make a free frame",
|
||||
"ToolTip": "Make a freestanding reference frame."})
|
||||
|
||||
# spawnClassCommand("ASM4StructureParsing",
|
||||
# RobossemblerFreeCadExportScenario().call,
|
||||
# {"Pixmap": str(os.path.join(ICONPATH, "assembly4.svg")),
|
||||
# "MenuText": "Make a ASM4 parsing",
|
||||
# "ToolTip": "Make a ASM4 1"})
|
||||
|
||||
spawnClassCommand("SelectedPartFrameCommand",
|
||||
makeSelectedPartFrames,
|
||||
|
@ -319,38 +314,6 @@ spawnClassCommand("FeatureFrameCommand",
|
|||
"MenuText": "frame on selected primitive",
|
||||
"ToolTip": "Create a frame on selected primitive."})
|
||||
|
||||
spawnClassCommand("PDDL_CreateTypes",
|
||||
freecad2pddl.add_types,
|
||||
{"Pixmap": str(os.path.join(ICONPATH, "featureframecreator.svg")),
|
||||
"MenuText": "Types",
|
||||
"ToolTip": "Add Types"})
|
||||
|
||||
spawnClassCommand("PDDL_CreateParameters",
|
||||
freecad2pddl.add_parameters,
|
||||
{"Pixmap": str(os.path.join(ICONPATH, "featureframecreator.svg")),
|
||||
"MenuText": "Parameters",
|
||||
"ToolTip": "Add Parameters"})
|
||||
spawnClassCommand("PDDL_CreateAction",
|
||||
freecad2pddl.add_action,
|
||||
{"Pixmap": str(os.path.join(ICONPATH, "featureframecreator.svg")),
|
||||
"MenuText": "Action",
|
||||
"ToolTip": "Add Action"})
|
||||
spawnClassCommand("PDDL_CreatePredicate",
|
||||
freecad2pddl.add_predicate,
|
||||
{"Pixmap": str(os.path.join(ICONPATH, "featureframecreator.svg")),
|
||||
"MenuText": "Predicate",
|
||||
"ToolTip": "Add Predicate"})
|
||||
spawnClassCommand("PDDL_CreateDurativeAction",
|
||||
freecad2pddl.add_durative_action,
|
||||
{"Pixmap": str(os.path.join(ICONPATH, "featureframecreator.svg")),
|
||||
"MenuText": "DurativeAction",
|
||||
"ToolTip": "Add Durative Action"})
|
||||
spawnClassCommand("PDDL_ExportPDDL",
|
||||
freecad2pddl.export_to_file,
|
||||
{"Pixmap": str(os.path.join(ICONPATH, "featureframecreator.svg")),
|
||||
"MenuText": "ExportDomain",
|
||||
"ToolTip": "Create and Export Domain.pddl to File"})
|
||||
|
||||
spawnClassCommand("Export_Entities",
|
||||
export_coordinate_systems,
|
||||
{"Pixmap": str(os.path.join(ICONPATH, "BoMList.svg")),
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
import FreeCAD as App
|
||||
import FreeCAD.Console as Console
|
||||
|
||||
class GeometryUseCase:
|
||||
def __init__(self, document):
|
||||
self.document = document
|
||||
|
||||
def is_valid_geometry(self, obj):
|
||||
return hasattr(obj, "Shape") and obj.Shape.Volume is not None
|
||||
|
||||
def create_intersection_matrix(self):
|
||||
bodies = [obj for obj in self.document.Objects if self.is_valid_geometry(obj)]
|
||||
num_bodies = len(bodies)
|
||||
intersection_matrix = [[False for _ in range(num_bodies)] for _ in range(num_bodies)]
|
||||
|
||||
for i in range(num_bodies):
|
||||
for j in range(i + 1, num_bodies):
|
||||
body1 = bodies[i]
|
||||
body2 = bodies[j]
|
||||
|
||||
if body1.Shape.common(body2.Shape).Volume > 0:
|
||||
intersection_matrix[i][j] = True
|
||||
intersection_matrix[j][i] = True
|
||||
|
||||
if not hasattr(body1, "AllowIntersections") or not hasattr(body2, "AllowIntersections"):
|
||||
Console.PrintWarning(
|
||||
f"Пересечение между '{body1.Name}' и '{body2.Name}' без разрешения.\n"
|
||||
)
|
||||
|
||||
return intersection_matrix
|
||||
|
||||
def create_contact_matrix(self):
|
||||
bodies = [obj for obj in self.document.Objects if self.is_valid_geometry(obj)]
|
||||
num_bodies = len(bodies)
|
||||
contact_matrix = [[False for _ in range(num_bodies)] for _ in range(num_bodies)]
|
||||
|
||||
for i in range(num_bodies):
|
||||
for j in range(i + 1, num_bodies):
|
||||
body1 = bodies[i]
|
||||
body2 = bodies[j]
|
||||
|
||||
if body1.Shape.common(body2.Shape).SurfaceArea > 0:
|
||||
contact_matrix[i][j] = True
|
||||
contact_matrix[j][i] = True
|
||||
|
||||
return contact_matrix
|
||||
|
||||
|
||||
doc = App.ActiveDocument
|
||||
if doc is None:
|
||||
Console.PrintError("Нет активного документа в FreeCAD.\n")
|
||||
else:
|
||||
use_case = GeometryUseCase(doc)
|
||||
|
||||
intersection_matrix = use_case.create_intersection_matrix()
|
||||
Console.PrintMessage("Матрица пересечений:\n")
|
||||
for row in intersection_matrix:
|
||||
Console.PrintMessage(f"{row}\n")
|
||||
|
||||
contact_matrix = use_case.create_contact_matrix()
|
||||
Console.PrintMessage("Матрица контактов:\n")
|
||||
for row in contact_matrix:
|
||||
Console.PrintMessage(f"{row}\n")
|
|
@ -1,81 +0,0 @@
|
|||
import FreeCAD
|
||||
import Part
|
||||
import ImportGui
|
||||
|
||||
class GeometryValidateUseCase:
|
||||
def __init__(self):
|
||||
self.doc = None
|
||||
|
||||
|
||||
|
||||
|
||||
def import_step_and_check(self, file_path):
|
||||
|
||||
FreeCAD.closeDocument("Unnamed")
|
||||
|
||||
|
||||
self.doc = FreeCAD.newDocument("Unnamed")
|
||||
|
||||
|
||||
ImportGui.open(file_path, "Unnamed")
|
||||
|
||||
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
Gui.SendMsgToActiveView("ViewFit")
|
||||
|
||||
|
||||
bodies = getBodies(self.doc)
|
||||
|
||||
|
||||
intersections = self.check_intersections(bodies)
|
||||
self.print_intersections(intersections)
|
||||
|
||||
|
||||
zero_volume_bodies = self.check_zero_volume(bodies)
|
||||
self.print_zero_volume(zero_volume_bodies)
|
||||
|
||||
|
||||
self.save_checked_document(file_path)
|
||||
|
||||
|
||||
def getBodies(doc):
|
||||
bodies = []
|
||||
for obj in doc.Objects:
|
||||
if hasattr(obj, 'TypeId') and obj.TypeId == "Part::Feature":
|
||||
bodies.append(obj)
|
||||
return bodies
|
||||
|
||||
|
||||
|
||||
def check_intersections(self, bodies):
|
||||
intersections = []
|
||||
for i in range(len(bodies)):
|
||||
for j in range(i + 1, len(bodies)):
|
||||
if bodies[i].intersects(bodies[j]):
|
||||
intersections.append((i + 1, j + 1))
|
||||
return intersections
|
||||
|
||||
def check_zero_volume(self, bodies):
|
||||
zero_volume_bodies = [i + 1 for i, body in enumerate(bodies) if body.Volume == 0]
|
||||
return zero_volume_bodies
|
||||
|
||||
def print_intersections(self, intersections):
|
||||
for i, j in intersections:
|
||||
print("Тела пересекаются: Body {} и Body {}".format(i, j))
|
||||
|
||||
def print_zero_volume(self, zero_volume_bodies):
|
||||
for i in zero_volume_bodies:
|
||||
print("Тело {} имеет нулевой объем".format(i))
|
||||
|
||||
def save_checked_document(self, original_file_path):
|
||||
checked_file_path = original_file_path.replace(".step", "_checked.FCStd")
|
||||
FreeCAD.saveDocument(self.doc, checked_file_path)
|
||||
print("Проверенная сборка сохранена в файл:", checked_file_path)
|
||||
FreeCAD.closeDocument("Unnamed")
|
||||
|
||||
|
||||
|
||||
use_case = GeometryValidateUseCase()
|
||||
step_file_path = '/path/to/file'
|
||||
use_case.import_step_and_check(step_file_path)
|
||||
|
|
@ -49,13 +49,7 @@ class Robossembler(Gui.Workbench):
|
|||
"Export_Entities",
|
||||
"ExportGazeboModels",
|
||||
"InsertGraspPose",
|
||||
# "ASM4StructureParsing",
|
||||
"PDDL_CreateTypes",
|
||||
"PDDL_CreateParameters",
|
||||
"PDDL_CreateAction",
|
||||
"PDDL_CreatePredicate",
|
||||
"PDDL_CreateDurativeAction",
|
||||
|
||||
# "ASM4StructureParsing",
|
||||
"Publish_Project"
|
||||
]
|
||||
self.appendToolbar(f"{__class__.__name__} Frames", self.framecommands)
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
# coding: utf-8
|
||||
# Copyright (C) 2023 Ilia Kurochkin <brothermechanic@yandex.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
'''
|
||||
DESCRIPTION.
|
||||
Simple FreeCAD's object test for manifold mawater-tight surface.
|
||||
'''
|
||||
|
||||
import FreeCAD
|
||||
|
||||
|
||||
def is_object_solid(obj):
|
||||
'''If obj is solid return True'''
|
||||
if not isinstance(obj, FreeCAD.DocumentObject):
|
||||
return False
|
||||
|
||||
if not hasattr(obj, 'Shape'):
|
||||
return False
|
||||
|
||||
return obj.Shape.isClosed()
|
131
freecad_workbench/freecad/robossembler/project_validator.py
Normal file
131
freecad_workbench/freecad/robossembler/project_validator.py
Normal file
|
@ -0,0 +1,131 @@
|
|||
import FreeCAD as App
|
||||
import FreeCAD.Console as Console
|
||||
import unicodedata
|
||||
from .helper.is_solid import is_object_solid
|
||||
|
||||
## Программа, содержащая все проверки активного проекта FreeCAD в соответствии с issue#149
|
||||
|
||||
doc = App.ActiveDocument
|
||||
|
||||
#========== Проверка имен ===============
|
||||
class FormalValidator:
|
||||
def __init__(self, document):
|
||||
self.document = document
|
||||
|
||||
def is_unicode_safe(self, name):
|
||||
normalized_name = unicodedata.normalize('NFKD', name).encode('ASCII', 'ignore').decode()
|
||||
return name == normalized_name
|
||||
|
||||
def fix_invalid_names(self):
|
||||
for obj in self.document.Objects:
|
||||
if not self.is_unicode_safe(obj.Label):
|
||||
safe_name = unicodedata.normalize('NFKD', obj.Label).encode('ASCII', 'ignore').decode()
|
||||
obj.Label = safe_name
|
||||
self.document.recompute()
|
||||
|
||||
#запуск проверки и исправления имен файлов
|
||||
def validate(self):
|
||||
invalid_names = [obj.Label for obj in self.document.Objects if not self.is_unicode_safe(obj.Label)]
|
||||
if invalid_names:
|
||||
print("Некорректные имена:")
|
||||
for name in invalid_names:
|
||||
print(name)
|
||||
self.fix_invalid_names()
|
||||
else:
|
||||
print("Все имена корректны.")
|
||||
|
||||
|
||||
class ModelValidator:
|
||||
def __init__(self, document):
|
||||
self.document = document
|
||||
|
||||
def validate_geometry(self):
|
||||
for obj in self.document.Objects:
|
||||
if hasattr(obj, "Shape") and not self.is_object_solid(obj):
|
||||
Console.PrintError(f"Объект '{obj.Name}' не является твердым телом.\n")
|
||||
|
||||
def is_intersecting(self, obj1, obj2):
|
||||
return obj1.Shape.common(obj2.Shape).Volume > 0
|
||||
|
||||
def has_allow_intersections(self, obj):
|
||||
return hasattr(obj, self.ALLOWED_INTERSECTION_ATTRIBUTE) and getattr(obj, self.ALLOWED_INTERSECTION_ATTRIBUTE, False)
|
||||
|
||||
def check_bodies_for_intersections(self):
|
||||
bodies = [obj for obj in self.document.Objects if hasattr(obj, "Shape")]
|
||||
num_bodies = len(bodies)
|
||||
|
||||
for i in range(num_bodies):
|
||||
for j in range(i + 1, num_bodies):
|
||||
body1 = bodies[i]
|
||||
body2 = bodies[j]
|
||||
if self.is_intersecting(body1, body2):
|
||||
if not self.has_allow_intersections(body1) or not self.has_allow_intersections(body2):
|
||||
Console.PrintWarning(
|
||||
f"Пересечение обнаружено между '{body1.Name}' и '{body2.Name}', "
|
||||
f"но у них нет разрешения на пересечение.\n"
|
||||
|
||||
def check_bodies_for_intersections(self):
|
||||
pass
|
||||
|
||||
def check_bodies_without_contacts(self):
|
||||
pass
|
||||
|
||||
def check_bodies_without_material(self):
|
||||
pass
|
||||
|
||||
def check_bodies_with_multiple_materials(self):
|
||||
pass
|
||||
|
||||
def run_checks(self):
|
||||
self.validate_geometry()
|
||||
self.check_bodies_for_intersections()
|
||||
self.check_bodies_without_contacts()
|
||||
self.check_bodies_without_material()
|
||||
self.check_bodies_with_multiple_materials()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class ProjectValidateUseCase:
|
||||
def __init__(self):
|
||||
self.doc = None
|
||||
|
||||
def getBodies(doc):
|
||||
bodies = []
|
||||
for obj in doc.Objects:
|
||||
if hasattr(obj, 'TypeId') and obj.TypeId == "Part::Feature":
|
||||
bodies.append(obj)
|
||||
return bodies
|
||||
|
||||
|
||||
|
||||
def check_intersections(self, bodies):
|
||||
intersections = []
|
||||
for i in range(len(bodies)):
|
||||
for j in range(i + 1, len(bodies)):
|
||||
if bodies[i].intersects(bodies[j]):
|
||||
intersections.append((i + 1, j + 1))
|
||||
return intersections
|
||||
|
||||
def check_zero_volume(self, bodies):
|
||||
zero_volume_bodies = [i + 1 for i, body in enumerate(bodies) if body.Volume == 0]
|
||||
return zero_volume_bodies
|
||||
|
||||
def print_intersections(self, intersections):
|
||||
for i, j in intersections:
|
||||
print("Тела пересекаются: Body {} и Body {}".format(i, j))
|
||||
|
||||
def print_zero_volume(self, zero_volume_bodies):
|
||||
for i in zero_volume_bodies:
|
||||
print("Тело {} имеет нулевой объем".format(i))
|
||||
|
||||
def save_checked_document(self, original_file_path):
|
||||
checked_file_path = original_file_path.replace(".step", "_checked.FCStd")
|
||||
FreeCAD.saveDocument(self.doc, checked_file_path)
|
||||
print("Проверенная сборка сохранена в файл:", checked_file_path)
|
||||
FreeCAD.closeDocument("Unnamed")
|
||||
|
||||
|
||||
|
||||
|
29
freecad_workbench/freecad/update_workbench.sh
Executable file
29
freecad_workbench/freecad/update_workbench.sh
Executable file
|
@ -0,0 +1,29 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Укажите путь к папке, которую нужно скопировать
|
||||
SOURCE_DIR="/home/markvoltov/GitProjects/framework/freecad_workbench/freecad/robossembler"
|
||||
|
||||
# Укажите путь к папке, которую нужно заменить
|
||||
DEST_DIR="/home/markvoltov/.local/share/FreeCAD/Mod/freecad_workbench"
|
||||
|
||||
# Проверка, что исходная папка существует
|
||||
if [ ! -d "$SOURCE_DIR" ]; then
|
||||
echo "Исходная папка не существует: $SOURCE_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Удаление содержимого папки назначения
|
||||
if [ -d "$DEST_DIR" ]; then
|
||||
echo "Удаление содержимого папки назначения: $DEST_DIR"
|
||||
rm -rf "$DEST_DIR/*"
|
||||
else
|
||||
# Создание папки назначения, если её нет
|
||||
echo "Создание папки назначения: $DEST_DIR"
|
||||
mkdir -p "$DEST_DIR"
|
||||
fi
|
||||
|
||||
# Копирование содержимого исходной папки в папку назначения
|
||||
echo "Копирование содержимого из $SOURCE_DIR в $DEST_DIR"
|
||||
cp -r "$SOURCE_DIR/." "$DEST_DIR/"
|
||||
|
||||
echo "Копирование завершено"
|
Loading…
Add table
Add a link
Reference in a new issue