Export helper object from FreeCAD: grasp poses, part's zero positions, planar (tables, surfaces) and volumetric zones (storage, boxes)
This commit is contained in:
parent
a38c3bec5a
commit
9fa936cfba
26 changed files with 12259 additions and 2 deletions
286
cg/freecad/Frames/AuxObjCreation.py
Normal file
286
cg/freecad/Frames/AuxObjCreation.py
Normal file
|
@ -0,0 +1,286 @@
|
|||
|
||||
|
||||
|
||||
"
|
||||
#Создаем объект, к которому привязываем вспомогательную информацию:
|
||||
1. Выбираем элемент в модели:
|
||||
1.1. Поверхность - можем создать призматический объект. Соответствует точке захвата, рабочей зоне, опорной поверхности, поверхности базирования
|
||||
1.1.1 Выбираем тип объкта, который нам нужно построить.
|
||||
1.1.2 В зависимости от выбора, предлагается указать объект для ориентации осей.
|
||||
1.1.3 Указать размеры зоны построения. Для захвата соответствует габаритам пальца, для рабочей зоны - координатам. Полуавтоматическое (ручное???) создание эскиза?
|
||||
1.1.4 Для захвата выбираем вытягивание объекта на нужную длину до параллельной поверхности
|
||||
1.1.5 - Результат - построенный параллелограмм с размерами и привязкой к моделям. Его мы экспортируем в json через существующий функционал
|
||||
|
||||
1.2. Цилиндрическая поверхность - осесимметричный объект. Захват двухпальцевый, трехпальцевый, четырехпальцевый, цилиндрическая зона установки, отверстия сопряжения
|
||||
1.2.1 Выбираем тип объекта
|
||||
1.2.2. Выбираем ориентацию главной оси. //Потенциально, мы можем захватить цилиндрический объект в любой ориентации, нужно ли добавлять конкретное указание?
|
||||
видимо, только если геометрия обязывает нас придерживаться ее (напр. не дает поставить 4 палец)
|
||||
указание требуется, если есть фиксаторы, опорные площадки или что-то иное, что приводит к однозначному позиционированию
|
||||
1.2.3. Вытягиваем зону до привязки. Пальцы ставим как цилиндрический массив( нужно ли??)
|
||||
1.2.4 Результат - цилиндрическая зона с радиусом, ориентациями осей.
|
||||
2. К размеченным объектам необходимо привязать метаданные, содержащие связи с конкретными моделями. STEP-файл захвата, step-файл входной детали и/или выходной.
|
||||
2.1 Нужно ли привязывать захват? Да, но в некоем обобщенном виде. Позиция захвата - характеристика способа воздействия, а не объекта.
|
||||
2.2 Для станков входы и выходы необходимы.
|
||||
2.3
|
||||
"
|
||||
|
||||
|
||||
#!/usr/bin/env python3
|
||||
# coding: utf-8
|
||||
#
|
||||
# LGPL
|
||||
# Copyright HUBERT Zoltán
|
||||
#
|
||||
# newDatumCmd.py
|
||||
|
||||
|
||||
import os
|
||||
|
||||
from PySide import QtGui, QtCore
|
||||
import FreeCADGui as Gui
|
||||
import FreeCAD as App
|
||||
from FreeCAD import Console as FCC
|
||||
|
||||
import Asm4_libs as Asm4
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
+-----------------------------------------------+
|
||||
| a class to create all Datum objects |
|
||||
+-----------------------------------------------+
|
||||
"""
|
||||
class newDatum:
|
||||
"My tool object"
|
||||
def __init__(self, datumName):
|
||||
self.datumName = datumName
|
||||
# recognised containers (not the same as Asm4.containerTypes !)
|
||||
self.containers = [ 'App::Part', 'PartDesign::Body', 'App::DocumentObjectGroup']
|
||||
if self.datumName == 'Point':
|
||||
self.datumType = 'PartDesign::Point'
|
||||
self.menutext = "New Point"
|
||||
self.tooltip = "Create a new Datum Point in a Part"
|
||||
self.icon = os.path.join( Asm4.iconPath , 'Asm4_Point.svg')
|
||||
self.datumColor = (0.00,0.00,0.00)
|
||||
self.datumAlpha = []
|
||||
elif self.datumName == 'Axis':
|
||||
self.datumType = 'PartDesign::Line'
|
||||
self.menutext = "New Axis"
|
||||
self.tooltip = "Create a new Datum Axis in a Part"
|
||||
self.icon = os.path.join( Asm4.iconPath , 'Asm4_Axis.svg')
|
||||
self.datumColor = (0.00,0.00,0.50)
|
||||
self.datumAlpha = []
|
||||
elif self.datumName == 'Plane':
|
||||
self.datumType = 'PartDesign::Plane'
|
||||
self.menutext = "New Plane"
|
||||
self.tooltip = "Create a new Datum Plane in a Part"
|
||||
self.icon = os.path.join( Asm4.iconPath , 'Asm4_Plane.svg')
|
||||
self.datumColor = (0.50,0.50,0.50)
|
||||
self.datumAlpha = 80
|
||||
elif self.datumName == 'LCS':
|
||||
self.datumType = 'PartDesign::CoordinateSystem'
|
||||
self.menutext = "New Coordinate System"
|
||||
self.tooltip = "Create a new Coordinate System in a Part"
|
||||
self.icon = os.path.join( Asm4.iconPath , 'Asm4_CoordinateSystem.svg')
|
||||
self.datumColor = []
|
||||
self.datumAlpha = []
|
||||
elif self.datumName == 'Sketch':
|
||||
self.datumType = 'Sketcher::SketchObject'
|
||||
self.menutext = "New Sketch"
|
||||
self.tooltip = "Create a new Sketch in a Part"
|
||||
self.icon = os.path.join( Asm4.iconPath , 'Asm4_Sketch.svg')
|
||||
self.datumColor = []
|
||||
self.datumAlpha = []
|
||||
|
||||
|
||||
def GetResources(self):
|
||||
return {"MenuText": self.menutext,
|
||||
"ToolTip": self.tooltip,
|
||||
"Pixmap" : self.icon }
|
||||
|
||||
|
||||
def IsActive(self):
|
||||
if App.ActiveDocument:
|
||||
# is something correct selected ?
|
||||
if self.checkSelection():
|
||||
return(True)
|
||||
return(False)
|
||||
|
||||
|
||||
def checkSelection(self):
|
||||
# if something is selected ...
|
||||
if Gui.Selection.getSelection():
|
||||
selectedObj = Gui.Selection.getSelection()[0]
|
||||
# ... and it's an App::Part or an datum object
|
||||
selType = selectedObj.TypeId
|
||||
if selType in self.containers or selType in Asm4.datumTypes or selType=='Sketcher::SketchObject':
|
||||
return(selectedObj)
|
||||
# or of nothing is selected ...
|
||||
elif Asm4.getAssembly():
|
||||
# ... but there is as assembly:
|
||||
return Asm4.getAssembly()
|
||||
# if we're here it's because we didn't find a good reason to not be here
|
||||
return None
|
||||
|
||||
|
||||
|
||||
"""
|
||||
+-----------------------------------------------+
|
||||
| the real stuff |
|
||||
+-----------------------------------------------+
|
||||
"""
|
||||
def Activated(self):
|
||||
# check that we have somewhere to put our stuff
|
||||
selectedObj = self.checkSelection()
|
||||
# default name increments the datum type's end numeral
|
||||
proposedName = Asm4.nextInstance( self.datumName, startAtOne=True )
|
||||
|
||||
parentContainer = None
|
||||
# check whether we have selected a container
|
||||
if selectedObj.TypeId in self.containers:
|
||||
parentContainer = selectedObj
|
||||
# if a datum object is selected
|
||||
elif selectedObj.TypeId in Asm4.datumTypes or selectedObj.TypeId=='Sketcher::SketchObject':
|
||||
# see whether it's in a container
|
||||
parent = selectedObj.getParentGeoFeatureGroup()
|
||||
if parent.TypeId in self.containers:
|
||||
parentContainer = parent
|
||||
# if there is an assembly
|
||||
elif Asm4.getAssembly():
|
||||
parentContainer = Asm4.getAssembly()
|
||||
# something went wrong
|
||||
else:
|
||||
Asm4.warningBox("I can't create a "+self.datumType+" with the current selections")
|
||||
|
||||
# check whether there is already a similar datum, and increment the instance number
|
||||
# instanceNum = 1
|
||||
#while App.ActiveDocument.getObject( self.datumName+'_'+str(instanceNum) ):
|
||||
# instanceNum += 1
|
||||
#datumName = self.datumName+'_'+str(instanceNum)
|
||||
if parentContainer:
|
||||
# input dialog to ask the user the name of the Sketch:
|
||||
#proposedName = Asm4.nextInstance( self.datumName + '_' + selectedObj.Label, startAtOne=True )
|
||||
text,ok = QtGui.QInputDialog.getText(None,'Create new '+self.datumName,
|
||||
'Enter '+self.datumName+' name :'+' '*40, text = proposedName)
|
||||
if ok and text:
|
||||
# App.activeDocument().getObject('Model').newObject( 'Sketcher::SketchObject', text )
|
||||
createdDatum = App.ActiveDocument.addObject( self.datumType, text )
|
||||
parentContainer.addObject( createdDatum )
|
||||
createdDatum.Label = text
|
||||
# automatic resizing of datum Plane sucks, so we set it to manual
|
||||
if self.datumType=='PartDesign::Plane':
|
||||
createdDatum.ResizeMode = 'Manual'
|
||||
createdDatum.Length = 100
|
||||
createdDatum.Width = 100
|
||||
elif self.datumType=='PartDesign::Line':
|
||||
createdDatum.ResizeMode = 'Manual'
|
||||
createdDatum.Length = 200
|
||||
# if color or transparency is specified for this datum type
|
||||
if self.datumColor:
|
||||
Gui.ActiveDocument.getObject(createdDatum.Name).ShapeColor = self.datumColor
|
||||
if self.datumAlpha:
|
||||
Gui.ActiveDocument.getObject(createdDatum.Name).Transparency = self.datumAlpha
|
||||
# highlight the created datum object
|
||||
Gui.Selection.clearSelection()
|
||||
Gui.Selection.addSelection( App.ActiveDocument.Name, parentContainer.Name, createdDatum.Name+'.' )
|
||||
Gui.runCommand('Part_EditAttachment')
|
||||
|
||||
|
||||
|
||||
"""
|
||||
+-----------------------------------------------+
|
||||
| a class to create an LCS on a hole |
|
||||
+-----------------------------------------------+
|
||||
"""
|
||||
class newHole:
|
||||
def GetResources(self):
|
||||
return {"MenuText": "New Hole Axis",
|
||||
"ToolTip": "Create a Datum Axis attached to a hole",
|
||||
"Pixmap" : os.path.join( Asm4.iconPath , 'Asm4_Hole.svg')
|
||||
}
|
||||
|
||||
def IsActive(self):
|
||||
selection = self.getSelectedEdges()
|
||||
if selection is None:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
"""
|
||||
+-----------------------------------------------+
|
||||
| the real stuff |
|
||||
+-----------------------------------------------+
|
||||
"""
|
||||
def getSelectedEdges(self):
|
||||
# check that we have selected only circular edges
|
||||
selection = None
|
||||
parent = None
|
||||
edges = []
|
||||
# 1 selection means a single parent
|
||||
if App.ActiveDocument and len(Gui.Selection.getSelection()) == 1:
|
||||
parent = Gui.Selection.getSelection()[0]
|
||||
# parse all sub-elemets of the selection
|
||||
for i in range(len(Gui.Selection.getSelectionEx()[0].SubObjects)):
|
||||
edgeObj = Gui.Selection.getSelectionEx()[0].SubObjects[i]
|
||||
edgeName = Gui.Selection.getSelectionEx()[0].SubElementNames[i]
|
||||
# if the edge is circular
|
||||
if Asm4.isCircle(edgeObj):
|
||||
edges.append( [edgeObj,edgeName] )
|
||||
# if we found circular edges
|
||||
if len(edges) > 0:
|
||||
selection = ( parent, edges )
|
||||
return selection
|
||||
|
||||
|
||||
def Activated(self):
|
||||
( selectedObj, edges ) = self.getSelectedEdges()
|
||||
for i in range(len(edges)):
|
||||
edgeObj = edges[i][0]
|
||||
edgeName = edges[i][1]
|
||||
parentPart = selectedObj.getParentGeoFeatureGroup()
|
||||
# we can create a datum only in a container
|
||||
if parentPart:
|
||||
parentDoc = parentPart.Document
|
||||
# if the solid having the edge is indeed in an App::Part
|
||||
if parentPart and (parentPart.TypeId=='App::Part' or parentPart.TypeId=='PartDesign::Body'):
|
||||
# check whether there is already a similar datum, and increment the instance number
|
||||
instanceNum = 1
|
||||
while parentDoc.getObject( 'HoleAxis_'+str(instanceNum) ):
|
||||
instanceNum += 1
|
||||
axis = parentPart.newObject('PartDesign::Line','HoleAxis_'+str(instanceNum))
|
||||
axis.Support = [( selectedObj, (edgeName,) )]
|
||||
axis.MapMode = 'AxisOfCurvature'
|
||||
axis.MapReversed = False
|
||||
axis.ResizeMode = 'Manual'
|
||||
axis.Length = edgeObj.BoundBox.DiagonalLength
|
||||
axis.ViewObject.ShapeColor = (0.0,0.0,1.0)
|
||||
axis.ViewObject.Transparency = 50
|
||||
axis.recompute()
|
||||
parentPart.recompute()
|
||||
#
|
||||
else:
|
||||
FCC.PrintMessage('Datum objects can only be created inside Part or Body containers')
|
||||
|
||||
|
||||
|
||||
"""
|
||||
+-----------------------------------------------+
|
||||
| add the commands to the workbench |
|
||||
+-----------------------------------------------+
|
||||
"""
|
||||
Gui.addCommand( 'Asm4_newPoint', newDatum('Point') )
|
||||
Gui.addCommand( 'Asm4_newAxis', newDatum('Axis') )
|
||||
Gui.addCommand( 'Asm4_newPlane', newDatum('Plane') )
|
||||
Gui.addCommand( 'Asm4_newLCS', newDatum('LCS') )
|
||||
Gui.addCommand( 'Asm4_newSketch',newDatum('Sketch'))
|
||||
Gui.addCommand( 'Asm4_newHole', newHole() )
|
||||
|
||||
# defines the drop-down button for Datum objects
|
||||
createDatumList = [ 'Asm4_newLCS',
|
||||
'Asm4_newPlane',
|
||||
'Asm4_newAxis',
|
||||
'Asm4_newPoint',
|
||||
'Asm4_newHole' ]
|
||||
Gui.addCommand( 'Asm4_createDatum', Asm4.dropDownCmd( createDatumList, 'Create Datum Object'))
|
110
cg/freecad/Frames/BoMList.py
Normal file
110
cg/freecad/Frames/BoMList.py
Normal file
|
@ -0,0 +1,110 @@
|
|||
from helper.is_solid import is_object_solid
|
||||
import FreeCAD as App
|
||||
import Spreadsheet
|
||||
|
||||
|
||||
def createSpreadsheet():
|
||||
|
||||
if App.ActiveDocument.getObject("BoM_List") == None:
|
||||
|
||||
sheet = App.activeDocument().addObject('Spreadsheet::Sheet', 'BoM_List')
|
||||
sheet.set('A1', 'п.п.')
|
||||
sheet.set('B1', 'Наименование детали')
|
||||
sheet.set('C1', 'Количество')
|
||||
else:
|
||||
|
||||
sheet = App.ActiveDocument.getObject("BoM_List")
|
||||
App.ActiveDocument.BoM_List.clear('A1:ZZ16384')
|
||||
sheet.set('A1', 'п.п.')
|
||||
sheet.set('B1', 'Наименование детали')
|
||||
sheet.set('C1', 'Количество')
|
||||
|
||||
return (sheet)
|
||||
|
||||
|
||||
class SolidBodiesParcer:
|
||||
_asmThere = []
|
||||
|
||||
def __init__(self) -> None:
|
||||
if (self._asmThere.__len__() == 0):
|
||||
|
||||
self.initParse()
|
||||
pass
|
||||
|
||||
def initParse(self):
|
||||
for el in App.ActiveDocument.RootObjects:
|
||||
if (is_object_solid(el) and hasattr(el, 'Group')):
|
||||
self.getSubPartsLink(el.Group, el.Label)
|
||||
|
||||
def getSubPartsLink(self, group, label):
|
||||
groupLink = {label: []}
|
||||
for el in group:
|
||||
if (is_object_solid(el)):
|
||||
groupLink[label].append(
|
||||
{'label': el.Label, 'isGroup': hasattr(el, 'Group'), 'solid': el})
|
||||
|
||||
for el in groupLink[label]:
|
||||
if ('isGroup' in el):
|
||||
if (el['isGroup'] == False):
|
||||
self._asmThere.append(el['solid'].Label)
|
||||
if (el['isGroup']):
|
||||
self.getSubPartsLink(el['solid'].Group, el['label']),
|
||||
|
||||
return groupLink
|
||||
|
||||
|
||||
def uniquePartsSort(labelParts):
|
||||
|
||||
uniquePartsLabels = {}
|
||||
|
||||
for el in labelParts:
|
||||
for k in labelParts:
|
||||
if (App.ActiveDocument.getObjectsByLabel(str(el))[0].Shape.isPartner(App.ActiveDocument.getObjectsByLabel(str(k))[0].Shape)):
|
||||
|
||||
if uniquePartsLabels.get(el) == None:
|
||||
uniquePartsLabels[el] = k
|
||||
|
||||
sortedParts = {}
|
||||
|
||||
for k, v in uniquePartsLabels.items():
|
||||
|
||||
if sortedParts.get(v) == None:
|
||||
sortedParts[v] = [k]
|
||||
else:
|
||||
sortedParts[v].append(k)
|
||||
|
||||
return sortedParts
|
||||
|
||||
|
||||
def countForUniques(sortedParts):
|
||||
countedParts = {}
|
||||
for k in sortedParts:
|
||||
countedParts[k] = len(sortedParts[k])
|
||||
return countedParts
|
||||
|
||||
|
||||
def fillInBoMList(sheet, countedParts):
|
||||
|
||||
a = 1
|
||||
|
||||
for label, count in countedParts.items():
|
||||
a += 1
|
||||
b = label
|
||||
c = count
|
||||
sheet.set('A' + str(a), str(a-1))
|
||||
sheet.set('B' + str(a), str(b))
|
||||
sheet.set('C' + str(a), str(c))
|
||||
|
||||
total_count = sum(countedParts.values())
|
||||
sheet.set('B'+str(a+1), 'Итого')
|
||||
|
||||
sheet.set('C' + str(a+1), str(total_count))
|
||||
|
||||
|
||||
def run_BoM_list():
|
||||
createSpreadsheet()
|
||||
sheet = App.ActiveDocument.getObject("BoM_List")
|
||||
labelParts = SolidBodiesParcer()._asmThere
|
||||
sortedParts = uniquePartsSort(labelParts)
|
||||
countedParts = countForUniques(sortedParts)
|
||||
fillInBoMList(sheet, countedParts)
|
49
cg/freecad/Frames/DatumCommand.py
Normal file
49
cg/freecad/Frames/DatumCommand.py
Normal file
|
@ -0,0 +1,49 @@
|
|||
import FreeCAD
|
||||
import FreeCADGui
|
||||
from PySide import QtGui, QtCore
|
||||
|
||||
class DatumTool:
|
||||
"""
|
||||
A tool for creating datums in existing models
|
||||
"""
|
||||
def __init__(self):
|
||||
self.active = False
|
||||
|
||||
def activate(self):
|
||||
self.active = True
|
||||
FreeCAD.Console.PrintMessage("Datum tool activatedn")
|
||||
|
||||
def deactivate(self):
|
||||
self.active = False
|
||||
FreeCAD.Console.PrintMessage("Datum tool deactivatedn")
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
if self.active:
|
||||
# Create a datum at the position of the mouse click
|
||||
pos = FreeCADGui.ActiveDocument.ActiveView.getCursorPos()
|
||||
point = FreeCADGui.ActiveDocument.ActiveView.getPoint(pos)
|
||||
datum = FreeCAD.ActiveDocument.addObject("Part::Datum", "Datum")
|
||||
datum.Placement.Base = point
|
||||
datum.ViewObject.ShapeColor = (0.0, 1.0, 0.0) # Set the color of the datum to green
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
class DatumCommand:
|
||||
"""
|
||||
A command for activating and deactivating the datum tool
|
||||
"""
|
||||
def __init__(self):
|
||||
self.tool = DatumTool()
|
||||
|
||||
def Activated(self):
|
||||
self.tool.activate()
|
||||
FreeCADGui.ActiveDocument.ActiveView.addEventCallback("SoMouseButtonEvent", self.tool.mousePressEvent)
|
||||
|
||||
def Deactivated(self):
|
||||
self.tool.deactivate()
|
||||
FreeCADGui.ActiveDocument.ActiveView.removeEventCallback("SoMouseButtonEvent", self.tool.mousePressEvent)
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap': 'path/to/icon.png', 'MenuText': 'Datum Tool', 'ToolTip': 'Creates datum elements in existing models'}
|
||||
|
||||
# Add the command to the Draft Workbench
|
||||
FreeCADGui.addCommand('DatumCommand', DatumCommand())
|
|
@ -11,9 +11,11 @@
|
|||
# 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/>.
|
||||
from BoMList import run_BoM_list
|
||||
import FreeCAD
|
||||
import Tools
|
||||
from scenarios.robossembler_freecad_export_scenario import RobossemblerFreeCadExportScenario
|
||||
from usecases.asm4parser_usecase import Asm4StructureParseUseCase
|
||||
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
import FreeCADGui
|
||||
|
@ -297,6 +299,13 @@ def makeAllPartFrames():
|
|||
def spawnFeatureFrameCreator():
|
||||
ffpanel = FeatureFramePanel()
|
||||
FreeCADGui.Control.showDialog(ffpanel)
|
||||
|
||||
def BoMGeneration(part):
|
||||
|
||||
|
||||
obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython",
|
||||
"FeatureFrame")
|
||||
print(obj)
|
||||
|
||||
|
||||
###################################################################
|
||||
|
@ -306,6 +315,12 @@ uidir = os.path.join(FreeCAD.getUserAppDataDir(),
|
|||
"Mod", __workbenchname__, "UI")
|
||||
icondir = os.path.join(uidir, "icons")
|
||||
|
||||
|
||||
Tools.spawnClassCommand("BoMGeneration",
|
||||
run_BoM_list,
|
||||
{"Pixmap": str(os.path.join(icondir, "BoMList.svg")),
|
||||
"MenuText": "Generate Bill of Materials",
|
||||
"ToolTip": "Press the button to create big BoM"})
|
||||
Tools.spawnClassCommand("FrameCommand",
|
||||
makeFrame,
|
||||
{"Pixmap": str(os.path.join(icondir, "frame.svg")),
|
||||
|
|
105
cg/freecad/Frames/ImportExportEntities.py
Normal file
105
cg/freecad/Frames/ImportExportEntities.py
Normal file
|
@ -0,0 +1,105 @@
|
|||
import os
|
||||
import json
|
||||
import FreeCAD
|
||||
import Tools
|
||||
|
||||
def export_coordinate_systems():
|
||||
# Получение активного документа FreeCAD
|
||||
doc = FreeCAD.ActiveDocument
|
||||
if not doc:
|
||||
raise ValueError("Нет активного документа FreeCAD.")
|
||||
|
||||
# Получение имени активного документа
|
||||
doc_name = doc.Name
|
||||
|
||||
|
||||
# Получение пути к папке и имя файла активного документа
|
||||
folder_path, file_name = os.path.split(doc.FileName)
|
||||
|
||||
# Создание папки для экспорта, если она не существует
|
||||
output_folder = os.path.join(folder_path, 'entities')
|
||||
if not os.path.exists(output_folder):
|
||||
os.makedirs(output_folder)
|
||||
|
||||
|
||||
|
||||
# Создание копии активного документа
|
||||
# надо сохраняться, а не копироваться
|
||||
|
||||
doc.save()
|
||||
|
||||
|
||||
|
||||
# Получение списка объектов документа
|
||||
objects = doc.Objects
|
||||
|
||||
# Обход объектов для сохранения локальных систем координат
|
||||
for obj in objects:
|
||||
if obj.TypeId == 'PartDesign::CoordinateSystem':
|
||||
|
||||
partprops = Tools.getLocalPartProps(obj)
|
||||
|
||||
|
||||
output_file_path = os.path.join(output_folder, f"{obj.Label}.json")
|
||||
with open(output_file_path, "w", encoding="utf8") as propfile:
|
||||
json.dump(partprops, propfile, indent=1, separators=(',', ': '))
|
||||
|
||||
|
||||
print("Экспорт обьектов завершен.")
|
||||
|
||||
export_coordinate_systems()
|
||||
|
||||
|
||||
#работает
|
||||
|
||||
|
||||
# def import_coordinate_systems():
|
||||
# # Получение активного документа FreeCAD
|
||||
# doc = FreeCAD.ActiveDocument
|
||||
# if not doc:
|
||||
# raise ValueError("Нет активного документа FreeCAD.")
|
||||
|
||||
# # Получение имени активного документа
|
||||
# doc_name = doc.Name
|
||||
|
||||
# # Получение пути к папке активного документа
|
||||
# folder_path, _ = os.path.split(doc.FileName)
|
||||
|
||||
# # Получение пути к папке с файлами JSON
|
||||
# json_folder_path = os.path.join(folder_path, doc_name)
|
||||
|
||||
# # Проверка существования папки с файлами JSON
|
||||
# if not os.path.exists(json_folder_path):
|
||||
# raise ValueError(f"Папка {json_folder_path} не существует.")
|
||||
|
||||
# # Получение списка файлов JSON в папке
|
||||
# json_files = [f for f in os.listdir(json_folder_path) if f.endswith(".json")]
|
||||
|
||||
# # Обход файлов JSON для создания локальных систем координат
|
||||
# for json_file in json_files:
|
||||
# json_file_path = os.path.join(json_folder_path, json_file)
|
||||
# with open(json_file_path, "r") as file:
|
||||
# json_data = json.load(file)
|
||||
|
||||
# # Извлечение информации о локальной системе координат из файла JSON
|
||||
# name = json_data.get("Name")
|
||||
# x = json_data.get("X")
|
||||
# y = json_data.get("Y")
|
||||
# z = json_data.get("Z")
|
||||
|
||||
# # Создание локальной системы координат
|
||||
# placement = FreeCAD.Placement()
|
||||
# placement.Base = FreeCAD.Vector(x, y, z)
|
||||
# part = doc.addObject("Part::Feature", name)
|
||||
# part.Placement = placement
|
||||
|
||||
# # Добавление дополнительных свойств из файла JSON
|
||||
# for key, value in json_data.items():
|
||||
# if key not in ["Name", "X", "Y", "Z"]:
|
||||
# setattr(part, key, value)
|
||||
|
||||
# print("Импорт локальных систем координат завершен.")
|
||||
|
||||
|
||||
# #import_coordinate_systems()
|
||||
|
|
@ -36,10 +36,13 @@ class Frames(Workbench):
|
|||
"""This function is executed when FreeCAD starts"""
|
||||
import Frames
|
||||
self.framecommands = [
|
||||
"BoMGeneration",
|
||||
"FrameCommand",
|
||||
|
||||
"SelectedPartFrameCommand",
|
||||
"AllPartFramesCommand",
|
||||
"FeatureFrameCommand"
|
||||
"FeatureFrameCommand",
|
||||
|
||||
]
|
||||
self.toolcommands = [
|
||||
"ExportPlacementAndPropertiesCommand",
|
||||
|
|
0
cg/freecad/Frames/MetaObj.py
Normal file
0
cg/freecad/Frames/MetaObj.py
Normal file
60
cg/freecad/Frames/Sheet_addition_test.py
Normal file
60
cg/freecad/Frames/Sheet_addition_test.py
Normal file
|
@ -0,0 +1,60 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Sheet_addition_test.py
|
||||
#
|
||||
# Copyright 2015 Ulrich Brammer <ulrich@Pauline>
|
||||
#
|
||||
# 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 2 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.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
# MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
theDoc = App.newDocument("SheetTest")
|
||||
App.setActiveDocument("SheetTest")
|
||||
#App.activeDocument("SheetTest")
|
||||
|
||||
|
||||
p=App.ParamGet("User parameter:BaseApp/Preferences/General")
|
||||
thePath = p.GetString('FileOpenSavePath')
|
||||
|
||||
|
||||
mySheet = theDoc.addObject('Spreadsheet::Sheet','Spreadsheet')
|
||||
|
||||
mySheet.set('A1', '1')
|
||||
mySheet.set('A2', '2')
|
||||
theDoc.recompute()
|
||||
mySheet.set('A3', '=A1+A2')
|
||||
mySheet.setPosition('A4')
|
||||
#theDoc.saveAs("/home/ulrich/FreeCAD/Spreadsheet/Sheet_4.fcstd")
|
||||
theDoc.saveAs(thePath + '/Sheet_5.fcstd')
|
||||
mySheet.set('A4', '=A3')
|
||||
theDoc.recompute()
|
||||
|
||||
if mySheet.State == ['Invalid']:
|
||||
print "Invalid Spreadsheet"
|
||||
else:
|
||||
print "No error found"
|
||||
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -184,11 +184,23 @@ def getLocalPartProps(obj):
|
|||
"label": obj.Label,
|
||||
"parent_label": obj_parent_label,
|
||||
"placement": placement2pose(old_placement),
|
||||
# "grip properties": { "grip open": obj.GripOpen, "grip depth": obj.GripDepth, "grip width": obj.GripWidth}
|
||||
# "boundingbox": boundingBox2list(obj.Shape.BoundBox),
|
||||
# "volume": obj.Shape.Volume*1e-9,
|
||||
# "centerofmass": vector2list(obj.Shape.CenterOfMass),
|
||||
# "principalproperties": principalProperties2dict(obj.Shape.PrincipalProperties)
|
||||
}
|
||||
|
||||
if obj.Type == 'grip':
|
||||
partprops["grip properties"] = { "grip open": obj.GripOpen, "grip depth": obj.GripDepth, "grip width": obj.GripWidth}
|
||||
|
||||
elif obj.Type == 'area':
|
||||
partprops["area dim"] = { } #свойства, описывающие размер
|
||||
|
||||
elif obj.Type == 'vol':
|
||||
partprops["vol dim"] = { } #свойства, описывающие размер области
|
||||
|
||||
|
||||
# obj.Placement = old_placement
|
||||
return partprops
|
||||
|
||||
|
|
72
cg/freecad/Frames/UI/icons/BoMList.svg
Normal file
72
cg/freecad/Frames/UI/icons/BoMList.svg
Normal file
|
@ -0,0 +1,72 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="32px" height="32px" id="svg3085" version="1.1" inkscape:version="0.92.4 (5da689c313, 2019-01-14)" sodipodi:docname="a2p_PartsList.svg">
|
||||
<defs id="defs3087">
|
||||
<inkscape:path-effect effect="skeletal" id="path-effect3066" is_visible="true" pattern="M 0,0 0,10 10,5 z" copytype="single_stretched" prop_scale="1" scale_y_rel="false" spacing="0" normal_offset="0" tang_offset="0" prop_units="false" vertical_pattern="false" fuse_tolerance="0"/>
|
||||
<linearGradient inkscape:collect="always" id="linearGradient7597">
|
||||
<stop style="stop-color:#000003;stop-opacity:1;" offset="0" id="stop7599"/>
|
||||
<stop style="stop-color:#000003;stop-opacity:0;" offset="1" id="stop7601"/>
|
||||
</linearGradient>
|
||||
<marker inkscape:stockid="Arrow1Lstart" orient="auto" refY="0.0" refX="0.0" id="Arrow1Lstart" style="overflow:visible">
|
||||
<path id="path3995" d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z " style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt" transform="scale(0.8) translate(12.5,0)"/>
|
||||
</marker>
|
||||
<marker inkscape:stockid="Arrow1Send" orient="auto" refY="0.0" refX="0.0" id="Arrow1Send" style="overflow:visible;">
|
||||
<path id="path4010" d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z " style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;" transform="scale(0.2) rotate(180) translate(6,0)"/>
|
||||
</marker>
|
||||
<marker inkscape:stockid="Arrow1Sstart" orient="auto" refY="0.0" refX="0.0" id="Arrow1Sstart" style="overflow:visible">
|
||||
<path id="path4007" d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z " style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt" transform="scale(0.2) translate(6,0)"/>
|
||||
</marker>
|
||||
<marker inkscape:stockid="Arrow2Sstart" orient="auto" refY="0.0" refX="0.0" id="Arrow2Sstart" style="overflow:visible">
|
||||
<path id="path4025" style="fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round" d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z " transform="scale(0.3) translate(-2.3,0)"/>
|
||||
</marker>
|
||||
<marker inkscape:stockid="TriangleOutL" orient="auto" refY="0.0" refX="0.0" id="TriangleOutL" style="overflow:visible">
|
||||
<path id="path4137" d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z " style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt" transform="scale(0.8)"/>
|
||||
</marker>
|
||||
<marker inkscape:stockid="DiamondLend" orient="auto" refY="0.0" refX="0.0" id="DiamondLend" style="overflow:visible">
|
||||
<path id="path4092" d="M 0,-7.0710768 L -7.0710894,0 L 0,7.0710589 L 7.0710462,0 L 0,-7.0710768 z " style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt" transform="scale(0.8) translate(-7,0)"/>
|
||||
</marker>
|
||||
<marker inkscape:stockid="DiamondLstart" orient="auto" refY="0.0" refX="0.0" id="DiamondLstart" style="overflow:visible">
|
||||
<path id="path4083" d="M 0,-7.0710768 L -7.0710894,0 L 0,7.0710589 L 7.0710462,0 L 0,-7.0710768 z " style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt" transform="scale(0.8) translate(7,0)"/>
|
||||
</marker>
|
||||
<marker inkscape:stockid="Arrow1Lend" orient="auto" refY="0.0" refX="0.0" id="Arrow1Lend" style="overflow:visible;">
|
||||
<path id="path3998" d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z " style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;" transform="scale(0.8) rotate(180) translate(12.5,0)"/>
|
||||
</marker>
|
||||
<linearGradient id="linearGradient3893">
|
||||
<stop style="stop-color:#ffffff;stop-opacity:1;" offset="0" id="stop3895"/>
|
||||
<stop style="stop-color:#ffffff;stop-opacity:0;" offset="1" id="stop3897"/>
|
||||
</linearGradient>
|
||||
<linearGradient inkscape:collect="always" xlink:href="#linearGradient7597" id="linearGradient7603" x1="15.71482" y1="22.299378" x2="34.136209" y2="22.299378" gradientUnits="userSpaceOnUse"/>
|
||||
</defs>
|
||||
<sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="16" inkscape:cx="17.629635" inkscape:cy="8.4343604" inkscape:current-layer="layer1" showgrid="true" inkscape:grid-bbox="true" inkscape:document-units="px" inkscape:window-width="1920" inkscape:window-height="1018" inkscape:window-x="-8" inkscape:window-y="-8" inkscape:window-maximized="1"/>
|
||||
<metadata id="metadata3090">
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||
<dc:title/>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g id="layer1" inkscape:label="Layer 1" inkscape:groupmode="layer">
|
||||
<rect style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.11415751000000000;stroke-linecap:butt;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" id="rect3018" width="1.2970899" height="32.02499" x="0.058561768" y="0.011311022"/>
|
||||
<rect style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.11415751000000000;stroke-linecap:butt;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" id="rect3018-5" width="1.2970899" height="32.02499" x="30.72147" y="0.021291591"/>
|
||||
<rect style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.11415751;stroke-linecap:butt;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" id="rect3018-3" width="1.2970899" height="32.02499" x="-0.026408914" y="-32.054981" transform="matrix(0,1,-1,0,0,0)"/>
|
||||
<rect style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.11415751;stroke-linecap:butt;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" id="rect3018-3-6" width="1.2970899" height="32.02499" x="11.354873" y="-31.978708" transform="matrix(0,1,-1,0,0,0)"/>
|
||||
<rect style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.11415751;stroke-linecap:butt;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" id="rect3018-3-6-9" width="1.2970899" height="32.02499" x="22.061981" y="-31.934517" transform="matrix(0,1,-1,0,0,0)"/>
|
||||
<rect style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.11415751;stroke-linecap:butt;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" id="rect3018-3-6-9-2" width="1.2970899" height="32.02499" x="30.636494" y="-31.934515" transform="matrix(0,1,-1,0,0,0)"/>
|
||||
<g id="g4617">
|
||||
<path sodipodi:nodetypes="ccccccccc" inkscape:connector-curvature="0" id="path3369" d="M 1.4229313,27.000257 V 23.442626 H 16.029106 30.63528 v 3.557631 3.557631 H 16.029106 1.4229313 Z" style="fill:#ffffff;fill-opacity:1"/>
|
||||
<text id="text3100" y="27.713165" x="11.433594" style="font-style:normal;font-weight:bold;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none" xml:space="preserve"><tspan style="font-size:8px;line-height:1.25;font-family:sans-serif" y="27.713165" x="11.433594" id="tspan3102" sodipodi:role="line">...</tspan></text>
|
||||
</g>
|
||||
<path style="fill:#ffffff;fill-opacity:1" d="m 6.2842904,5.3724349 0,-0.8007207 0.5237353,0 c 0.5882447,0 0.7620882,0.041783 0.9637654,0.2316391 0.2499922,0.2353392 0.2907934,0.7148274 0.086406,1.0154223 C 7.6895473,6.0668098 7.4826438,6.1358185 6.848381,6.1555803 l -0.5640906,0.017575 0,-0.8007208 z" id="path3375" inkscape:connector-curvature="0"/>
|
||||
<path style="fill:#ffffff;fill-opacity:1" d="M 11.823275,8.4123074 C 11.48911,8.3155617 11.339256,7.8685764 11.550817,7.5996196 11.705969,7.4023768 11.881121,7.3559471 12.470055,7.3559471 l 0.53175,0 -2.48e-4,0.1436311 c -7.16e-4,0.4175365 -0.294436,0.8141335 -0.673669,0.9096255 -0.234672,0.059091 -0.309638,0.059552 -0.504614,0.0031 l 1e-6,0 z" id="path3377" inkscape:connector-curvature="0"/>
|
||||
<g id="g3754">
|
||||
<path inkscape:connector-curvature="0" id="path3373" d="M 1.4229313,6.317384 V 1.345539 H 16.029106 30.63528 v 4.971845 4.971845 H 16.029106 1.4229313 Z" style="fill:#ffffff;fill-opacity:1" sodipodi:nodetypes="ccccccccc"/>
|
||||
<text id="text3013" y="9.2841949" x="4.0273438" style="font-style:normal;font-weight:bold;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none" xml:space="preserve"><tspan style="font-size:8px;line-height:1.25;font-family:sans-serif" y="9.2841949" x="4.0273438" id="tspan3015" sodipodi:role="line">Parts</tspan></text>
|
||||
</g>
|
||||
<g id="g4612">
|
||||
<path sodipodi:nodetypes="ccccccccc" inkscape:connector-curvature="0" id="path3371" d="M 1.4229313,17.365927 V 12.747636 H 16.029106 30.63528 v 4.618291 4.618292 H 16.029106 1.4229313 Z" style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-opacity:1"/>
|
||||
<text id="text3017" y="20.221827" x="7.5078125" style="font-style:normal;font-weight:bold;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none" xml:space="preserve"><tspan style="font-size:8px;line-height:1.25;font-family:sans-serif" y="20.221827" x="7.5078125" id="tspan3019" sodipodi:role="line">List</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 9.4 KiB |
240
cg/freecad/Frames/UI/icons/auxDatum.svg
Normal file
240
cg/freecad/Frames/UI/icons/auxDatum.svg
Normal file
|
@ -0,0 +1,240 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="64px"
|
||||
height="64px"
|
||||
id="svg5821"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="1.0 (1.0+r73+1)"
|
||||
sodipodi:docname="Asm4_Datum.svg"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"
|
||||
version="1.1"
|
||||
inkscape:export-filename="/home/normand/Images/Icônes/PartDesign_Std_Plane_nc1.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90">
|
||||
<defs
|
||||
id="defs5823">
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient6349">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop6351" />
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop6353" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3377">
|
||||
<stop
|
||||
style="stop-color:#0019a3;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3379" />
|
||||
<stop
|
||||
style="stop-color:#0069ff;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3381" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3377"
|
||||
id="linearGradient3383"
|
||||
x1="901.1875"
|
||||
y1="1190.875"
|
||||
x2="1267.9062"
|
||||
y2="1190.875"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(-1,0,0,1,2199.356,0)" />
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 32 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="64 : 32 : 1"
|
||||
inkscape:persp3d-origin="32 : 21.333333 : 1"
|
||||
id="perspective5829" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6349"
|
||||
id="radialGradient6355"
|
||||
cx="1103.6399"
|
||||
cy="1424.4465"
|
||||
fx="1103.6399"
|
||||
fy="1424.4465"
|
||||
r="194.40614"
|
||||
gradientTransform="matrix(-1.4307499,-1.3605156e-7,-1.202713e-8,0.1264801,2674.7488,1244.2826)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3776"
|
||||
id="linearGradient3782"
|
||||
x1="33.052631"
|
||||
y1="73.676765"
|
||||
x2="23.483253"
|
||||
y2="19.131313"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient3776">
|
||||
<stop
|
||||
style="stop-color:#cc0000;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3778" />
|
||||
<stop
|
||||
style="stop-color:#ef2929;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3780" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="8.0773852"
|
||||
inkscape:cx="50.403398"
|
||||
inkscape:cy="46.554586"
|
||||
inkscape:current-layer="g3360-3"
|
||||
showgrid="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1048"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="32"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:snap-bbox="false"
|
||||
inkscape:snap-nodes="true"
|
||||
inkscape:snap-global="false"
|
||||
inkscape:document-rotation="0">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid2997"
|
||||
empspacing="2"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata5826">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>[wmayer]</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:title>Sketcher_Sketch</dc:title>
|
||||
<dc:date>2011-10-10</dc:date>
|
||||
<dc:relation>http://www.freecadweb.org/wiki/index.php?title=Artwork</dc:relation>
|
||||
<dc:publisher>
|
||||
<cc:Agent>
|
||||
<dc:title>FreeCAD</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
<dc:identifier>FreeCAD/src/Mod/Sketcher/Gui/Resources/icons/Sketcher_Sketch.svg</dc:identifier>
|
||||
<dc:rights>
|
||||
<cc:Agent>
|
||||
<dc:title>FreeCAD LGPL2+</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:rights>
|
||||
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
|
||||
<dc:contributor>
|
||||
<cc:Agent>
|
||||
<dc:title>[agryson] Alexander Gryson</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:contributor>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer">
|
||||
<g
|
||||
id="g3360"
|
||||
inkscape:export-filename="/home/yorik/Documents/Lab/Draft/icons/draft.png"
|
||||
inkscape:export-xdpi="3.2478156"
|
||||
inkscape:export-ydpi="3.2478156"
|
||||
transform="matrix(0.1367863,0,0,0.1367863,-119.15519,-134.86962)">
|
||||
<g
|
||||
id="g3360-3"
|
||||
inkscape:export-filename="/home/yorik/Documents/Lab/Draft/icons/draft.png"
|
||||
inkscape:export-xdpi="3.2478156"
|
||||
inkscape:export-ydpi="3.2478156"
|
||||
transform="translate(-2.96179e-7,2.9614273e-6)">
|
||||
<g
|
||||
transform="matrix(0.86585328,0,0,1.0022331,158.36626,-5.6554462)"
|
||||
id="g860">
|
||||
<rect
|
||||
transform="matrix(0.86410236,0.50331612,-0.8660254,0.5,0,0)"
|
||||
y="479.03436"
|
||||
x="1745.9357"
|
||||
height="266.99686"
|
||||
width="266.52176"
|
||||
id="rect3860-36-6"
|
||||
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;stroke:#c1b7bb;stroke-width:36.8974;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" />
|
||||
<rect
|
||||
ry="2.5860298"
|
||||
transform="matrix(0.8660254,0.5,-0.8660254,0.5,0,0)"
|
||||
y="487.53268"
|
||||
x="1749.7632"
|
||||
height="265.90598"
|
||||
width="267.47815"
|
||||
id="rect3860-3-1"
|
||||
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#999999;fill-opacity:1;stroke:#d6d6d6;stroke-width:15.7117;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" />
|
||||
<g
|
||||
transform="matrix(1,0,0,-1,15.589571,2441.7923)"
|
||||
id="g4172">
|
||||
<rect
|
||||
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#999999;fill-opacity:0.5;stroke:#403c3d;stroke-width:36.8936;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
|
||||
id="rect3860-36"
|
||||
width="266.52176"
|
||||
height="266.99686"
|
||||
x="1138.8126"
|
||||
y="1717.2489"
|
||||
transform="matrix(0.86793573,-0.49667653,0,1,0,0)" />
|
||||
<rect
|
||||
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;fill-opacity:1;stroke:#897e7e;stroke-width:15.7117;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
|
||||
id="rect3860-3"
|
||||
width="267.47815"
|
||||
height="265.90598"
|
||||
x="1141.3009"
|
||||
y="1723.0597"
|
||||
transform="matrix(0.8660254,-0.5,0,1,0,0)"
|
||||
ry="2.5860298" />
|
||||
</g>
|
||||
<g
|
||||
transform="translate(14.62135,61.744554)"
|
||||
id="g4162">
|
||||
<rect
|
||||
style="fill:#b90720;fill-opacity:1;stroke:#172a04;stroke-width:13.677;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect4154"
|
||||
width="289.10248"
|
||||
height="22.203979"
|
||||
x="1130.2279"
|
||||
y="528.82843"
|
||||
ry="10.96601"
|
||||
rx="0"
|
||||
transform="matrix(0.8660254,0.5,0,1,0,0)" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 8.6 KiB |
9697
cg/freecad/Frames/axis.gcode
Normal file
9697
cg/freecad/Frames/axis.gcode
Normal file
File diff suppressed because it is too large
Load diff
0
cg/freecad/Frames/box.py
Normal file
0
cg/freecad/Frames/box.py
Normal file
31
cg/freecad/Frames/file.py
Normal file
31
cg/freecad/Frames/file.py
Normal file
|
@ -0,0 +1,31 @@
|
|||
|
||||
import os
|
||||
import subprocess
|
||||
import time
|
||||
import subprocess
|
||||
# import FreeCAD
|
||||
# import Mesh
|
||||
|
||||
|
||||
import gcoder
|
||||
|
||||
|
||||
# gcode_path = '/home/mark-voltov/Winder/axis.gcode'
|
||||
|
||||
# gcode.main(gcode)
|
||||
|
||||
|
||||
|
||||
file_path = '/home/mark-voltov/Winder/axis.gcode'
|
||||
gcoder_path = '/home/mark-voltov/GitProjects/framework/cg/freecad/Frames/gcoder.py'
|
||||
|
||||
|
||||
with open(file_path, 'r') as file:
|
||||
gcode = file.readlines()
|
||||
|
||||
# print(gcode)
|
||||
|
||||
(gcoder.LightGCode(gcode))
|
||||
cmd = [file_path gcoder_path]
|
||||
subprocess.run(cmd)
|
||||
# subprocess.run( '/home/mark-voltov/Winder/axis.gcode', '/home/mark-voltov/GitProjects/framework/cg/freecad/Frames/gcoder.py')
|
60
cg/freecad/Frames/freecad2pddl.py
Normal file
60
cg/freecad/Frames/freecad2pddl.py
Normal file
|
@ -0,0 +1,60 @@
|
|||
# import FreeCAD as App
|
||||
|
||||
|
||||
# doc = App.ActiveDocument
|
||||
|
||||
|
||||
#генерируем pddl
|
||||
#импортируем кучу обьектов из дерева построения freecad
|
||||
|
||||
#задаем шаблон на действие
|
||||
|
||||
predicates = ';; Предикаты \n(:predicates \n(at ?c - component ?location - component) \n(printed ?c - component) \n(has-material ?m - material) \n(compatible ?m - material ?c - component))'
|
||||
|
||||
# print(predicates)
|
||||
|
||||
|
||||
predicates =
|
||||
|
||||
|
||||
(" ;; Предикаты \
|
||||
'(:predicates (at ?c - component ?location - component)' \
|
||||
'(printed ?c - component)' \
|
||||
'(printed ?c - component))'
|
||||
|
||||
|
||||
print(predicates)
|
||||
|
||||
# (:predicates
|
||||
# (at ?c - component ?location - component)
|
||||
# (printed ?c - component)
|
||||
# (has-material ?m - material)
|
||||
# (compatible ?m - material ?c - component))''
|
||||
|
||||
|
||||
|
||||
# ;; Предикаты
|
||||
# (:predicates
|
||||
# (at ?c - component ?location - component)
|
||||
# (printed ?c - component)
|
||||
# (has-material ?m - material)
|
||||
# (compatible ?m - material ?c - component))
|
||||
|
||||
# ;; Действия
|
||||
# (:action load-material
|
||||
# :parameters (?m - material ?c - component)
|
||||
# :precondition (and (at ?m ?c) (compatible ?m ?c))
|
||||
# :effect (has-material ?m))
|
||||
|
||||
# (:action unload-material
|
||||
# :parameters (?m - material ?c - component)
|
||||
# :precondition (has-material ?m)
|
||||
# :effect (and (not (has-material ?m)) (at ?m ?c)))
|
||||
|
||||
# (:action print-component
|
||||
# :parameters (?c - component)
|
||||
# :precondition (and (at ?c ?printer) (has-material ?m) (compatible ?m ?c))
|
||||
# :effect (printed ?c))
|
||||
|
||||
# ;; Цель
|
||||
# (:goal (forall (?c - component) (printed ?c))))
|
769
cg/freecad/Frames/gcoder.py
Normal file
769
cg/freecad/Frames/gcoder.py
Normal file
|
@ -0,0 +1,769 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# This file is part of the Printrun suite.
|
||||
#
|
||||
# Printrun 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.
|
||||
#
|
||||
# Printrun 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.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# # along with Printrun. If not, see <http://www.gnu.org/licenses/>.
|
||||
# Добавлен файл программы, проводящей анализ файла .gcode и на основе полученных данных вычисляющая материальные и временные затраты на 3д-печать.
|
||||
# Программа вычисляет габариты задания, длину затрачиваемой нити филамента, длительность маршрута движения головки принтера и длительность печати.
|
||||
# Эксперименты показывают, что на данный момент есть расхождение между реальным временем и вычисленным с помощью программы,0 в зависимости от файла, на 20-40% (недооценка вычисленной длительности в сравнении с реальной). Остальные результаты выглядят адекватными, но проверить их сложнее.
|
||||
|
||||
|
||||
|
||||
import sys
|
||||
import re
|
||||
import math
|
||||
import datetime
|
||||
import logging
|
||||
from array import array
|
||||
|
||||
gcode_parsed_args = ["x", "y", "e", "f", "z", "i", "j"]
|
||||
gcode_parsed_nonargs = 'gtmnd'
|
||||
to_parse = "".join(gcode_parsed_args) + gcode_parsed_nonargs
|
||||
gcode_exp = re.compile("\([^\(\)]*\)|;.*|[/\*].*\n|([%s])\s*([-+]?[0-9]*\.?[0-9]*)" % to_parse)
|
||||
gcode_strip_comment_exp = re.compile("\([^\(\)]*\)|;.*|[/\*].*\n")
|
||||
m114_exp = re.compile("\([^\(\)]*\)|[/\*].*\n|([XYZ]):?([-+]?[0-9]*\.?[0-9]*)")
|
||||
specific_exp = "(?:\([^\(\)]*\))|(?:;.*)|(?:[/\*].*\n)|(%s[-+]?[0-9]*\.?[0-9]*)"
|
||||
move_gcodes = ["G0", "G1", "G2", "G3"]
|
||||
|
||||
class PyLine:
|
||||
|
||||
__slots__ = ('x', 'y', 'z', 'e', 'f', 'i', 'j',
|
||||
'raw', 'command', 'is_move',
|
||||
'relative', 'relative_e',
|
||||
'current_x', 'current_y', 'current_z', 'extruding',
|
||||
'current_tool',
|
||||
'gcview_end_vertex')
|
||||
|
||||
def __init__(self, l):
|
||||
self.raw = l
|
||||
|
||||
def __getattr__(self, name):
|
||||
return None
|
||||
|
||||
class PyLightLine:
|
||||
|
||||
__slots__ = ('raw', 'command')
|
||||
|
||||
def __init__(self, l):
|
||||
self.raw = l
|
||||
|
||||
def __getattr__(self, name):
|
||||
return None
|
||||
|
||||
try:
|
||||
from . import gcoder_line
|
||||
Line = gcoder_line.GLine
|
||||
LightLine = gcoder_line.GLightLine
|
||||
except Exception as e:
|
||||
logging.warning("Memory-efficient GCoder implementation unavailable: %s" % e)
|
||||
Line = PyLine
|
||||
LightLine = PyLightLine
|
||||
|
||||
def find_specific_code(line, code):
|
||||
exp = specific_exp % code
|
||||
bits = [bit for bit in re.findall(exp, line.raw) if bit]
|
||||
if not bits: return None
|
||||
else: return float(bits[0][1:])
|
||||
|
||||
def S(line):
|
||||
return find_specific_code(line, "S")
|
||||
|
||||
def P(line):
|
||||
return find_specific_code(line, "P")
|
||||
|
||||
def split(line):
|
||||
split_raw = gcode_exp.findall(line.raw.lower())
|
||||
if split_raw and split_raw[0][0] == "n":
|
||||
del split_raw[0]
|
||||
if not split_raw:
|
||||
line.command = line.raw
|
||||
line.is_move = False
|
||||
logging.warning("raw G-Code line \"%s\" could not be parsed" % line.raw)
|
||||
return [line.raw]
|
||||
command = split_raw[0]
|
||||
line.command = command[0].upper() + command[1]
|
||||
line.is_move = line.command in move_gcodes
|
||||
return split_raw
|
||||
|
||||
def parse_coordinates(line, split_raw, imperial = False, force = False):
|
||||
# Not a G-line, we don't want to parse its arguments
|
||||
if not force and line.command[0] != "G":
|
||||
return
|
||||
unit_factor = 25.4 if imperial else 1
|
||||
for bit in split_raw:
|
||||
code = bit[0]
|
||||
if code not in gcode_parsed_nonargs and bit[1]:
|
||||
setattr(line, code, unit_factor * float(bit[1]))
|
||||
|
||||
class Layer(list):
|
||||
|
||||
__slots__ = ("duration", "z")
|
||||
|
||||
def __init__(self, lines, z = None):
|
||||
super(Layer, self).__init__(lines)
|
||||
self.z = z
|
||||
self.duration = 0
|
||||
|
||||
class GCode:
|
||||
|
||||
line_class = Line
|
||||
|
||||
lines = None
|
||||
layers = None
|
||||
all_layers = None
|
||||
layer_idxs = None
|
||||
line_idxs = None
|
||||
append_layer = None
|
||||
append_layer_id = None
|
||||
|
||||
imperial = False
|
||||
cutting = False
|
||||
relative = False
|
||||
relative_e = False
|
||||
current_tool = 0
|
||||
# Home position: current absolute position counted from machine origin
|
||||
home_x = 0
|
||||
home_y = 0
|
||||
home_z = 0
|
||||
# Current position: current absolute position counted from machine origin
|
||||
current_x = 0
|
||||
current_y = 0
|
||||
current_z = 0
|
||||
# For E this is the absolute position from machine start
|
||||
current_e = 0
|
||||
current_e_multi=[0]
|
||||
total_e = 0
|
||||
total_e_multi=[0]
|
||||
max_e = 0
|
||||
max_e_multi=[0]
|
||||
# Current feedrate
|
||||
current_f = 0
|
||||
# Offset: current offset between the machine origin and the machine current
|
||||
# absolute coordinate system (as shifted by G92s)
|
||||
offset_x = 0
|
||||
offset_y = 0
|
||||
offset_z = 0
|
||||
offset_e = 0
|
||||
offset_e_multi = [0]
|
||||
|
||||
# Expected behavior:
|
||||
# - G28 X => X axis is homed, offset_x <- 0, current_x <- home_x
|
||||
# - G92 Xk => X axis does not move, so current_x does not change
|
||||
# and offset_x <- current_x - k,
|
||||
# - absolute G1 Xk => X axis moves, current_x <- offset_x + k
|
||||
# How to get...
|
||||
# current abs X from machine origin: current_x
|
||||
# current abs X in machine current coordinate system: current_x - offset_x
|
||||
|
||||
filament_length = None
|
||||
filament_length_multi=[0]
|
||||
duration = None
|
||||
xmin = None
|
||||
xmax = None
|
||||
ymin = None
|
||||
ymax = None
|
||||
zmin = None
|
||||
zmax = None
|
||||
width = None
|
||||
depth = None
|
||||
height = None
|
||||
|
||||
est_layer_height = None
|
||||
|
||||
# abs_x is the current absolute X in machine current coordinate system
|
||||
# (after the various G92 transformations) and can be used to store the
|
||||
# absolute position of the head at a given time
|
||||
def _get_abs_x(self):
|
||||
return self.current_x - self.offset_x
|
||||
abs_x = property(_get_abs_x)
|
||||
|
||||
def _get_abs_y(self):
|
||||
return self.current_y - self.offset_y
|
||||
abs_y = property(_get_abs_y)
|
||||
|
||||
def _get_abs_z(self):
|
||||
return self.current_z - self.offset_z
|
||||
abs_z = property(_get_abs_z)
|
||||
|
||||
def _get_abs_e(self):
|
||||
return self.current_e - self.offset_e
|
||||
abs_e = property(_get_abs_e)
|
||||
|
||||
def _get_abs_e_multi(self,i):
|
||||
return self.current_e_multi[i] - self.offset_e_multi[i]
|
||||
abs_e = property(_get_abs_e)
|
||||
|
||||
def _get_abs_pos(self):
|
||||
return (self.abs_x, self.abs_y, self.abs_z)
|
||||
abs_pos = property(_get_abs_pos)
|
||||
|
||||
def _get_current_pos(self):
|
||||
return (self.current_x, self.current_y, self.current_z)
|
||||
current_pos = property(_get_current_pos)
|
||||
|
||||
def _get_home_pos(self):
|
||||
return (self.home_x, self.home_y, self.home_z)
|
||||
|
||||
def _set_home_pos(self, home_pos):
|
||||
if home_pos:
|
||||
self.home_x, self.home_y, self.home_z = home_pos
|
||||
home_pos = property(_get_home_pos, _set_home_pos)
|
||||
|
||||
def _get_layers_count(self):
|
||||
return len(self.all_zs)
|
||||
layers_count = property(_get_layers_count)
|
||||
|
||||
def __init__(self, data = None, home_pos = None,
|
||||
layer_callback = None, deferred = False,
|
||||
cutting_as_extrusion = False):
|
||||
self.cutting_as_extrusion = cutting_as_extrusion
|
||||
if not deferred:
|
||||
self.prepare(data, home_pos, layer_callback)
|
||||
|
||||
def prepare(self, data = None, home_pos = None, layer_callback = None):
|
||||
self.home_pos = home_pos
|
||||
if data:
|
||||
line_class = self.line_class
|
||||
self.lines = [line_class(l2) for l2 in
|
||||
(l.strip() for l in data)
|
||||
if l2]
|
||||
self._preprocess(build_layers = True,
|
||||
layer_callback = layer_callback)
|
||||
else:
|
||||
self.lines = []
|
||||
self.append_layer_id = 0
|
||||
self.append_layer = Layer([])
|
||||
self.all_layers = [self.append_layer]
|
||||
self.all_zs = set()
|
||||
self.layers = {}
|
||||
self.layer_idxs = array('I', [])
|
||||
self.line_idxs = array('I', [])
|
||||
|
||||
def has_index(self, i):
|
||||
return i < len(self)
|
||||
def __len__(self):
|
||||
return len(self.line_idxs)
|
||||
|
||||
def __iter__(self):
|
||||
return self.lines.__iter__()
|
||||
|
||||
def prepend_to_layer(self, commands, layer_idx):
|
||||
# Prepend commands in reverse order
|
||||
commands = [c.strip() for c in commands[::-1] if c.strip()]
|
||||
layer = self.all_layers[layer_idx]
|
||||
# Find start index to append lines
|
||||
# and end index to append new indices
|
||||
start_index = self.layer_idxs.index(layer_idx)
|
||||
for i in range(start_index, len(self.layer_idxs)):
|
||||
if self.layer_idxs[i] != layer_idx:
|
||||
end_index = i
|
||||
break
|
||||
else:
|
||||
end_index = i + 1
|
||||
end_line = self.line_idxs[end_index - 1]
|
||||
for i, command in enumerate(commands):
|
||||
gline = Line(command)
|
||||
# Split to get command
|
||||
split(gline)
|
||||
# Force is_move to False
|
||||
gline.is_move = False
|
||||
# Insert gline at beginning of layer
|
||||
layer.insert(0, gline)
|
||||
# Insert gline at beginning of list
|
||||
self.lines.insert(start_index, gline)
|
||||
# Update indices arrays & global gcodes list
|
||||
self.layer_idxs.insert(end_index + i, layer_idx)
|
||||
self.line_idxs.insert(end_index + i, end_line + i + 1)
|
||||
return commands[::-1]
|
||||
|
||||
def rewrite_layer(self, commands, layer_idx):
|
||||
# Prepend commands in reverse order
|
||||
commands = [c.strip() for c in commands[::-1] if c.strip()]
|
||||
layer = self.all_layers[layer_idx]
|
||||
# Find start index to append lines
|
||||
# and end index to append new indices
|
||||
start_index = self.layer_idxs.index(layer_idx)
|
||||
for i in range(start_index, len(self.layer_idxs)):
|
||||
if self.layer_idxs[i] != layer_idx:
|
||||
end_index = i
|
||||
break
|
||||
else:
|
||||
end_index = i + 1
|
||||
self.layer_idxs = self.layer_idxs[:start_index] + array('I', len(commands) * [layer_idx]) + self.layer_idxs[end_index:]
|
||||
self.line_idxs = self.line_idxs[:start_index] + array('I', range(len(commands))) + self.line_idxs[end_index:]
|
||||
del self.lines[start_index:end_index]
|
||||
del layer[:]
|
||||
for i, command in enumerate(commands):
|
||||
gline = Line(command)
|
||||
# Split to get command
|
||||
split(gline)
|
||||
# Force is_move to False
|
||||
gline.is_move = False
|
||||
# Insert gline at beginning of layer
|
||||
layer.insert(0, gline)
|
||||
# Insert gline at beginning of list
|
||||
self.lines.insert(start_index, gline)
|
||||
return commands[::-1]
|
||||
|
||||
def append(self, command, store = True):
|
||||
command = command.strip()
|
||||
if not command:
|
||||
return
|
||||
gline = Line(command)
|
||||
self._preprocess([gline])
|
||||
if store:
|
||||
self.lines.append(gline)
|
||||
self.append_layer.append(gline)
|
||||
self.layer_idxs.append(self.append_layer_id)
|
||||
self.line_idxs.append(len(self.append_layer)-1)
|
||||
return gline
|
||||
|
||||
def _preprocess(self, lines = None, build_layers = False,
|
||||
layer_callback = None):
|
||||
"""Checks for imperial/relativeness settings and tool changes"""
|
||||
if not lines:
|
||||
lines = self.lines
|
||||
imperial = self.imperial
|
||||
relative = self.relative
|
||||
relative_e = self.relative_e
|
||||
current_tool = self.current_tool
|
||||
current_x = self.current_x
|
||||
current_y = self.current_y
|
||||
current_z = self.current_z
|
||||
offset_x = self.offset_x
|
||||
offset_y = self.offset_y
|
||||
offset_z = self.offset_z
|
||||
|
||||
# Extrusion computation
|
||||
current_e = self.current_e
|
||||
offset_e = self.offset_e
|
||||
total_e = self.total_e
|
||||
max_e = self.max_e
|
||||
cutting = self.cutting
|
||||
|
||||
current_e_multi = self.current_e_multi[current_tool]
|
||||
offset_e_multi = self.offset_e_multi[current_tool]
|
||||
total_e_multi = self.total_e_multi[current_tool]
|
||||
max_e_multi = self.max_e_multi[current_tool]
|
||||
|
||||
# Store this one out of the build_layers scope for efficiency
|
||||
cur_layer_has_extrusion = False
|
||||
|
||||
# Initialize layers and other global computations
|
||||
if build_layers:
|
||||
# Bounding box computation
|
||||
xmin = float("inf")
|
||||
ymin = float("inf")
|
||||
zmin = 0
|
||||
xmax = float("-inf")
|
||||
ymax = float("-inf")
|
||||
zmax = float("-inf")
|
||||
# Also compute extrusion-only values
|
||||
xmin_e = float("inf")
|
||||
ymin_e = float("inf")
|
||||
xmax_e = float("-inf")
|
||||
ymax_e = float("-inf")
|
||||
|
||||
# Duration estimation
|
||||
# TODO:
|
||||
# get device caps from firmware: max speed, acceleration/axis
|
||||
# (including extruder)
|
||||
# calculate the maximum move duration accounting for above ;)
|
||||
lastx = lasty = lastz = None
|
||||
laste = lastf = 0
|
||||
lastdx = 0
|
||||
lastdy = 0
|
||||
x = y = e = f = 0.0
|
||||
currenttravel = 0.0
|
||||
moveduration = 0.0
|
||||
totalduration = 0.0
|
||||
acceleration = 2000.0 # mm/s^2
|
||||
layerbeginduration = 0.0
|
||||
|
||||
# Initialize layers
|
||||
all_layers = self.all_layers = []
|
||||
all_zs = self.all_zs = set()
|
||||
layer_idxs = self.layer_idxs = []
|
||||
line_idxs = self.line_idxs = []
|
||||
|
||||
|
||||
last_layer_z = None
|
||||
prev_z = None
|
||||
cur_z = None
|
||||
cur_lines = []
|
||||
|
||||
def append_lines(lines, isEnd):
|
||||
if not build_layers:
|
||||
return
|
||||
nonlocal layerbeginduration, last_layer_z
|
||||
if cur_layer_has_extrusion and prev_z != last_layer_z \
|
||||
or not all_layers:
|
||||
layer = Layer([], prev_z)
|
||||
last_layer_z = prev_z
|
||||
finished_layer = len(all_layers)-1 if all_layers else None
|
||||
all_layers.append(layer)
|
||||
all_zs.add(prev_z)
|
||||
else:
|
||||
layer = all_layers[-1]
|
||||
finished_layer = None
|
||||
layer_id = len(all_layers)-1
|
||||
layer_line = len(layer)
|
||||
for i, ln in enumerate(lines):
|
||||
layer.append(ln)
|
||||
layer_idxs.append(layer_id)
|
||||
line_idxs.append(layer_line+i)
|
||||
layer.duration += totalduration - layerbeginduration
|
||||
layerbeginduration = totalduration
|
||||
if layer_callback:
|
||||
# we finish a layer when inserting the next
|
||||
if finished_layer is not None:
|
||||
layer_callback(self, finished_layer)
|
||||
# notify about end layer, there will not be next
|
||||
if isEnd:
|
||||
layer_callback(self, layer_id)
|
||||
|
||||
if self.line_class != Line:
|
||||
get_line = lambda l: Line(l.raw)
|
||||
else:
|
||||
get_line = lambda l: l
|
||||
for true_line in lines:
|
||||
# # Parse line
|
||||
# Use a heavy copy of the light line to preprocess
|
||||
line = get_line(true_line)
|
||||
split_raw = split(line)
|
||||
if line.command:
|
||||
# Update properties
|
||||
if line.is_move:
|
||||
line.relative = relative
|
||||
line.relative_e = relative_e
|
||||
line.current_tool = current_tool
|
||||
elif line.command == "G20":
|
||||
imperial = True
|
||||
elif line.command == "G21":
|
||||
imperial = False
|
||||
elif line.command == "G90":
|
||||
relative = False
|
||||
relative_e = False
|
||||
elif line.command == "G91":
|
||||
relative = True
|
||||
relative_e = True
|
||||
elif line.command == "M82":
|
||||
relative_e = False
|
||||
elif line.command == "M83":
|
||||
relative_e = True
|
||||
elif line.command[0] == "T":
|
||||
try:
|
||||
current_tool = int(line.command[1:])
|
||||
except:
|
||||
pass #handle T? by treating it as no tool change
|
||||
while current_tool+1 > len(self.current_e_multi):
|
||||
self.current_e_multi+=[0]
|
||||
self.offset_e_multi+=[0]
|
||||
self.total_e_multi+=[0]
|
||||
self.max_e_multi+=[0]
|
||||
elif line.command == "M3" or line.command == "M4":
|
||||
cutting = True
|
||||
elif line.command == "M5":
|
||||
cutting = False
|
||||
|
||||
current_e_multi = self.current_e_multi[current_tool]
|
||||
offset_e_multi = self.offset_e_multi[current_tool]
|
||||
total_e_multi = self.total_e_multi[current_tool]
|
||||
max_e_multi = self.max_e_multi[current_tool]
|
||||
|
||||
|
||||
if line.command[0] == "G":
|
||||
parse_coordinates(line, split_raw, imperial)
|
||||
|
||||
# Compute current position
|
||||
if line.is_move:
|
||||
x = line.x
|
||||
y = line.y
|
||||
z = line.z
|
||||
|
||||
if line.f is not None:
|
||||
self.current_f = line.f
|
||||
|
||||
if line.relative:
|
||||
x = current_x + (x or 0)
|
||||
y = current_y + (y or 0)
|
||||
z = current_z + (z or 0)
|
||||
else:
|
||||
if x is not None: x = x + offset_x
|
||||
if y is not None: y = y + offset_y
|
||||
if z is not None: z = z + offset_z
|
||||
|
||||
if x is not None: current_x = x
|
||||
if y is not None: current_y = y
|
||||
if z is not None: current_z = z
|
||||
|
||||
elif line.command == "G28":
|
||||
home_all = not any([line.x, line.y, line.z])
|
||||
if home_all or line.x is not None:
|
||||
offset_x = 0
|
||||
current_x = self.home_x
|
||||
if home_all or line.y is not None:
|
||||
offset_y = 0
|
||||
current_y = self.home_y
|
||||
if home_all or line.z is not None:
|
||||
offset_z = 0
|
||||
current_z = self.home_z
|
||||
|
||||
elif line.command == "G92":
|
||||
if line.x is not None: offset_x = current_x - line.x
|
||||
if line.y is not None: offset_y = current_y - line.y
|
||||
if line.z is not None: offset_z = current_z - line.z
|
||||
|
||||
line.current_x = current_x
|
||||
line.current_y = current_y
|
||||
line.current_z = current_z
|
||||
|
||||
# # Process extrusion
|
||||
if line.e is not None:
|
||||
if line.is_move:
|
||||
if line.relative_e:
|
||||
line.extruding = line.e > 0
|
||||
total_e += line.e
|
||||
current_e += line.e
|
||||
total_e_multi += line.e
|
||||
current_e_multi += line.e
|
||||
else:
|
||||
new_e = line.e + offset_e
|
||||
line.extruding = new_e > current_e
|
||||
total_e += new_e - current_e
|
||||
current_e = new_e
|
||||
new_e_multi = line.e + offset_e_multi
|
||||
total_e_multi += new_e_multi - current_e_multi
|
||||
current_e_multi = new_e_multi
|
||||
|
||||
max_e = max(max_e, total_e)
|
||||
max_e_multi=max(max_e_multi, total_e_multi)
|
||||
cur_layer_has_extrusion |= line.extruding and (line.x is not None or line.y is not None)
|
||||
elif line.command == "G92":
|
||||
offset_e = current_e - line.e
|
||||
offset_e_multi = current_e_multi - line.e
|
||||
if cutting and self.cutting_as_extrusion:
|
||||
line.extruding = True
|
||||
|
||||
self.current_e_multi[current_tool]=current_e_multi
|
||||
self.offset_e_multi[current_tool]=offset_e_multi
|
||||
self.max_e_multi[current_tool]=max_e_multi
|
||||
self.total_e_multi[current_tool]=total_e_multi
|
||||
|
||||
# # Create layers and perform global computations
|
||||
if build_layers:
|
||||
# Update bounding box
|
||||
if line.is_move:
|
||||
if line.extruding:
|
||||
if line.current_x is not None:
|
||||
# G0 X10 ; G1 X20 E5 results in 10..20 even as G0 is not extruding
|
||||
xmin_e = min(xmin_e, line.current_x, xmin_e if lastx is None else lastx)
|
||||
xmax_e = max(xmax_e, line.current_x, xmax_e if lastx is None else lastx)
|
||||
if line.current_y is not None:
|
||||
ymin_e = min(ymin_e, line.current_y, ymin_e if lasty is None else lasty)
|
||||
ymax_e = max(ymax_e, line.current_y, ymax_e if lasty is None else lasty)
|
||||
if max_e <= 0:
|
||||
if line.current_x is not None:
|
||||
xmin = min(xmin, line.current_x)
|
||||
xmax = max(xmax, line.current_x)
|
||||
if line.current_y is not None:
|
||||
ymin = min(ymin, line.current_y)
|
||||
ymax = max(ymax, line.current_y)
|
||||
|
||||
# Compute duration
|
||||
if line.command == "G0" or line.command == "G1":
|
||||
x = line.x if line.x is not None else (lastx or 0)
|
||||
y = line.y if line.y is not None else (lasty or 0)
|
||||
z = line.z if line.z is not None else (lastz or 0)
|
||||
e = line.e if line.e is not None else laste
|
||||
# mm/s vs mm/m => divide by 60
|
||||
f = line.f / 60.0 if line.f is not None else lastf
|
||||
|
||||
# given last feedrate and current feedrate calculate the
|
||||
# distance needed to achieve current feedrate.
|
||||
# if travel is longer than req'd distance, then subtract
|
||||
# distance to achieve full speed, and add the time it took
|
||||
# to get there.
|
||||
# then calculate the time taken to complete the remaining
|
||||
# distance
|
||||
|
||||
# FIXME: this code has been proven to be super wrong when 2
|
||||
# subsquent moves are in opposite directions, as requested
|
||||
# speed is constant but printer has to fully decellerate
|
||||
# and reaccelerate
|
||||
# The following code tries to fix it by forcing a full
|
||||
# reacceleration if this move is in the opposite direction
|
||||
# of the previous one
|
||||
dx = x - (lastx or 0)
|
||||
dy = y - (lasty or 0)
|
||||
if dx * lastdx + dy * lastdy <= 0:
|
||||
lastf = 0
|
||||
|
||||
currenttravel = math.hypot(dx, dy)
|
||||
if currenttravel == 0:
|
||||
if line.z is not None:
|
||||
currenttravel = abs(line.z) if line.relative else abs(line.z - (lastz or 0))
|
||||
elif line.e is not None:
|
||||
currenttravel = abs(line.e) if line.relative_e else abs(line.e - laste)
|
||||
# Feedrate hasn't changed, no acceleration/decceleration planned
|
||||
if f == lastf:
|
||||
moveduration = currenttravel / f if f != 0 else 0.
|
||||
else:
|
||||
# FIXME: review this better
|
||||
# this looks wrong : there's little chance that the feedrate we'll decelerate to is the previous feedrate
|
||||
# shouldn't we instead look at three consecutive moves ?
|
||||
distance = 2 * abs(((lastf + f) * (f - lastf) * 0.5) / acceleration) # multiply by 2 because we have to accelerate and decelerate
|
||||
if distance <= currenttravel and lastf + f != 0 and f != 0:
|
||||
moveduration = 2 * distance / (lastf + f) # This is distance / mean(lastf, f)
|
||||
moveduration += (currenttravel - distance) / f
|
||||
else:
|
||||
moveduration = 2 * currenttravel / (lastf + f) # This is currenttravel / mean(lastf, f)
|
||||
# FIXME: probably a little bit optimistic, but probably a much better estimate than the previous one:
|
||||
# moveduration = math.sqrt(2 * distance / acceleration) # probably buggy : not taking actual travel into account
|
||||
|
||||
lastdx = dx
|
||||
lastdy = dy
|
||||
|
||||
totalduration += moveduration
|
||||
|
||||
lastx = x
|
||||
lasty = y
|
||||
lastz = z
|
||||
laste = e
|
||||
lastf = f
|
||||
elif line.command == "G4":
|
||||
moveduration = P(line)
|
||||
if moveduration:
|
||||
moveduration /= 1000.0
|
||||
totalduration += moveduration
|
||||
|
||||
# FIXME : looks like this needs to be tested with "lift Z on move"
|
||||
if line.z is not None:
|
||||
if line.command == "G92":
|
||||
cur_z = line.z
|
||||
elif line.is_move:
|
||||
if line.relative and cur_z is not None:
|
||||
cur_z += line.z
|
||||
else:
|
||||
cur_z = line.z
|
||||
|
||||
if cur_z != prev_z and cur_layer_has_extrusion:
|
||||
append_lines(cur_lines, False)
|
||||
cur_lines = []
|
||||
cur_layer_has_extrusion = False
|
||||
|
||||
if build_layers:
|
||||
cur_lines.append(true_line)
|
||||
prev_z = cur_z
|
||||
# ## Loop done
|
||||
|
||||
# Store current status
|
||||
self.imperial = imperial
|
||||
self.relative = relative
|
||||
self.relative_e = relative_e
|
||||
self.current_tool = current_tool
|
||||
self.current_x = current_x
|
||||
self.current_y = current_y
|
||||
self.current_z = current_z
|
||||
self.offset_x = offset_x
|
||||
self.offset_y = offset_y
|
||||
self.offset_z = offset_z
|
||||
self.current_e = current_e
|
||||
self.offset_e = offset_e
|
||||
self.max_e = max_e
|
||||
self.total_e = total_e
|
||||
self.current_e_multi[current_tool]=current_e_multi
|
||||
self.offset_e_multi[current_tool]=offset_e_multi
|
||||
self.max_e_multi[current_tool]=max_e_multi
|
||||
self.total_e_multi[current_tool]=total_e_multi
|
||||
self.cutting = cutting
|
||||
|
||||
|
||||
# Finalize layers
|
||||
if build_layers:
|
||||
if cur_lines:
|
||||
append_lines(cur_lines, True)
|
||||
|
||||
self.append_layer_id = len(all_layers)
|
||||
self.append_layer = Layer([])
|
||||
self.append_layer.duration = 0
|
||||
all_layers.append(self.append_layer)
|
||||
self.layer_idxs = array('I', layer_idxs)
|
||||
self.line_idxs = array('I', line_idxs)
|
||||
|
||||
# Compute bounding box
|
||||
all_zs = self.all_zs.union({zmin}).difference({None})
|
||||
zmin = min(all_zs)
|
||||
zmax = max(all_zs)
|
||||
|
||||
self.filament_length = self.max_e
|
||||
while len(self.filament_length_multi)<len(self.max_e_multi):
|
||||
self.filament_length_multi+=[0]
|
||||
for i in enumerate(self.max_e_multi):
|
||||
self.filament_length_multi[i[0]]=i[1]
|
||||
|
||||
|
||||
if self.filament_length > 0:
|
||||
self.xmin = xmin_e if not math.isinf(xmin_e) else 0
|
||||
self.xmax = xmax_e if not math.isinf(xmax_e) else 0
|
||||
self.ymin = ymin_e if not math.isinf(ymin_e) else 0
|
||||
self.ymax = ymax_e if not math.isinf(ymax_e) else 0
|
||||
else:
|
||||
self.xmin = xmin if not math.isinf(xmin) else 0
|
||||
self.xmax = xmax if not math.isinf(xmax) else 0
|
||||
self.ymin = ymin if not math.isinf(ymin) else 0
|
||||
self.ymax = ymax if not math.isinf(ymax) else 0
|
||||
self.zmin = zmin if not math.isinf(zmin) else 0
|
||||
self.zmax = zmax if not math.isinf(zmax) else 0
|
||||
self.width = self.xmax - self.xmin
|
||||
self.depth = self.ymax - self.ymin
|
||||
self.height = self.zmax - self.zmin
|
||||
|
||||
# Finalize duration
|
||||
totaltime = datetime.timedelta(seconds = int(totalduration))
|
||||
self.duration = totaltime
|
||||
|
||||
def idxs(self, i):
|
||||
return self.layer_idxs[i], self.line_idxs[i]
|
||||
|
||||
def estimate_duration(self):
|
||||
return self.layers_count, self.duration
|
||||
|
||||
class LightGCode(GCode):
|
||||
line_class = LightLine
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("usage: %s filename.gcode" % sys.argv[0])
|
||||
return
|
||||
|
||||
print("Line object size:", sys.getsizeof(Line("G0 X0")))
|
||||
print("Light line object size:", sys.getsizeof(LightLine("G0 X0")))
|
||||
gcode = GCode(open(sys.argv[1], "rU"))
|
||||
|
||||
print("Dimensions:")
|
||||
xdims = (gcode.xmin, gcode.xmax, gcode.width)
|
||||
print("\tX: %0.02f - %0.02f (%0.02f)" % xdims)
|
||||
ydims = (gcode.ymin, gcode.ymax, gcode.depth)
|
||||
print("\tY: %0.02f - %0.02f (%0.02f)" % ydims)
|
||||
zdims = (gcode.zmin, gcode.zmax, gcode.height)
|
||||
print("\tZ: %0.02f - %0.02f (%0.02f)" % zdims)
|
||||
print("Filament used: %0.02fmm" % gcode.filament_length)
|
||||
for i in enumerate(gcode.filament_length_multi):
|
||||
print("E%d %0.02fmm" % (i[0],i[1]))
|
||||
print("Number of layers: %d" % gcode.layers_count)
|
||||
print("Estimated duration: %s" % gcode.estimate_duration()[1])
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
BIN
cg/freecad/Frames/img/qXX7sBMbsvA.jpg
Normal file
BIN
cg/freecad/Frames/img/qXX7sBMbsvA.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 86 KiB |
131
cg/freecad/Frames/markupEntities.py
Normal file
131
cg/freecad/Frames/markupEntities.py
Normal file
|
@ -0,0 +1,131 @@
|
|||
import FreeCAD as App
|
||||
import FreeCADGui as Gui
|
||||
|
||||
doc = App.ActiveDocument
|
||||
|
||||
|
||||
|
||||
|
||||
print('lcs - локальная система координат')
|
||||
print('grip - позиция захвата')
|
||||
print('area - плоскость')
|
||||
print('vol - зона')
|
||||
print('joint - соединение')
|
||||
entityType = input('Введите тип получаемого обьекта')
|
||||
|
||||
def create(entityType):
|
||||
|
||||
if entityType == 'grip':
|
||||
print('задайте начальную точку захватной зоны. ')
|
||||
print('Учтите, что ось X должна быть направлена вдоль направления раскрытия пальцев')
|
||||
print('Ось Z должна быть направлена в противоположную сторону от направления кончика пальца захвата')
|
||||
objname = input('Введите название начальной точки' + "\n")
|
||||
obj = doc.getObject(objname)
|
||||
|
||||
|
||||
poseGenerator(obj)
|
||||
|
||||
|
||||
|
||||
elif entityType == 'area':
|
||||
print('задайте плоскость')
|
||||
objname = input('Введите название плоскости'+ "\n")
|
||||
obj = doc.getObject(objname)
|
||||
|
||||
areaProps(obj)
|
||||
|
||||
elif entityType == 'vol':
|
||||
print('задайте обьем')
|
||||
objname = input('Введите название обьема'+ "\n")
|
||||
obj = doc.getObject(objname)
|
||||
|
||||
volProps(obj)
|
||||
|
||||
elif entityType == 'joint':
|
||||
print('Задайте позицию соединения')
|
||||
objname = input('Введите название соединения'+ "\n")
|
||||
part1 = input('укажите название первой детали'+ "\n")
|
||||
part2 = input('укажите название второй детали'+ "\n")
|
||||
|
||||
jointGenerator(objname, part1, part2)
|
||||
|
||||
obj.addProperty("App::PropertyString", "Type").Type = entityType
|
||||
|
||||
|
||||
|
||||
## заглушки функций на случай, если что-то придумаю полезное для них
|
||||
|
||||
|
||||
def areaProps(area):
|
||||
#здесь нужно отметить свойства зоны
|
||||
#в принципе, Placement и размеры тут есть, больше ничего особо не нужно
|
||||
#добавить характеристику entityType
|
||||
|
||||
print(area.Label)
|
||||
|
||||
def volProps(vol):
|
||||
#желательно указать координаты, габариты, позицию привязки
|
||||
#но думаю, что это все уже есть
|
||||
print(vol.Label)
|
||||
|
||||
|
||||
def jointGenerator(jointName, part1, part2):
|
||||
|
||||
#получаем относительные координаты для первой детали и для второй детали
|
||||
#создаем две сущности - точка входа и точка выхода (??????)
|
||||
#в работе сейчас
|
||||
print(jointName.Label)
|
||||
print(part1.Label)
|
||||
print(part2.Label)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Эта функция работает нормально
|
||||
|
||||
|
||||
def poseGenerator(lcs):
|
||||
|
||||
box = doc.addObject("Part::Box", "gripSpace")
|
||||
box.Length = 62 #раскрытие
|
||||
box.Width = 10 #ширина пальца
|
||||
box.Height = 40 #глубина
|
||||
|
||||
box.Placement = lcs.Placement
|
||||
|
||||
|
||||
#есть смысл создавать привязку прямо здесь же. благодаря параметризации, при подстройке куба все точки сместятся как надо
|
||||
gripPose = App.ActiveDocument.addObject('PartDesign::CoordinateSystem', 'GripPose')
|
||||
gripPose.Support = box
|
||||
gripPose.positionBySupport()
|
||||
gripPose.MapMode = 'ObjectXY'
|
||||
gripPose.AttachmentOffset.Base = [str(box.Label) + '.Length' + '/2', str(box.Label) + '.Length'+ '/2', 0] #здесь должна быть активная привязка, не просто значения координат
|
||||
|
||||
gripPose.addProperty("App::PropertyFloat", "GripOpen")
|
||||
gripPose.addProperty("App::PropertyFloat", "GripDepth")
|
||||
gripPose.addProperty("App::PropertyFloat", "GripWidth")
|
||||
gripPose.setExpression('GripDepth', str(box.Label) + '.Height')
|
||||
gripPose.setExpression('GripOpen', str(box.Label) + '.Length')
|
||||
gripPose.setExpression('GripWidth', str(box.Label) + '.Width')
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#нужно создавать внутри Part, а не внутри главного документа. сбиваются привязки !!!
|
||||
|
||||
|
||||
print('Установите захватную зону вручную, растянув обьект GripSpace')
|
||||
doc.recompute()
|
||||
|
||||
|
||||
#теперь нужно производить экспорт
|
||||
#он делается через Tools.py или через импорт-экспорт
|
||||
|
||||
|
||||
create(entityType)
|
||||
|
||||
|
259
cg/freecad/Frames/newDatumCmd.py
Normal file
259
cg/freecad/Frames/newDatumCmd.py
Normal file
|
@ -0,0 +1,259 @@
|
|||
#!/usr/bin/env python3
|
||||
# coding: utf-8
|
||||
#
|
||||
# LGPL
|
||||
# Copyright HUBERT Zoltán
|
||||
#
|
||||
# newDatumCmd.py
|
||||
|
||||
|
||||
import os
|
||||
|
||||
from PySide import QtGui, QtCore
|
||||
import FreeCADGui as Gui
|
||||
import FreeCAD as App
|
||||
from FreeCAD import Console as FCC
|
||||
|
||||
import Asm4_libs as Asm4
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
+-----------------------------------------------+
|
||||
| a class to create all Datum objects |
|
||||
+-----------------------------------------------+
|
||||
"""
|
||||
class newDatum:
|
||||
"My tool object"
|
||||
def __init__(self, datumName):
|
||||
self.datumName = datumName
|
||||
# recognised containers (not the same as Asm4.containerTypes !)
|
||||
self.containers = [ 'App::Part', 'PartDesign::Body', 'App::DocumentObjectGroup']
|
||||
if self.datumName == 'Point':
|
||||
self.datumType = 'PartDesign::Point'
|
||||
self.menutext = "New Point"
|
||||
self.tooltip = "Create a new Datum Point in a Part"
|
||||
self.icon = os.path.join( Asm4.iconPath , 'Asm4_Point.svg')
|
||||
self.datumColor = (0.00,0.00,0.00)
|
||||
self.datumAlpha = []
|
||||
elif self.datumName == 'Axis':
|
||||
self.datumType = 'PartDesign::Line'
|
||||
self.menutext = "New Axis"
|
||||
self.tooltip = "Create a new Datum Axis in a Part"
|
||||
self.icon = os.path.join( Asm4.iconPath , 'Asm4_Axis.svg')
|
||||
self.datumColor = (0.00,0.00,0.50)
|
||||
self.datumAlpha = []
|
||||
elif self.datumName == 'Plane':
|
||||
self.datumType = 'PartDesign::Plane'
|
||||
self.menutext = "New Plane"
|
||||
self.tooltip = "Create a new Datum Plane in a Part"
|
||||
self.icon = os.path.join( Asm4.iconPath , 'Asm4_Plane.svg')
|
||||
self.datumColor = (0.50,0.50,0.50)
|
||||
self.datumAlpha = 80
|
||||
elif self.datumName == 'LCS':
|
||||
self.datumType = 'PartDesign::CoordinateSystem'
|
||||
self.menutext = "New Coordinate System"
|
||||
self.tooltip = "Create a new Coordinate System in a Part"
|
||||
self.icon = os.path.join( Asm4.iconPath , 'Asm4_CoordinateSystem.svg')
|
||||
self.datumColor = []
|
||||
self.datumAlpha = []
|
||||
elif self.datumName == 'Sketch':
|
||||
self.datumType = 'Sketcher::SketchObject'
|
||||
self.menutext = "New Sketch"
|
||||
self.tooltip = "Create a new Sketch in a Part"
|
||||
self.icon = os.path.join( Asm4.iconPath , 'Asm4_Sketch.svg')
|
||||
self.datumColor = []
|
||||
self.datumAlpha = []
|
||||
|
||||
|
||||
def GetResources(self):
|
||||
return {"MenuText": self.menutext,
|
||||
"ToolTip": self.tooltip,
|
||||
"Pixmap" : self.icon }
|
||||
|
||||
|
||||
def IsActive(self):
|
||||
if App.ActiveDocument:
|
||||
# is something correct selected ?
|
||||
if self.checkSelection():
|
||||
return(True)
|
||||
return(False)
|
||||
|
||||
|
||||
def checkSelection(self):
|
||||
# if something is selected ...
|
||||
if Gui.Selection.getSelection():
|
||||
selectedObj = Gui.Selection.getSelection()[0]
|
||||
# ... and it's an App::Part or an datum object
|
||||
selType = selectedObj.TypeId
|
||||
if selType in self.containers or selType in Asm4.datumTypes or selType=='Sketcher::SketchObject':
|
||||
return(selectedObj)
|
||||
# or of nothing is selected ...
|
||||
elif Asm4.getAssembly():
|
||||
# ... but there is as assembly:
|
||||
return Asm4.getAssembly()
|
||||
# if we're here it's because we didn't find a good reason to not be here
|
||||
return None
|
||||
|
||||
|
||||
|
||||
"""
|
||||
+-----------------------------------------------+
|
||||
| the real stuff |
|
||||
+-----------------------------------------------+
|
||||
"""
|
||||
def Activated(self):
|
||||
# check that we have somewhere to put our stuff
|
||||
selectedObj = self.checkSelection()
|
||||
# default name increments the datum type's end numeral
|
||||
proposedName = Asm4.nextInstance( self.datumName, startAtOne=True )
|
||||
|
||||
parentContainer = None
|
||||
# check whether we have selected a container
|
||||
if selectedObj.TypeId in self.containers:
|
||||
parentContainer = selectedObj
|
||||
# if a datum object is selected
|
||||
elif selectedObj.TypeId in Asm4.datumTypes or selectedObj.TypeId=='Sketcher::SketchObject':
|
||||
# see whether it's in a container
|
||||
parent = selectedObj.getParentGeoFeatureGroup()
|
||||
if parent.TypeId in self.containers:
|
||||
parentContainer = parent
|
||||
# if there is an assembly
|
||||
elif Asm4.getAssembly():
|
||||
parentContainer = Asm4.getAssembly()
|
||||
# something went wrong
|
||||
else:
|
||||
Asm4.warningBox("I can't create a "+self.datumType+" with the current selections")
|
||||
|
||||
# check whether there is already a similar datum, and increment the instance number
|
||||
# instanceNum = 1
|
||||
#while App.ActiveDocument.getObject( self.datumName+'_'+str(instanceNum) ):
|
||||
# instanceNum += 1
|
||||
#datumName = self.datumName+'_'+str(instanceNum)
|
||||
if parentContainer:
|
||||
# input dialog to ask the user the name of the Sketch:
|
||||
#proposedName = Asm4.nextInstance( self.datumName + '_' + selectedObj.Label, startAtOne=True )
|
||||
text,ok = QtGui.QInputDialog.getText(None,'Create new '+self.datumName,
|
||||
'Enter '+self.datumName+' name :'+' '*40, text = proposedName)
|
||||
if ok and text:
|
||||
# App.activeDocument().getObject('Model').newObject( 'Sketcher::SketchObject', text )
|
||||
createdDatum = App.ActiveDocument.addObject( self.datumType, text )
|
||||
parentContainer.addObject( createdDatum )
|
||||
createdDatum.Label = text
|
||||
# automatic resizing of datum Plane sucks, so we set it to manual
|
||||
if self.datumType=='PartDesign::Plane':
|
||||
createdDatum.ResizeMode = 'Manual'
|
||||
createdDatum.Length = 100
|
||||
createdDatum.Width = 100
|
||||
elif self.datumType=='PartDesign::Line':
|
||||
createdDatum.ResizeMode = 'Manual'
|
||||
createdDatum.Length = 200
|
||||
# if color or transparency is specified for this datum type
|
||||
if self.datumColor:
|
||||
Gui.ActiveDocument.getObject(createdDatum.Name).ShapeColor = self.datumColor
|
||||
if self.datumAlpha:
|
||||
Gui.ActiveDocument.getObject(createdDatum.Name).Transparency = self.datumAlpha
|
||||
# highlight the created datum object
|
||||
Gui.Selection.clearSelection()
|
||||
Gui.Selection.addSelection( App.ActiveDocument.Name, parentContainer.Name, createdDatum.Name+'.' )
|
||||
Gui.runCommand('Part_EditAttachment')
|
||||
|
||||
|
||||
|
||||
"""
|
||||
+-----------------------------------------------+
|
||||
| a class to create an LCS on a hole |
|
||||
+-----------------------------------------------+
|
||||
"""
|
||||
class newHole:
|
||||
def GetResources(self):
|
||||
return {"MenuText": "New Hole Axis",
|
||||
"ToolTip": "Create a Datum Axis attached to a hole",
|
||||
"Pixmap" : os.path.join( Asm4.iconPath , 'Asm4_Hole.svg')
|
||||
}
|
||||
|
||||
def IsActive(self):
|
||||
selection = self.getSelectedEdges()
|
||||
if selection is None:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
"""
|
||||
+-----------------------------------------------+
|
||||
| the real stuff |
|
||||
+-----------------------------------------------+
|
||||
"""
|
||||
def getSelectedEdges(self):
|
||||
# check that we have selected only circular edges
|
||||
selection = None
|
||||
parent = None
|
||||
edges = []
|
||||
# 1 selection means a single parent
|
||||
if App.ActiveDocument and len(Gui.Selection.getSelection()) == 1:
|
||||
parent = Gui.Selection.getSelection()[0]
|
||||
# parse all sub-elemets of the selection
|
||||
for i in range(len(Gui.Selection.getSelectionEx()[0].SubObjects)):
|
||||
edgeObj = Gui.Selection.getSelectionEx()[0].SubObjects[i]
|
||||
edgeName = Gui.Selection.getSelectionEx()[0].SubElementNames[i]
|
||||
# if the edge is circular
|
||||
if Asm4.isCircle(edgeObj):
|
||||
edges.append( [edgeObj,edgeName] )
|
||||
# if we found circular edges
|
||||
if len(edges) > 0:
|
||||
selection = ( parent, edges )
|
||||
return selection
|
||||
|
||||
|
||||
def Activated(self):
|
||||
( selectedObj, edges ) = self.getSelectedEdges()
|
||||
for i in range(len(edges)):
|
||||
edgeObj = edges[i][0]
|
||||
edgeName = edges[i][1]
|
||||
parentPart = selectedObj.getParentGeoFeatureGroup()
|
||||
# we can create a datum only in a container
|
||||
if parentPart:
|
||||
parentDoc = parentPart.Document
|
||||
# if the solid having the edge is indeed in an App::Part
|
||||
if parentPart and (parentPart.TypeId=='App::Part' or parentPart.TypeId=='PartDesign::Body'):
|
||||
# check whether there is already a similar datum, and increment the instance number
|
||||
instanceNum = 1
|
||||
while parentDoc.getObject( 'HoleAxis_'+str(instanceNum) ):
|
||||
instanceNum += 1
|
||||
axis = parentPart.newObject('PartDesign::Line','HoleAxis_'+str(instanceNum))
|
||||
axis.Support = [( selectedObj, (edgeName,) )]
|
||||
axis.MapMode = 'AxisOfCurvature'
|
||||
axis.MapReversed = False
|
||||
axis.ResizeMode = 'Manual'
|
||||
axis.Length = edgeObj.BoundBox.DiagonalLength
|
||||
axis.ViewObject.ShapeColor = (0.0,0.0,1.0)
|
||||
axis.ViewObject.Transparency = 50
|
||||
axis.recompute()
|
||||
parentPart.recompute()
|
||||
#
|
||||
else:
|
||||
FCC.PrintMessage('Datum objects can only be created inside Part or Body containers')
|
||||
|
||||
|
||||
|
||||
"""
|
||||
+-----------------------------------------------+
|
||||
| add the commands to the workbench |
|
||||
+-----------------------------------------------+
|
||||
"""
|
||||
Gui.addCommand( 'Asm4_newPoint', newDatum('Point') )
|
||||
Gui.addCommand( 'Asm4_newAxis', newDatum('Axis') )
|
||||
Gui.addCommand( 'Asm4_newPlane', newDatum('Plane') )
|
||||
Gui.addCommand( 'Asm4_newLCS', newDatum('LCS') )
|
||||
Gui.addCommand( 'Asm4_newSketch',newDatum('Sketch'))
|
||||
Gui.addCommand( 'Asm4_newHole', newHole() )
|
||||
|
||||
# defines the drop-down button for Datum objects
|
||||
createDatumList = [ 'Asm4_newLCS',
|
||||
'Asm4_newPlane',
|
||||
'Asm4_newAxis',
|
||||
'Asm4_newPoint',
|
||||
'Asm4_newHole' ]
|
||||
Gui.addCommand( 'Asm4_createDatum', Asm4.dropDownCmd( createDatumList, 'Create Datum Object'))
|
17
cg/freecad/Frames/newLabel.py
Normal file
17
cg/freecad/Frames/newLabel.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
import FreeCAD as App
|
||||
|
||||
|
||||
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()
|
||||
|
||||
def addProperty():
|
||||
for obj in App.ActiveDocument().Objects:
|
||||
if is_object_solid(obj):
|
||||
|
0
cg/freecad/Frames/normalEstimator
Normal file
0
cg/freecad/Frames/normalEstimator
Normal file
53
cg/freecad/Frames/poseGenerator.py
Normal file
53
cg/freecad/Frames/poseGenerator.py
Normal file
|
@ -0,0 +1,53 @@
|
|||
import FreeCAD as App
|
||||
import FreeCADGui as Gui
|
||||
|
||||
# App.newDocument()
|
||||
doc = App.ActiveDocument
|
||||
|
||||
|
||||
print('задайте начальную точку захватной зоны. ')
|
||||
print('Учтите, что ось X должна быть направлена вдоль направления раскрытия пальцев')
|
||||
print('Ось Z должна быть направлена в противоположную сторону от направления кончика пальца захвата')
|
||||
lcsname = input('Введите название начальной точки' + "\n")
|
||||
|
||||
lcs = doc.getObject(lcsname)
|
||||
|
||||
def poseGenerator(lcs):
|
||||
|
||||
box = doc.addObject("Part::Box", "gripSpace")
|
||||
box.Length = 62 #раскрытие
|
||||
box.Width = 10 #ширина пальца
|
||||
box.Height = 40 #глубина
|
||||
|
||||
box.Placement = lcs.Placement
|
||||
#box.Transparency = 80 #не работает, хз почему
|
||||
|
||||
|
||||
|
||||
#есть смысл создавать привязку прямо здесь же. благодаря параметризации, при подстройке куба все точки сместятся как надо
|
||||
gripPose = App.ActiveDocument.addObject('PartDesign::CoordinateSystem', 'GripPose')
|
||||
gripPose.Support = box
|
||||
gripPose.MapMode = 'ObjectXY'
|
||||
gripPose.AttachmentOffset.Base = [box.Length/2, box.Width/2, 0] #здесь должна быть активная привязка, не просто значения координат
|
||||
|
||||
gripPose.addProperty("App::PropertyFloat", "GripOpen")
|
||||
gripPose.addProperty("App::PropertyFloat", "GripDepth")
|
||||
gripPose.addProperty("App::PropertyFloat", "GripWidth")
|
||||
gripPose.GripOpen = box.Length.Value
|
||||
gripPose.GripWidth = box.Width.Value
|
||||
gripPose.GripDepth = box.Height.Value
|
||||
|
||||
|
||||
|
||||
|
||||
#нужно создавать обьект внутри Part, а не внутри главного документа. сбиваются привязки !!!
|
||||
|
||||
|
||||
print('Установите захватную зону вручную, растянув обьект GripSpace')
|
||||
doc.recompute()
|
||||
|
||||
#вроде как работает
|
||||
|
||||
|
||||
poseGenerator(lcs)
|
||||
|
98
cg/freecad/Frames/printETA.py
Normal file
98
cg/freecad/Frames/printETA.py
Normal file
|
@ -0,0 +1,98 @@
|
|||
|
||||
import os
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
import FreeCAD
|
||||
import Mesh
|
||||
|
||||
|
||||
import gcoder
|
||||
|
||||
|
||||
def export_to_stl(doc, output_dir):
|
||||
objects = doc.Objects
|
||||
|
||||
for obj in objects:
|
||||
if isinstance(obj, Part.Feature):
|
||||
stl_path = os.path.join(output_dir, obj.Label + ".stl")
|
||||
mesh = obj.Shape.tessellate(0.1)
|
||||
|
||||
# Создаем меш из геометрии объекта
|
||||
mesh_obj = Mesh.Mesh(mesh)
|
||||
|
||||
# Проверяем, что меш содержит данные
|
||||
if len(mesh_obj.Points) > 0:
|
||||
# Экспортируем меш в формат STL
|
||||
mesh_obj.write(stl_path)
|
||||
|
||||
|
||||
def create_gcode(stl_dir, output_dir):
|
||||
stl_files = [f for f in os.listdir(stl_dir) if f.endswith(".stl")]
|
||||
|
||||
for stl_file in stl_files:
|
||||
stl_path = os.path.join(stl_dir, stl_file)
|
||||
|
||||
gcode_file = stl_file.replace(".stl", ".gcode")
|
||||
gcode_path = os.path.join(output_dir, gcode_file)
|
||||
|
||||
# Команда для запуска Slic3r (в Ubuntu используется slic3r-пакет)
|
||||
cmd = ["slic3r", "--load", "/home/mark-voltov/my_config.ini", "-o", gcode_path, stl_path]
|
||||
|
||||
# Запускаем Slic3r
|
||||
subprocess.run(cmd)
|
||||
|
||||
def get_print_duration(gcode_dir):
|
||||
|
||||
gcoder.G
|
||||
gcode_files = [f for f in os.listdir(gcode_dir) if f.endswith(".gcode")]
|
||||
|
||||
total_duration = 0
|
||||
|
||||
for gcode_file in gcode_files:
|
||||
gcode_path = os.path.join(gcode_dir, gcode_file)
|
||||
|
||||
with open(gcode_path, "r") as file:
|
||||
lines = file.readlines()
|
||||
|
||||
for line in lines:
|
||||
if line.startswith("; estimated printing time"):
|
||||
duration = float(line.split(":")[1].strip()) * 60
|
||||
total_duration += duration
|
||||
break
|
||||
|
||||
return total_duration
|
||||
|
||||
|
||||
file_location = App.ActiveDocument.FileName
|
||||
file_name = os.path.basename(file_location ) #eds_report.csv
|
||||
location = os.path.dirname(file_location )
|
||||
|
||||
|
||||
# Путь к документу FreeCAD
|
||||
doc_path = file_location
|
||||
|
||||
# Каталог для сохранения STL-файлов
|
||||
stl_dir = location + '/stl'
|
||||
|
||||
# Каталог для сохранения G-code файлов
|
||||
gcode_dir = location + '/gcode'
|
||||
|
||||
# Открываем документ FreeCAD
|
||||
doc = FreeCAD.open(doc_path)
|
||||
|
||||
# Экспортируем модель в файлы STL
|
||||
export_to_stl(doc, stl_dir)
|
||||
print("STL файлы успешно созданы.")
|
||||
|
||||
# Создаем G-code файлы с помощью Slic3r
|
||||
create_gcode(stl_dir, gcode_dir)
|
||||
print("G-code файлы успешно созданы.")
|
||||
|
||||
# Получаем оценочную длительность печати
|
||||
print_duration = get_print_duration(gcode_dir)
|
||||
print("Оценочная длительность печати: {} секунд.".format(print_duration))
|
||||
|
||||
# Закрываем документ FreeCAD
|
||||
FreeCAD.closeDocument(doc)
|
||||
|
77
cg/freecad/Frames/testSpread2.py
Normal file
77
cg/freecad/Frames/testSpread2.py
Normal file
|
@ -0,0 +1,77 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# faculSpread.py
|
||||
#
|
||||
# Copyright 2015 ulrich1a
|
||||
#
|
||||
# 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 2 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.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
# MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
|
||||
p=App.ParamGet("User parameter:BaseApp/Preferences/General")
|
||||
thePath = p.GetString('FileOpenSavePath')
|
||||
|
||||
|
||||
theDoc = App.newDocument("SheetTest2")
|
||||
App.setActiveDocument("SheetTest2")
|
||||
|
||||
mySheet = theDoc.addObject('Spreadsheet::Sheet','Spreadsheet')
|
||||
|
||||
|
||||
col = 'A'
|
||||
mySheet.setColumnWidth(col, 125)
|
||||
theDoc.recompute()
|
||||
|
||||
startRow = 3
|
||||
|
||||
for i in range(20):
|
||||
print "i: ", i
|
||||
if i == 0:
|
||||
print "setting start"
|
||||
mySheet.set(col+str(startRow+i), '1')
|
||||
#App.activeDocument().recompute()
|
||||
else:
|
||||
mySheet.set(col + str(startRow+i), '='+col+str(startRow+i-1)+'*'+str(i))
|
||||
#App.activeDocument().recompute()
|
||||
|
||||
#mySheet.show()
|
||||
#App.activeDocument().recompute()
|
||||
|
||||
mySheet.set('A1', 'This is the very long Titel, which needs more space!')
|
||||
mySheet.set('A2', '=A1')
|
||||
|
||||
# mySheet.setDisplayUnit('A10:A11', 'mm')
|
||||
|
||||
theDoc.recompute()
|
||||
mySheet.setAlias('A1', 'Title')
|
||||
mySheet.set('D1', '=Title')
|
||||
|
||||
theDoc.recompute()
|
||||
|
||||
theDoc.saveAs(thePath + '/Sheet_6.fcstd')
|
||||
mySheet.set('B4','=A4')
|
||||
mySheet.set('E1', '=D1')
|
||||
theDoc.recompute()
|
||||
mySheet.setAlias('A2', 'huhu')
|
||||
theDoc.recompute()
|
||||
|
||||
if mySheet.State == ['Invalid']:
|
||||
print "Invalid Spreadsheet"
|
||||
else:
|
||||
print "No error found"
|
53
cg/freecad/Frames/usecases/asm4parser_usecase.py
Normal file
53
cg/freecad/Frames/usecases/asm4parser_usecase.py
Normal file
|
@ -0,0 +1,53 @@
|
|||
import FreeCAD as App
|
||||
|
||||
class Asm4StructureParseUseCase:
|
||||
_parts = []
|
||||
_label = []
|
||||
|
||||
def getSubPartsLabel(self, group):
|
||||
groupLabel = []
|
||||
for el in group:
|
||||
if str(el) == '<Part::PartFeature>':
|
||||
groupLabel.append(el.Label)
|
||||
return groupLabel
|
||||
|
||||
def parseLabel(self, nextGroup, label, level=2, nextGroupParse=0):
|
||||
if nextGroup.__len__() == nextGroupParse:
|
||||
return
|
||||
else:
|
||||
groupParts = []
|
||||
|
||||
for el in nextGroup:
|
||||
if str(el) == '<App::Link object>':
|
||||
groupParts.append(el)
|
||||
|
||||
for el in groupParts:
|
||||
if str(el) == '<App::Link object>':
|
||||
label.append({
|
||||
"level": level,
|
||||
"attachedTo": el.AttachedTo.split('#'),
|
||||
"label": el.Label,
|
||||
"axis": self.getSubPartsLabel(el.Group)
|
||||
})
|
||||
|
||||
def initParse(self):
|
||||
|
||||
model = App.ActiveDocument.RootObjects[1]
|
||||
self._label.append({
|
||||
"level": 1,
|
||||
"attachedTo": "Parent Assembly",
|
||||
"label": model.Label,
|
||||
"axis": self.getSubPartsLabel(model.Group)
|
||||
})
|
||||
for parent in model.Group:
|
||||
if str(parent) == '<App::Link object>':
|
||||
self._label.append({
|
||||
"level": 1,
|
||||
"attachedTo": parent.AttachedTo.split('#'),
|
||||
"label": parent.Label,
|
||||
"axis": self.getSubPartsLabel(parent.Group)
|
||||
})
|
||||
print(self._label)
|
||||
|
||||
|
||||
|
60
cg/freecad/Frames/Модуль технологической подготовки.md
Normal file
60
cg/freecad/Frames/Модуль технологической подготовки.md
Normal file
|
@ -0,0 +1,60 @@
|
|||
|
||||
|
||||
# Модуль технологической подготовки
|
||||
|
||||
На вход: - step-модель описываемого оборудования.
|
||||
|
||||
1. Описание имеющихся сущностей:
|
||||
- Мы можем создать описание действий со станком и его состояниями для связи с окружающим миром нужен некий обобщенный объект
|
||||
его свойством будет возможность воздействовать на состояния, но сам он будет любым. Думаю, нужно описать сами рычаги, а не того, кто будет их нажимать.
|
||||
|
||||
- Описать объекты можно созданием зон вокруг них и/или созданием призрака геометрии оборудования (может быть сложно и избыточно).
|
||||
- Для этого возможно создать параллелепипед/цилиндр, сопоставимый по габаритам с рассматриваемой зоной. С помощью инструментов верстака Part создается объект, имеющий желаемое название (Станок, рабочая зона, etc). Эта зона с помощью отношений parent привязана к геометрии станка.
|
||||
|
||||
2. Описание действий
|
||||
- Создается папка actions, в которую сохраняются создаваемые действия
|
||||
- Интерфейс создания действий аналогичен интерфейсу задания других сущностей. Через него мы задаем ссылки на существующие действия и элементы и указываем их тип: триггеры, рабочие органы, конечные состояния, начальные состояния. Указываем их статус (выбор "да/нет")
|
||||
- Указываем ссылки на привязываемую к этим действиям геометрию. (катушки, статоры, расходники и тд) Для этого их геометрия должна быть импортирована в модель. Ссылка будет указывать на конкретный импортированный файл. Если существует идентификатор детали, можно ссылаться на него.
|
||||
|
||||
3. Задание состояний и переменных
|
||||
- Переменные задаются средствами параметризации FreeCAD, инструменты для этого можно взять из ASM4, используя функционал переменных (Variables) и панели конфигураций.
|
||||
- Для состояний переменных аналогично создается (в ASM4 уже имеется при создании модели) отдельная директория.
|
||||
|
||||
4. Результатом описания будет модель, имеющая дерево объектов, в свойствах которых мы имеем всю необходимую информацию. Геометрические характеристики мы сохраняем как json и отправляем в среды, работающие с геометрией и физикой. Действия и геометрия подставляются в шаблон pddl в соответствующие абзацы.
|
||||
|
||||
## Пример описания объекта
|
||||
|
||||
Action - "Заправка 3д-принтера пластиком"
|
||||
|
||||
- |- Объекты:
|
||||
- - 3d-принтер [printer_id] /прямоугольная зона по габаритам принтера. Зона привязана к геометрии оборудования
|
||||
- Workzone [printer_id] / прямоугольная зона. Указание на объект workzone, который содержит в себе габариты и позиционирование рабочей зоны относительно 3d-принтера.
|
||||
- Wirenest [printer_id] /цилиндрическая зона. Указание на объект wirenest (цилиндр), хранящий информацию об ориентации и положении гнезда для катушки с пластиком
|
||||
- Filament [filament_id] /катушка с пластиком требуемой модели, формы и габаритов.
|
||||
- Observer [observer_id] / некая сущность(манипулятор, человек, камера), к которой обращается станок, чтобы с ним провели внешние манипуляции
|
||||
- |- Длительность действия, с
|
||||
|
||||
|
||||
- |- Стартовые состояния:
|
||||
- Пластика достаточно (нет)
|
||||
- Наблюдатель свободен (да)
|
||||
- |- Во время действия:
|
||||
- Наблюдатель[observer_id] свободен (нет)
|
||||
- Катушка пластика установлена (нет)
|
||||
- |- После окончания:
|
||||
- Катушка пластика установлена (да)
|
||||
- Наблюдатель [observer_id] свободен (да)
|
||||
- Пластика достаточно (да)
|
||||
|
||||
|
||||
--В раздел Variables мы можем (должны ли?) полуавтоматически/автоматически указать подобные состояния, привязанные к значениям да/нет.-- (Указывать стартовые значения по умолчанию?)
|
||||
|
||||
|
||||
Указанные отдельно состояния пригодились бы, чтобы ссылаться на них при задавании действий, поскольку действия сообщаются между собой не напрямую, а через выполнение определенного набора состояний.
|
||||
|
||||
|
||||
|
||||
|
||||
Пример размеченной модели:
|
||||
|
||||

|
Loading…
Add table
Add a link
Reference in a new issue