Доработан плагин экспорта SDF для FreeCAD
1
Init.py
|
@ -1 +0,0 @@
|
|||
#I made this?
|
41
InitGui.py
|
@ -1,41 +0,0 @@
|
|||
class ARBench(Workbench):
|
||||
MenuText = "ARBench"
|
||||
ToolTip = "Annotation for Robotics workbench"
|
||||
Icon = """"""
|
||||
|
||||
def __init__(self):
|
||||
import os
|
||||
self.Icon = os.path.join(FreeCAD.getUserAppDataDir(), "Mod",
|
||||
"ARBench", "UI", "icons", "frame.svg")
|
||||
|
||||
def Initialize(self):
|
||||
"""This function is executed when FreeCAD starts"""
|
||||
import ARFrames
|
||||
self.framecommands = ["FrameCommand",
|
||||
"AllPartFramesCommand",
|
||||
"FeatureFrameCommand"]
|
||||
self.toolcommands = ["ExportPartInfoAndFeaturesDialogueCommand",
|
||||
"ExportGazeboModels",
|
||||
"InsertGraspPose"]
|
||||
self.appendToolbar("AR Frames", self.framecommands)
|
||||
self.appendToolbar("AR Tools", self.toolcommands)
|
||||
|
||||
def Activated(self):
|
||||
"""This function is executed when the workbench is activated."""
|
||||
#
|
||||
return
|
||||
|
||||
def Deactivated(self):
|
||||
"""This function is executed when the workbench is deactivated."""
|
||||
#
|
||||
return
|
||||
|
||||
def ContextMenu(self, recipient):
|
||||
"""This is execcuted whenever the user right-clicks on screen."""
|
||||
pass
|
||||
|
||||
def GetClassName(self):
|
||||
# This function is mandatory if this is a full python workbench
|
||||
return "Gui::PythonWorkbench"
|
||||
|
||||
Gui.addWorkbench(ARBench())
|
33
README.md
|
@ -21,36 +21,3 @@
|
|||
- __Предикат стабильной осуществимости__. Верен для последовательности сборки, когда сборка на каждом из этапов приходит к стабильному состоянию.
|
||||
- __Предикат степеней свободы__. Формируется на основе уже сгенерированных графов/графа сборки. В каких степенях свободы возможно перемещать деталь.
|
||||
|
||||
# Верстак ARBench
|
||||
|
||||
Разметка моделей деталей и сборок для роботизированного производства.
|
||||
|
||||
## Установка
|
||||
|
||||
Поддерживается версия FreeCAD>0.16. Скопируйте содержимое репозитория в директорию `.FreeCAD/Mod/`.
|
||||
|
||||
## Экспорт мета-данных дeталей (en)
|
||||
|
||||
1. Click a small feature e.g. a circle
|
||||
2. Press the feature frame creator (cone with a magnifying glass on it icon)
|
||||
3. Chose type of feature to create
|
||||
4. Chose feature parameters if relevant and the offset of the frame from the feature.
|
||||
5. Repeat 4 for each feature you want on each part
|
||||
6. Click a part and press the export to json button (block->textfile icon)
|
||||
7. Save json
|
||||
8. Use the json with whatever you want. E.g. [`arbench_part_publisher`](https://github.com/mahaarbo/arbench_part_publisher)
|
||||
|
||||
|
||||
## Генерация пакетов деталей для симулятора Gazebo
|
||||
|
||||
Для генерации SDF-пакета из документа FreeCAD выделите нужные детали и нажмите "Gazebo Export" в пользовательском интерфейсе верстака ARBench, после чего будет для каждого отдельного твёрдого тела (детали, `Solid`; `Compound` пока не поддерживаются) будет создана директория со следующей структурой
|
||||
```
|
||||
name_of_part
|
||||
├── model.sdf
|
||||
├── meshes
|
||||
│ └── part.dae
|
||||
└── model.config
|
||||
|
||||
```
|
||||
По умолчанию эти пакеты будут размещены в директории с документом FreeCAD. Чтобы добавить их в Gazebo нужно копировать сгенерированные директории в папку моделей (обычно `~/.gazebo/models`).
|
||||
|
||||
|
|
|
@ -1,401 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Dialog</class>
|
||||
<widget class="QDialog" name="Dialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>323</width>
|
||||
<height>325</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>316</width>
|
||||
<height>325</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
</property>
|
||||
<widget class="QLabel" name="TaskLabel">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>20</y>
|
||||
<width>71</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Task Label</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="PushButton">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>150</x>
|
||||
<y>290</y>
|
||||
<width>161</width>
|
||||
<height>27</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Get from selection</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="lineEdit">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>100</x>
|
||||
<y>20</y>
|
||||
<width>211</width>
|
||||
<height>27</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="HoleLabel">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>50</y>
|
||||
<width>31</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Hole</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="PegLabel">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>130</y>
|
||||
<width>31</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Peg</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="HolePartLabel">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>50</x>
|
||||
<y>70</y>
|
||||
<width>41</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Part:</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="HoleFaceLabel">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>50</x>
|
||||
<y>90</y>
|
||||
<width>41</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Face:</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="PegPartLabel">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>50</x>
|
||||
<y>150</y>
|
||||
<width>41</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Part:</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="PegFaceLabel">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>50</x>
|
||||
<y>170</y>
|
||||
<width>41</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Face:</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="HolePartField">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>100</x>
|
||||
<y>70</y>
|
||||
<width>231</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>PartLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="HoleFaceIDField">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>100</x>
|
||||
<y>90</y>
|
||||
<width>231</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>FaceID</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="PegFaceIDField">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>100</x>
|
||||
<y>170</y>
|
||||
<width>231</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>FaceID</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="PegPartField">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>100</x>
|
||||
<y>150</y>
|
||||
<width>231</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>PartLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="HoleFeatureSummary">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>100</x>
|
||||
<y>110</y>
|
||||
<width>231</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Feature Description</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="HoleFeatureLabel">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>30</x>
|
||||
<y>110</y>
|
||||
<width>61</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Feature:</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="PegFeatureSummary">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>100</x>
|
||||
<y>190</y>
|
||||
<width>231</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Feature Description</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="PegFeatureLabel">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>30</x>
|
||||
<y>190</y>
|
||||
<width>61</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Feature:</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="YLabel">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>80</x>
|
||||
<y>250</y>
|
||||
<width>16</width>
|
||||
<height>17</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>y</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>YBox</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="XLabel">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>80</x>
|
||||
<y>230</y>
|
||||
<width>16</width>
|
||||
<height>17</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>x</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>XBox</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="ZLabel">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>80</x>
|
||||
<y>270</y>
|
||||
<width>16</width>
|
||||
<height>17</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>z</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>ZBox</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QDoubleSpinBox" name="YBox">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>100</x>
|
||||
<y>250</y>
|
||||
<width>61</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>offset in part frame z direction</string>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>-1.000000000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.100000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QDoubleSpinBox" name="XBox">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>100</x>
|
||||
<y>230</y>
|
||||
<width>61</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Offset in part frame x direction</string>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>-1.000000000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.100000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QDoubleSpinBox" name="ZBox">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>100</x>
|
||||
<y>270</y>
|
||||
<width>61</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>offset in part frame z direction</string>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>-1.000000000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.100000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="AssemblyAxisLabel">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>210</y>
|
||||
<width>121</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Assembly Axis</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="AnimateButton">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>290</y>
|
||||
<width>131</width>
|
||||
<height>27</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Animate</string>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -1,5 +1,18 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2023 by brothermechanic. All Rights Reserved.
|
||||
# Based on ARBench by github/mahaarbo
|
||||
# This library is free software: you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation, either
|
||||
# version 3 of the License, or (at your option) any later version.
|
||||
# This library 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
|
||||
# Lesser General Public License for more details.
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
import FreeCAD
|
||||
import ARTools
|
||||
import Tools
|
||||
if FreeCAD.GuiUp:
|
||||
import FreeCADGui
|
||||
from pivy import coin
|
||||
|
@ -7,11 +20,11 @@ if FreeCAD.GuiUp:
|
|||
import Part
|
||||
import os
|
||||
|
||||
__title__ = "ARFrames"
|
||||
__author__ = "Mathias Hauan Arbo"
|
||||
__workbenchname__ = "ARBench"
|
||||
__version__ = "0.1"
|
||||
__url__ = "https://github.com/mahaarbo/ARBench"
|
||||
__title__ = 'Frames'
|
||||
__author__ = 'brothermechanic'
|
||||
__workbenchname__ = 'Frames'
|
||||
__version__ = '0.1'
|
||||
__url__ = ["https://robossembler.org"]
|
||||
__doc__ = """"""
|
||||
|
||||
|
||||
|
@ -44,7 +57,7 @@ class Frame(object):
|
|||
def getDict(self):
|
||||
d = {}
|
||||
d["label"] = str(self.obj.Label)
|
||||
d["placement"] = ARTools.placement2axisvec(self.obj.Placement)
|
||||
d["placement"] = Tools.placement2axisvec(self.obj.Placement)
|
||||
d.update(self.additional_data)
|
||||
return d
|
||||
|
||||
|
@ -95,7 +108,7 @@ class FeatureFrame(PartFrame):
|
|||
|
||||
def getDict(self):
|
||||
d = PartFrame.getDict(self)
|
||||
d["featureplacement"] = ARTools.placement2axisvec(self.obj.FeaturePlacement)
|
||||
d["featureplacement"] = Tools.placement2axisvec(self.obj.FeaturePlacement)
|
||||
d["shapetype"] = str(self.obj.ShapeType)
|
||||
d["positioning"] = str(self.obj.Positioning)
|
||||
return d
|
||||
|
@ -126,7 +139,7 @@ class ViewProviderFrame(object):
|
|||
# and Toggle_DH_Frames by galou_breizh on the forums
|
||||
self.vframe = coin.SoType.fromName("SoShapeScale").createInstance()
|
||||
self.vframe.setPart("shape", coin.SoType.fromName("SoAxisCrossKit").createInstance())
|
||||
self.vframe.scaleFactor.setValue(0.1)
|
||||
self.vframe.scaleFactor.setValue(0.2)
|
||||
ax = self.vframe.getPart("shape", 0)
|
||||
cone = ax.getPart("xHead.shape", 0)
|
||||
cone.bottomRadius.setValue(vobj.HeadSize)
|
||||
|
@ -245,9 +258,9 @@ def makeFrame(placement=FreeCAD.Placement()):
|
|||
def makePartFrame(part):
|
||||
obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython", "PartFrame")
|
||||
PartFrame(obj, part)
|
||||
if int(FreeCAD.Version()[1]) > 16:
|
||||
geo_feature_group = part.getParentGeoFeatureGroup()
|
||||
geo_feature_group.addObject(obj)
|
||||
#if int(FreeCAD.Version()[1]) > 16:
|
||||
# geo_feature_group = part.getParentGeoFeatureGroup()
|
||||
# geo_feature_group.addObject(obj)
|
||||
if FreeCAD.GuiUp:
|
||||
ViewProviderPartFrame(obj.ViewObject)
|
||||
return obj
|
||||
|
@ -258,13 +271,18 @@ def makeFeatureFrame(part, featurepl):
|
|||
"FeatureFrame")
|
||||
FeatureFrame(obj, part, featurepl)
|
||||
# If we're >0.16, add the feature frame to the assembly
|
||||
if int(FreeCAD.Version()[1]) > 16:
|
||||
geo_feature_group = part.getParentGeoFeatureGroup()
|
||||
geo_feature_group.addObject(obj)
|
||||
#if int(FreeCAD.Version()[1]) > 16:
|
||||
# geo_feature_group = part.getParentGeoFeatureGroup()
|
||||
# geo_feature_group.addObject(obj)
|
||||
if FreeCAD.GuiUp:
|
||||
ViewProviderFeatureFrame(obj.ViewObject)
|
||||
return obj
|
||||
|
||||
def makeSelectedPartFrames():
|
||||
for part in FreeCADGui.Selection.getSelection():
|
||||
if isinstance(part, Part.Feature):
|
||||
pf = makePartFrame(part)
|
||||
pf.Label = "Frame"+str(part.Label)
|
||||
|
||||
def makeAllPartFrames():
|
||||
dc = FreeCAD.activeDocument()
|
||||
|
@ -286,22 +304,28 @@ uidir = os.path.join(FreeCAD.getUserAppDataDir(),
|
|||
"Mod", __workbenchname__, "UI")
|
||||
icondir = os.path.join(uidir, "icons")
|
||||
|
||||
ARTools.spawnClassCommand("FrameCommand",
|
||||
Tools.spawnClassCommand("FrameCommand",
|
||||
makeFrame,
|
||||
{"Pixmap": str(os.path.join(icondir, "frame.svg")),
|
||||
"MenuText": "Make a free frame",
|
||||
"ToolTip": "Make a freestanding reference frame."})
|
||||
|
||||
ARTools.spawnClassCommand("AllPartFramesCommand",
|
||||
Tools.spawnClassCommand("SelectedPartFrameCommand",
|
||||
makeSelectedPartFrames,
|
||||
{"Pixmap": str(os.path.join(icondir, "partframe.svg")),
|
||||
"MenuText": "selected parts frames",
|
||||
"ToolTip": "Make selected parts frames."})
|
||||
|
||||
Tools.spawnClassCommand("AllPartFramesCommand",
|
||||
makeAllPartFrames,
|
||||
{"Pixmap": str(os.path.join(icondir, "allpartframes.svg")),
|
||||
"MenuText": "All part frames",
|
||||
"ToolTip": "Make all part frames."})
|
||||
ARTools.spawnClassCommand("FeatureFrameCommand",
|
||||
"MenuText": "All parts frames",
|
||||
"ToolTip": "Make all parts frames."})
|
||||
Tools.spawnClassCommand("FeatureFrameCommand",
|
||||
spawnFeatureFrameCreator,
|
||||
{"Pixmap": str(os.path.join(icondir, "featureframecreator.svg")),
|
||||
"MenuText": "Feature frame creator",
|
||||
"ToolTip": "Create a feature frame on selected primitive."})
|
||||
"MenuText": "frame on selected primitive",
|
||||
"ToolTip": "Create a frame on selected primitive."})
|
||||
|
||||
|
||||
###################################################################
|
||||
|
@ -327,7 +351,7 @@ class FeatureFramePanel:
|
|||
self.reject()
|
||||
|
||||
# Choices related to selection
|
||||
so_desc = ARTools.describeSubObject(selected.SubObjects[0])
|
||||
so_desc = Tools.describeSubObject(selected.SubObjects[0])
|
||||
self.so_desc = so_desc
|
||||
shape_choices = {
|
||||
"Vertex": [],
|
||||
|
@ -428,7 +452,7 @@ class BaseFeaturePanel(object):
|
|||
self.fframe = makeFeatureFrame(self.selected.Object, self.local_ffpl)
|
||||
self.fframe.PrimitiveType = self.so_desc[0]
|
||||
self.fframe.ShapeType = self.so_desc[1]
|
||||
ad = ARTools.getPrimitiveInfo(self.so_desc[0],
|
||||
ad = Tools.getPrimitiveInfo(self.so_desc[0],
|
||||
self.selected.SubObjects[0])
|
||||
self.fframe.Proxy.additional_data.update(ad)
|
||||
|
|
@ -65,7 +65,7 @@ def insert():
|
|||
|
||||
def controlled_insert(code):
|
||||
a = FreeCAD.ActiveDocument.Objects
|
||||
Part.insert(u"C:/Users/ibryl/AppData/Roaming/FreeCAD/Mod/ARBench/"+code+".brep",FreeCAD.ActiveDocument.Name)
|
||||
Part.insert(u"~/.local/share/FreeCAD/Mod/Frames/"+code+".brep",FreeCAD.ActiveDocument.Name)
|
||||
b = FreeCAD.ActiveDocument.Objects
|
||||
return list(set(b) - set(a))[0]
|
||||
|
2
cg/freecad/Frames/Init.py
Normal file
|
@ -0,0 +1,2 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""FreeCAD init script"""
|
70
cg/freecad/Frames/InitGui.py
Normal file
|
@ -0,0 +1,70 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2023 by brothermechanic. All Rights Reserved.
|
||||
# Based on ARBench by github/mahaarbo
|
||||
# This library is free software: you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation, either
|
||||
# version 3 of the License, or (at your option) any later version.
|
||||
# This library 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
|
||||
# Lesser General Public License for more details.
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
__title__='Frames Workbench'
|
||||
__author__ = 'brothermechanic'
|
||||
__copyright__ = 'Copyright (c) 2023 brothermechanic'
|
||||
__license__ = 'GPL-2.1'
|
||||
__version__ = "0.1"
|
||||
__email__ = 'brothermechanic@gmail.com'
|
||||
__url__ = ["https://robossembler.org"]
|
||||
__status__ = 'development'
|
||||
|
||||
|
||||
class Frames(Workbench):
|
||||
MenuText = "Frames"
|
||||
ToolTip = "Frames Workbench"
|
||||
Icon = """"""
|
||||
|
||||
def __init__(self):
|
||||
import os
|
||||
self.Icon = os.path.join(FreeCAD.getUserAppDataDir(), "Mod",
|
||||
__class__.__name__, "UI", "icons", "frame.svg")
|
||||
|
||||
def Initialize(self):
|
||||
"""This function is executed when FreeCAD starts"""
|
||||
import Frames
|
||||
self.framecommands = [
|
||||
"FrameCommand",
|
||||
"SelectedPartFrameCommand",
|
||||
"AllPartFramesCommand",
|
||||
"FeatureFrameCommand"
|
||||
]
|
||||
self.toolcommands = [
|
||||
"ExportPartInfoAndFeaturesDialogueCommand",
|
||||
"ExportGazeboModels",
|
||||
"InsertGraspPose"
|
||||
]
|
||||
self.appendToolbar(f"{__class__.__name__} Frames", self.framecommands)
|
||||
self.appendToolbar(f"{__class__.__name__} Tools", self.toolcommands)
|
||||
|
||||
def Activated(self):
|
||||
"""This function is executed when the workbench is activated."""
|
||||
#
|
||||
return
|
||||
|
||||
def Deactivated(self):
|
||||
"""This function is executed when the workbench is deactivated."""
|
||||
#
|
||||
return
|
||||
|
||||
def ContextMenu(self, recipient):
|
||||
"""This is execcuted whenever the user right-clicks on screen."""
|
||||
pass
|
||||
|
||||
def GetClassName(self):
|
||||
# This function is mandatory if this is a full python workbench
|
||||
return "Gui::PythonWorkbench"
|
||||
|
||||
Gui.addWorkbench(Frames())
|
34
cg/freecad/Frames/README.md
Normal file
|
@ -0,0 +1,34 @@
|
|||
# Плагин для FreeCAD
|
||||
# Верстак ARBench
|
||||
|
||||
Разметка моделей деталей и сборок для роботизированного производства.
|
||||
|
||||
## Установка
|
||||
|
||||
Поддерживается версия FreeCAD>0.16. Скопируйте содержимое репозитория в директорию `.FreeCAD/Mod/`.
|
||||
|
||||
## Экспорт мета-данных дeталей (en)
|
||||
|
||||
1. Click a small feature e.g. a circle
|
||||
2. Press the feature frame creator (cone with a magnifying glass on it icon)
|
||||
3. Chose type of feature to create
|
||||
4. Chose feature parameters if relevant and the offset of the frame from the feature.
|
||||
5. Repeat 4 for each feature you want on each part
|
||||
6. Click a part and press the export to json button (block->textfile icon)
|
||||
7. Save json
|
||||
8. Use the json with whatever you want. E.g. [`arbench_part_publisher`](https://github.com/mahaarbo/arbench_part_publisher)
|
||||
|
||||
|
||||
## Генерация пакетов деталей для симулятора Gazebo
|
||||
|
||||
Для генерации SDF-пакета из документа FreeCAD выделите нужные детали и нажмите "Gazebo Export" в пользовательском интерфейсе верстака ARBench, после чего будет для каждого отдельного твёрдого тела (детали, `Solid`; `Compound` пока не поддерживаются) будет создана директория со следующей структурой
|
||||
```
|
||||
name_of_part
|
||||
├── model.sdf
|
||||
├── meshes
|
||||
│ └── part.dae
|
||||
└── model.config
|
||||
|
||||
```
|
||||
По умолчанию эти пакеты будут размещены в директории с документом FreeCAD. Чтобы добавить их в Gazebo нужно копировать сгенерированные директории в папку моделей (обычно `~/.gazebo/models`).
|
||||
|
|
@ -1,3 +1,16 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2023 by brothermechanic. All Rights Reserved.
|
||||
# Based on ARBench by github/mahaarbo
|
||||
# This library is free software: you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation, either
|
||||
# version 3 of the License, or (at your option) any later version.
|
||||
# This library 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
|
||||
# Lesser General Public License for more details.
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
import FreeCAD
|
||||
import Part
|
||||
import json # For exporting part infos
|
||||
|
@ -7,12 +20,12 @@ import GraspPose
|
|||
if FreeCAD.GuiUp:
|
||||
import FreeCADGui
|
||||
from PySide import QtGui
|
||||
|
||||
__title__ = "ARTools"
|
||||
__author__ = "Mathias Hauan Arbo"
|
||||
__workbenchname__ = "ARBench"
|
||||
__version__ = "0.1"
|
||||
__url__ = "https://github.com/mahaarbo/ARBench"
|
||||
import time
|
||||
__title__ = 'Tools'
|
||||
__author__ = 'brothermechanic'
|
||||
__workbenchname__ = 'Frames'
|
||||
__version__ = '0.1'
|
||||
__url__ = ["https://robossembler.org"]
|
||||
__doc__ = """
|
||||
Useful tools for the Annotations for Robotics workbench."""
|
||||
|
||||
|
@ -161,8 +174,8 @@ def spawnClassCommand(classname, function, resources):
|
|||
|
||||
def getLocalPartProps(obj):
|
||||
old_placement = obj.Placement
|
||||
obj.Placement = FreeCAD.Placement()
|
||||
# Part properties
|
||||
# obj.Placement = FreeCAD.Placement()
|
||||
""" Part properties """
|
||||
partprops = {
|
||||
"label": obj.Label,
|
||||
"placement": placement2pose(old_placement),
|
||||
|
@ -171,7 +184,7 @@ def getLocalPartProps(obj):
|
|||
# "centerofmass": vector2list(obj.Shape.CenterOfMass),
|
||||
# "principalproperties": principalProperties2dict(obj.Shape.PrincipalProperties)
|
||||
}
|
||||
obj.Placement = old_placement
|
||||
# obj.Placement = old_placement
|
||||
return partprops
|
||||
|
||||
# Longest match for mesh name
|
||||
|
@ -243,9 +256,9 @@ def exportGazeboModels():
|
|||
parts[obj.PartToHandle.Label].update({"graspposes" : graspposes})
|
||||
|
||||
# Add part placement position on Plane surface
|
||||
import ARFrames
|
||||
import Frames
|
||||
if hasattr(obj, 'Proxy') and "ShapeType" in obj.PropertiesList:
|
||||
if isinstance(obj.Proxy, ARFrames.FeatureFrame) and obj.ShapeType == 'Face':
|
||||
if isinstance(obj.Proxy, Frames.FeatureFrame) and obj.ShapeType == 'Face':
|
||||
parts[obj.Part.Label].update({ "placements": { obj.Label: placement2pose(obj.Placement) } })
|
||||
|
||||
# Create SDF package from Parts or other packages
|
||||
|
@ -260,7 +273,7 @@ def exportGazeboModels():
|
|||
with open(os.path.join(model_dir, 'model.config'), 'w') as config_file:
|
||||
config_file.write(GazeboExport.config(name,
|
||||
'model.sdf', 'Author', 'Email', 'Comment', 'Version'))
|
||||
|
||||
'''
|
||||
with open(os.path.join(model_dir, 'frames.json'), 'w') as frames_file:
|
||||
json.dump({"label": name,
|
||||
"placement": placement2pose(parts[name]["obj"].Placement),
|
||||
|
@ -268,11 +281,15 @@ def exportGazeboModels():
|
|||
{ "graspposes" : parts[name]["graspposes"]
|
||||
, "placements" : parts[name]["placements"]}},
|
||||
frames_file, indent=1, separators=(',', ': '))
|
||||
'''
|
||||
|
||||
|
||||
|
||||
# Export assets for parts
|
||||
for obj in selected_objects:
|
||||
create_package(obj.Label, [obj], export_dir)
|
||||
exportPartInfo(obj, os.path.join(export_dir, obj.Label, 'frames.json') )
|
||||
appendFeatureFrames(obj, os.path.join(export_dir, obj.Label, 'frames.json'))
|
||||
|
||||
# Export asset for subassembly
|
||||
# subasm_name = "_".join(list(map(lambda x: x.Label[:8], selected_objects)))
|
||||
|
@ -290,17 +307,11 @@ def exportPartInfo(obj, ofile):
|
|||
For more information on principal properties, see TopoShape in OCCT
|
||||
documentation.
|
||||
"""
|
||||
# File path stuff
|
||||
odir, of = os.path.split(ofile)
|
||||
if not os.path.exists(odir):
|
||||
os.makedirs(odir)
|
||||
if not of.lower().endswith(".json"):
|
||||
ofile = ofile + ".json"
|
||||
|
||||
partprops = getLocalPartProps(obj)
|
||||
with open(ofile, "w", encoding="utf8") as propfile:
|
||||
json.dump(partprops, propfile, indent=1, separators=(',', ': '))
|
||||
return True
|
||||
return ofile
|
||||
|
||||
|
||||
def appendPartInfo(obj, ofile):
|
||||
|
@ -323,18 +334,12 @@ def appendPartInfo(obj, ofile):
|
|||
def exportFeatureFrames(obj, ofile):
|
||||
"""Exports feature frames attached to a part."""
|
||||
# Get the feature frames
|
||||
import ARFrames
|
||||
ff_check = lambda x: isinstance(x.Proxy, ARFrames.FeatureFrame) if hasattr(x, 'Proxy') else False
|
||||
import Frames
|
||||
ff_check = lambda x: isinstance(x.Proxy, Frames.FeatureFrame) if hasattr(x, 'Proxy') else False
|
||||
ff_list = filter(ff_check, obj.InList)
|
||||
ff_named = { ff.Label: ff.Proxy.getDict() for ff in ff_list }
|
||||
feature_dict = { "features": ff_named }
|
||||
|
||||
# File stuff
|
||||
odir, of = os.path.split(ofile)
|
||||
if not os.path.exists(odir):
|
||||
os.makedirs(odir)
|
||||
if not of.lower().endswith(".json"):
|
||||
ofile = ofile + ".json"
|
||||
with open(ofile, "w", encoding="utf8") as propfile:
|
||||
json.dump(feature_dict, propfile, indent=1, separators=(',', ': '))
|
||||
return True
|
||||
|
@ -344,12 +349,13 @@ def appendFeatureFrames(obj, ofile):
|
|||
"""Rewrites/appends featureframes attached to a part to an existing json
|
||||
file."""
|
||||
# Get the feature frames
|
||||
import ARFrames
|
||||
import Frames
|
||||
with open(ofile, "r", encoding="utf8") as propfile:
|
||||
partprops = json.load(propfile)
|
||||
ff_check = lambda x: isinstance(x.Proxy, ARFrames.FeatureFrame) if hasattr(x, 'Proxy') else False
|
||||
ff_check = lambda x: isinstance(x.Proxy, Frames.FeatureFrame) if hasattr(x, 'Proxy') else False
|
||||
# part's children list
|
||||
ff_list = filter(ff_check, obj.InList)
|
||||
ff_named = { ff.Label: {"label": ff.Label, "placement": placement2pose(ff.Placement)} for ff in ff_list }
|
||||
ff_named = { ff.Label: {"label": ff.Label, "placement": placement2pose(ff.FeaturePlacement)} for ff in ff_list }
|
||||
feature_dict = { "features": ff_named }
|
||||
if "features" not in partprops.keys():
|
||||
partprops.update(feature_dict)
|
||||
|
@ -413,7 +419,7 @@ def exportPartInfoDialogue():
|
|||
def exportFeatureFramesDialogue():
|
||||
"""Spawns a dialogue window for a part's feature frames to be exported."""
|
||||
# Select only true parts
|
||||
import ARFrames
|
||||
import Frames
|
||||
s = FreeCADGui.Selection.getSelection()
|
||||
FreeCADGui.Selection.clearSelection()
|
||||
if len(s) == 0:
|
||||
|
@ -421,7 +427,7 @@ def exportFeatureFramesDialogue():
|
|||
return False
|
||||
unique_selected = []
|
||||
for item in s:
|
||||
if item not in unique_selected and isinstance(item.Proxy, ARFrames.FeatureFrame):
|
||||
if item not in unique_selected and isinstance(item.Proxy, Frames.FeatureFrame):
|
||||
# Ensuring that we are parts
|
||||
unique_selected.append(item)
|
||||
FreeCADGui.Selection.addSelection(item)
|
||||
|
@ -461,7 +467,7 @@ def exportFeatureFramesDialogue():
|
|||
|
||||
def exportPartInfoAndFeaturesDialogue():
|
||||
"""Spawns a dialogue window for exporting both."""
|
||||
import ARFrames
|
||||
import Frames
|
||||
s = FreeCADGui.Selection.getSelection()
|
||||
FreeCADGui.Selection.clearSelection()
|
||||
if len(s) == 0:
|
||||
|
@ -486,6 +492,14 @@ def exportPartInfoAndFeaturesDialogue():
|
|||
if ofile == "":
|
||||
# User cancelled
|
||||
return False
|
||||
|
||||
# File path stuff
|
||||
odir, of = os.path.split(ofile)
|
||||
if not os.path.exists(odir):
|
||||
os.makedirs(odir)
|
||||
if not of.lower().endswith(".json"):
|
||||
ofile = ofile + ".json"
|
||||
|
||||
if os.path.exists(ofile):
|
||||
msgbox = QtGui.QMessageBox()
|
||||
msgbox.setText("File already exists. We can overwrite the file, or add the information/rewrite only relevant sections.")
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 6.1 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 8.2 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 6.1 KiB |
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 101 KiB |
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
130
example.json
|
@ -1,130 +0,0 @@
|
|||
{
|
||||
"Gripper_for_Box": {
|
||||
"Part name": "Box",
|
||||
"Part label": "\u041a\u0443\u0431",
|
||||
"Part position XYZ": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"Part rotation axis XYZ": [
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
],
|
||||
"Part rotation angle": 0.0,
|
||||
"Gripper position XYZ": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"Gripper rotation axis XYZ": [
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
],
|
||||
"Gripper rotation angle": 0.0,
|
||||
"Grip size": 10.0,
|
||||
"OperationType": 0,
|
||||
"Operation Priority": 0,
|
||||
"a.Operation Parameter 1": 0.0,
|
||||
"a.Operation Parameter 2": 0.0,
|
||||
"a.Operation Parameter 3": 0.0
|
||||
},
|
||||
"Gripper_for_Cylinder": {
|
||||
"Part name": "Cylinder",
|
||||
"Part label": "\u0426\u0438\u043b\u0438\u043d\u0434\u0440",
|
||||
"Part position XYZ": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"Part rotation axis XYZ": [
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
],
|
||||
"Part rotation angle": 0.0,
|
||||
"Gripper position XYZ": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"Gripper rotation axis XYZ": [
|
||||
1.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"Gripper rotation angle": 4.1887902047863905,
|
||||
"Grip size": 3.9708354963922163,
|
||||
"OperationType": 0,
|
||||
"Operation Priority": 0,
|
||||
"a.Operation Parameter 1": 0.0,
|
||||
"a.Operation Parameter 2": 0.0,
|
||||
"a.Operation Parameter 3": 0.0
|
||||
},
|
||||
"Gripper_for_Box001": {
|
||||
"Part name": "Box001",
|
||||
"Part label": "\u041a\u0443\u0431001",
|
||||
"Part position XYZ": [
|
||||
0.0,
|
||||
9.0,
|
||||
0.0
|
||||
],
|
||||
"Part rotation axis XYZ": [
|
||||
0.0,
|
||||
0.0,
|
||||
1.0
|
||||
],
|
||||
"Part rotation angle": 0.0,
|
||||
"Gripper position XYZ": [
|
||||
3.8035643556662895,
|
||||
99.89718356183919,
|
||||
-9.331967349091768
|
||||
],
|
||||
"Gripper rotation axis XYZ": [
|
||||
-0.6901619708141958,
|
||||
0.717463959241293,
|
||||
0.09445592216305763
|
||||
],
|
||||
"Gripper rotation angle": 3.570126372396101,
|
||||
"Grip size": 20.0,
|
||||
"OperationType": 0,
|
||||
"Operation Priority": 0,
|
||||
"a.Operation Parameter 1": 0.0,
|
||||
"a.Operation Parameter 2": 0.0,
|
||||
"a.Operation Parameter 3": 0.0
|
||||
},
|
||||
"Gripper_for_Tube": {
|
||||
"Part name": "Tube",
|
||||
"Part label": "Tube",
|
||||
"Part position XYZ": [
|
||||
0.0,
|
||||
370.0,
|
||||
10.0
|
||||
],
|
||||
"Part rotation axis XYZ": [
|
||||
1.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"Part rotation angle": 1.5707963267948966,
|
||||
"Gripper position XYZ": [
|
||||
0.0,
|
||||
345.0,
|
||||
10.0
|
||||
],
|
||||
"Gripper rotation axis XYZ": [
|
||||
1.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"Gripper rotation angle": 1.5707963267948966,
|
||||
"Grip size": 50.0,
|
||||
"OperationType": 0,
|
||||
"Operation Priority": 0,
|
||||
"a.Operation Parameter 1": 0.0,
|
||||
"a.Operation Parameter 2": 0.0,
|
||||
"a.Operation Parameter 3": 0.0
|
||||
}
|
||||
}
|