From 3e7bfba1785f91eb83d754140f5828ee9116c5d7 Mon Sep 17 00:00:00 2001 From: Mark Voltov Date: Thu, 9 May 2024 15:26:32 +0300 Subject: [PATCH] =?UTF-8?q?=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=20=D0=BD?= =?UTF-8?q?=D0=B0=D0=B4=20=D0=B2=D0=B0=D0=BB=D0=B8=D0=B4=D0=B0=D1=82=D0=BE?= =?UTF-8?q?=D1=80=D0=BE=D0=BC=20=D0=B3=D0=B5=D0=BE=D0=BC=D0=B5=D1=82=D1=80?= =?UTF-8?q?=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../freecad/robossembler/Frames.py | 39 +----- ...rtExportEntities.py => export_entities.py} | 0 .../geometry_usecase.py | 63 +++++++++ .../robossembler/importFreecadUsecase.py | 81 ----------- .../freecad/robossembler/init_gui.py | 8 +- .../freecad/robossembler/is_object_solid.py | 29 ---- .../{markupEntities.py => markup_entities.py} | 0 .../{poseGenerator.py => pose_generator.py} | 0 .../freecad/robossembler/project_validator.py | 131 ++++++++++++++++++ freecad_workbench/freecad/update_workbench.sh | 29 ++++ 10 files changed, 225 insertions(+), 155 deletions(-) rename freecad_workbench/freecad/robossembler/{ImportExportEntities.py => export_entities.py} (100%) create mode 100644 freecad_workbench/freecad/robossembler/geometric_feasibility_predicate/geometry_usecase.py delete mode 100644 freecad_workbench/freecad/robossembler/importFreecadUsecase.py delete mode 100644 freecad_workbench/freecad/robossembler/is_object_solid.py rename freecad_workbench/freecad/robossembler/{markupEntities.py => markup_entities.py} (100%) rename freecad_workbench/freecad/robossembler/{poseGenerator.py => pose_generator.py} (100%) create mode 100644 freecad_workbench/freecad/robossembler/project_validator.py create mode 100755 freecad_workbench/freecad/update_workbench.sh diff --git a/freecad_workbench/freecad/robossembler/Frames.py b/freecad_workbench/freecad/robossembler/Frames.py index 6a794f9..6473ae5 100644 --- a/freecad_workbench/freecad/robossembler/Frames.py +++ b/freecad_workbench/freecad/robossembler/Frames.py @@ -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")), diff --git a/freecad_workbench/freecad/robossembler/ImportExportEntities.py b/freecad_workbench/freecad/robossembler/export_entities.py similarity index 100% rename from freecad_workbench/freecad/robossembler/ImportExportEntities.py rename to freecad_workbench/freecad/robossembler/export_entities.py diff --git a/freecad_workbench/freecad/robossembler/geometric_feasibility_predicate/geometry_usecase.py b/freecad_workbench/freecad/robossembler/geometric_feasibility_predicate/geometry_usecase.py new file mode 100644 index 0000000..3807ad5 --- /dev/null +++ b/freecad_workbench/freecad/robossembler/geometric_feasibility_predicate/geometry_usecase.py @@ -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") diff --git a/freecad_workbench/freecad/robossembler/importFreecadUsecase.py b/freecad_workbench/freecad/robossembler/importFreecadUsecase.py deleted file mode 100644 index 3ed4fae..0000000 --- a/freecad_workbench/freecad/robossembler/importFreecadUsecase.py +++ /dev/null @@ -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) - diff --git a/freecad_workbench/freecad/robossembler/init_gui.py b/freecad_workbench/freecad/robossembler/init_gui.py index 5cdbc87..760482b 100644 --- a/freecad_workbench/freecad/robossembler/init_gui.py +++ b/freecad_workbench/freecad/robossembler/init_gui.py @@ -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) diff --git a/freecad_workbench/freecad/robossembler/is_object_solid.py b/freecad_workbench/freecad/robossembler/is_object_solid.py deleted file mode 100644 index 1d69d3a..0000000 --- a/freecad_workbench/freecad/robossembler/is_object_solid.py +++ /dev/null @@ -1,29 +0,0 @@ -# coding: utf-8 -# Copyright (C) 2023 Ilia Kurochkin -# -# 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() diff --git a/freecad_workbench/freecad/robossembler/markupEntities.py b/freecad_workbench/freecad/robossembler/markup_entities.py similarity index 100% rename from freecad_workbench/freecad/robossembler/markupEntities.py rename to freecad_workbench/freecad/robossembler/markup_entities.py diff --git a/freecad_workbench/freecad/robossembler/poseGenerator.py b/freecad_workbench/freecad/robossembler/pose_generator.py similarity index 100% rename from freecad_workbench/freecad/robossembler/poseGenerator.py rename to freecad_workbench/freecad/robossembler/pose_generator.py diff --git a/freecad_workbench/freecad/robossembler/project_validator.py b/freecad_workbench/freecad/robossembler/project_validator.py new file mode 100644 index 0000000..a4f8d68 --- /dev/null +++ b/freecad_workbench/freecad/robossembler/project_validator.py @@ -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") + + + + diff --git a/freecad_workbench/freecad/update_workbench.sh b/freecad_workbench/freecad/update_workbench.sh new file mode 100755 index 0000000..3ac11df --- /dev/null +++ b/freecad_workbench/freecad/update_workbench.sh @@ -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 "Копирование завершено"