393 lines
11 KiB
Plaintext
Executable file
393 lines
11 KiB
Plaintext
Executable file
import importSVG
|
|
import FreeCAD
|
|
import FreeCADGui
|
|
import Draft
|
|
import os
|
|
import Mesh
|
|
from PySide.QtWidgets import QWidget, QLabel, QSpinBox, QHBoxLayout, QListWidgetItem
|
|
from datetime import datetime
|
|
|
|
|
|
class ObjectListItem(QWidget):
|
|
def __init__(self, obj):
|
|
super().__init__()
|
|
self.obj = obj
|
|
self.label = QLabel(obj.Label)
|
|
self.spinbox = QSpinBox()
|
|
self.spinbox.setRange(1, 9999)
|
|
self.spinbox.setValue(1)
|
|
|
|
layout = QHBoxLayout()
|
|
layout.addWidget(self.label)
|
|
layout.addWidget(self.spinbox)
|
|
layout.setContentsMargins(0, 0, 0, 0)
|
|
self.setLayout(layout)
|
|
|
|
def get_value(self):
|
|
return self.spinbox.value()
|
|
|
|
def set_value(self, value):
|
|
self.spinbox.setValue(value)
|
|
|
|
|
|
class WindowDialog():
|
|
def __init__(self):
|
|
self.selectedItems = []
|
|
self.outputRacine = ""
|
|
self.ui_file = os.path.join(FreeCAD.getUserMacroDir(True), 'MultiExport/MultiExport.ui')
|
|
self.form = FreeCADGui.PySideUic.loadUi(self.ui_file)
|
|
self.connect_widgets()
|
|
self.populate_selector_list()
|
|
self.listVarSets()
|
|
|
|
# FreeCADGui.Selection.addObserver(self.on_selection_changed)
|
|
|
|
self.form.show()
|
|
|
|
|
|
def connect_widgets(self):
|
|
self.form.pushButtonExport.pressed.connect(self.export)
|
|
self.form.pushButtonSave.pressed.connect(self.saveVarSet)
|
|
self.form.pushButtonLoad.pressed.connect(self.loadVarSet)
|
|
|
|
|
|
def populate_selector_list(self):
|
|
self.form.listWidgetSelector.clear()
|
|
FreeCADGui.Selection.clearSelection()
|
|
|
|
for obj in FreeCAD.ActiveDocument.Objects:
|
|
if obj.Label.endswith("_SVG") or obj.Label.endswith("_STL"):
|
|
item = QListWidgetItem()
|
|
widget = ObjectListItem(obj)
|
|
|
|
item.setSizeHint(widget.sizeHint())
|
|
self.form.listWidgetSelector.addItem(item)
|
|
self.form.listWidgetSelector.setItemWidget(item, widget)
|
|
|
|
item.setSelected(True)
|
|
|
|
|
|
def createFolder(self):
|
|
date = datetime.today().strftime('%Y-%m-%d')
|
|
time = datetime.today().strftime('%H:%M:%S')
|
|
|
|
folderPath = f"{FreeCAD.activeDocument().getFileName().rpartition('.')[0]}-{date}-Exports/"
|
|
|
|
if not os.path.exists(folderPath):
|
|
os.makedirs(folderPath)
|
|
|
|
folderPath += f"{time}/"
|
|
|
|
if not os.path.exists(folderPath):
|
|
os.makedirs(folderPath)
|
|
|
|
return folderPath
|
|
|
|
|
|
# -------- MODIFICATION DE LA MACRO "exportSketchEnMasse_SVG" de Gauthier Brière -------- #
|
|
|
|
def exportSketchBasic(self, sketchList):
|
|
for __O__ in sketchList:
|
|
__obj__ = []
|
|
__obj__.append(__O__)
|
|
|
|
print('Export SVG de : ' + __obj__[0].Label)
|
|
|
|
fichierSVG = u"" + self.outputRacine + __obj__[0].Label + '-' + '.svg'
|
|
importSVG.export(__obj__, fichierSVG)
|
|
FreeCAD.ActiveDocument.removeObject(__O__.Label)
|
|
|
|
FreeCAD.activeDocument().recompute()
|
|
|
|
def exportSketchCalepinage(self, sketchList, plateLength, plateWidth, spacing):
|
|
copy_sketchList = sketchList[:]
|
|
__obj__ = []
|
|
currentX = 0.0
|
|
currentY = 0.0
|
|
isSVGFull = False
|
|
YBlockCounter = 0
|
|
SVGNameCounter = 1
|
|
maxY = 0.0
|
|
index = 0
|
|
listSize = len(copy_sketchList)
|
|
errorCount = 0
|
|
|
|
while listSize > 0:
|
|
while index < listSize:
|
|
__O__ = copy_sketchList[index]
|
|
boundingBox = __O__.Shape.BoundBox
|
|
if (boundingBox.XLength > plateLength) or (boundingBox.YLength > plateWidth):
|
|
copy_sketchList.pop(index)
|
|
listSize = len(copy_sketchList)
|
|
errorCount += 1
|
|
FreeCAD.ActiveDocument.removeObject(__O__.Label)
|
|
FreeCAD.activeDocument().recompute()
|
|
continue
|
|
if boundingBox.XLength + currentX > plateLength:
|
|
if boundingBox.YLength + currentY > plateWidth:
|
|
YBlockCounter += 1
|
|
if YBlockCounter >= listSize:
|
|
isSVGFull = True
|
|
break
|
|
index += 1
|
|
continue
|
|
if boundingBox.YLength + currentY > plateWidth:
|
|
YBlockCounter += 1
|
|
if YBlockCounter >= listSize:
|
|
isSVGFull = True
|
|
break
|
|
index += 1
|
|
continue
|
|
__obj__.insert(0, __O__)
|
|
maxY = boundingBox.YLength if boundingBox.YLength > maxY else maxY
|
|
__obj__[0].Placement=FreeCAD.Placement(FreeCAD.Vector(0,0,0), FreeCAD.Rotation(FreeCAD.Vector(1,0,0),0), FreeCAD.Vector(0,0,0))
|
|
|
|
if currentX == 0.0:
|
|
__obj__[0].Placement.Base = FreeCAD.Vector(-boundingBox.XMin + currentX, -boundingBox.YMin + currentY, 0.0)
|
|
currentX += boundingBox.XLength
|
|
else:
|
|
__obj__[0].Placement.Base = FreeCAD.Vector(-boundingBox.XMin + currentX + spacing, -boundingBox.YMin + currentY, 0.0)
|
|
currentX += boundingBox.XLength + spacing
|
|
|
|
copy_sketchList.pop(index)
|
|
listSize = len(copy_sketchList)
|
|
FreeCAD.activeDocument().recompute()
|
|
index = 0
|
|
YBlockCounter = 0
|
|
|
|
currentX = 0.0
|
|
currentY += maxY + spacing
|
|
maxY = 0.0
|
|
index = 0
|
|
listSize = len(copy_sketchList)
|
|
FreeCAD.activeDocument().recompute()
|
|
|
|
if listSize <= 0 or isSVGFull:
|
|
fichierSVG = u"" + self.outputRacine + 'CALEPINAGE-(' + str(SVGNameCounter) + ')' + '.svg'
|
|
print('Export SVG de : ' + u"" + str(os.path.basename(self.outputRacine)) + 'CALEPINAGE-(' + str(SVGNameCounter) + ')' + '.svg')
|
|
importSVG.export(__obj__, fichierSVG)
|
|
for __i__ in __obj__:
|
|
FreeCAD.ActiveDocument.removeObject(__i__.Label)
|
|
|
|
FreeCAD.activeDocument().recompute()
|
|
__obj__.clear()
|
|
maxY = 0.0
|
|
index = 0
|
|
currentX = 0.0
|
|
currentY = 0.0
|
|
isSVGFull = False
|
|
SVGNameCounter += 1
|
|
YBlockCounter = 0
|
|
listSize = len(copy_sketchList)
|
|
|
|
if errorCount == 1:
|
|
print(f"ERREUR : {errorCount} sketch ne rentre pas sur le plateau")
|
|
elif errorCount > 1:
|
|
print(f"ERREUR : {errorCount} sketchs ne rentrent pas sur le plateau")
|
|
|
|
# -------- END -------- #
|
|
|
|
def getLargestFaceDir(self, obj):
|
|
if not hasattr(obj, "Shape") or not obj.Shape.Faces:
|
|
return None
|
|
|
|
maxArea = 0.0
|
|
largestFace = None
|
|
|
|
for face in obj.Shape.Faces:
|
|
area = face.Area
|
|
if area > maxArea:
|
|
maxArea = area
|
|
largestFace = face
|
|
|
|
if largestFace:
|
|
u, v = largestFace.Surface.parameter(largestFace.CenterOfMass)
|
|
normal = largestFace.normalAt(u, v)
|
|
return normal.normalize()
|
|
|
|
return None
|
|
|
|
|
|
def exportSVG(self, isCalepinage, svgList):
|
|
plateLength = self.form.doubleSpinBoxLength.value()
|
|
plateWidth = self.form.doubleSpinBoxWidth.value()
|
|
spacing = self.form.doubleSpinBoxSpacing.value()
|
|
sketchList = []
|
|
|
|
for _, obj, objCount in svgList:
|
|
for _ in range(objCount):
|
|
if hasattr(obj, "Dir"):
|
|
vector = FreeCAD.Vector(obj.Dir.x, obj.Dir.y, obj.Dir.z)
|
|
else:
|
|
vector = self.getLargestFaceDir(obj)
|
|
|
|
sv0 = Draft.make_shape2dview(obj, vector)
|
|
|
|
FreeCAD.ActiveDocument.recompute()
|
|
sk = Draft.make_sketch(sv0, autoconstraints=True)
|
|
sk.ViewObject.LineColor = (1.0, 0.0, 0.0)
|
|
FreeCAD.ActiveDocument.recompute()
|
|
sketchList.append(sk)
|
|
|
|
if hasattr(sv0, 'Name'):
|
|
FreeCAD.ActiveDocument.removeObject(sv0.Name)
|
|
|
|
if isCalepinage:
|
|
self.exportSketchCalepinage(sketchList, plateLength, plateWidth, spacing)
|
|
else:
|
|
self.exportSketchBasic(sketchList)
|
|
|
|
|
|
|
|
def exportSTL(self, singleMode, stlList):
|
|
objects_to_export = []
|
|
|
|
for objLabel, obj, objCount in stlList:
|
|
for i in range(objCount):
|
|
clone = Draft.clone(obj)
|
|
objects_to_export.append((clone, objLabel, i + 1))
|
|
|
|
FreeCAD.ActiveDocument.recompute()
|
|
|
|
if not singleMode:
|
|
for obj, label, i in objects_to_export:
|
|
filename = os.path.join(self.outputRacine, f"{label}_{i}.stl")
|
|
Mesh.export([obj], filename)
|
|
print(f"Exported STL: {filename}")
|
|
FreeCAD.ActiveDocument.removeObject(obj.Name)
|
|
else:
|
|
export_objs = [obj for obj, _, _ in objects_to_export]
|
|
if export_objs:
|
|
label = FreeCAD.activeDocument().Name
|
|
filename = os.path.join(self.outputRacine, f"{label}.stl")
|
|
Mesh.export(export_objs, filename)
|
|
print(f"Exported merged STL: {filename}")
|
|
for obj in export_objs:
|
|
FreeCAD.ActiveDocument.removeObject(obj.Name)
|
|
|
|
FreeCAD.ActiveDocument.recompute()
|
|
|
|
|
|
def export(self):
|
|
isCalepinage = self.form.checkBoxCalepinage.isChecked()
|
|
isCombine = self.form.checkBoxCombine.isChecked()
|
|
|
|
svgList = []
|
|
stlList = []
|
|
|
|
for obj in FreeCAD.ActiveDocument.Objects:
|
|
count = 1
|
|
|
|
for index in range(self.form.listWidgetSelector.count()):
|
|
item = self.form.listWidgetSelector.item(index)
|
|
widget = self.form.listWidgetSelector.itemWidget(item)
|
|
|
|
if not item.isSelected():
|
|
continue
|
|
|
|
if widget.obj == obj:
|
|
count = widget.get_value()
|
|
if obj.Label.endswith("_SVG"):
|
|
svgList.append((obj.Label, obj, count))
|
|
elif obj.Label.endswith("_STL"):
|
|
stlList.append((obj.Label, obj, count))
|
|
break
|
|
|
|
if not svgList == [] or not stlList == []:
|
|
self.outputRacine = self.createFolder()
|
|
|
|
self.exportSVG(isCalepinage, svgList)
|
|
self.exportSTL(isCombine, stlList)
|
|
|
|
|
|
def saveVarSet(self):
|
|
varSetName = self.form.saveTextValue.text()
|
|
varSet = FreeCAD.ActiveDocument.addObject("App::VarSet", varSetName)
|
|
self.form.saveTextValue.setText("")
|
|
|
|
for obj in FreeCAD.ActiveDocument.Objects:
|
|
count = 1
|
|
for index in range(self.form.listWidgetSelector.count()):
|
|
item = self.form.listWidgetSelector.item(index)
|
|
widget = self.form.listWidgetSelector.itemWidget(item)
|
|
|
|
if widget.obj == obj:
|
|
count = widget.get_value()
|
|
break
|
|
|
|
if obj.Label.endswith("_SVG") or obj.Label.endswith("_STL"):
|
|
varSet.addProperty("App::PropertyStringList", obj.Label, "MultiExport")
|
|
if obj.Label.endswith("_SVG"):
|
|
setattr(varSet, obj.Label, ("SVG", str(count)))
|
|
else:
|
|
setattr(varSet, obj.Label, ("STL", str(count)))
|
|
|
|
self.listVarSets()
|
|
|
|
FreeCAD.ActiveDocument.recompute()
|
|
|
|
|
|
'''def loadVarSet(self):
|
|
# faire lien entre VarSet et liste
|
|
# refresh a la fin ?
|
|
|
|
varSetName = self.form.loadDropDown.currentText()
|
|
varSet = FreeCAD.ActiveDocument.getObject(varSetName)
|
|
for prop in varSet.PropertiesList:
|
|
if varSet.getGroupOfProperty(prop) == "MultiExport":
|
|
print(f"{prop}: {getattr(varSet, prop)}")
|
|
|
|
for index in range(self.form.listWidgetSelector.count()):
|
|
item = self.form.listWidgetSelector.item(index)
|
|
widget = self.form.listWidgetSelector.itemWidget(item)
|
|
|
|
if widget.obj == obj:
|
|
count = widget.get_value()
|
|
break'''
|
|
|
|
def loadVarSet(self):
|
|
varSetName = self.form.loadDropDown.currentText()
|
|
if not varSetName:
|
|
return
|
|
|
|
varSet = FreeCAD.ActiveDocument.getObject(varSetName)
|
|
if not varSet:
|
|
return
|
|
|
|
values_dict = {}
|
|
for prop in varSet.PropertiesList:
|
|
if varSet.getGroupOfProperty(prop) == "MultiExport":
|
|
val = getattr(varSet, prop)
|
|
if isinstance(val, tuple) or isinstance(val, list):
|
|
try:
|
|
count = int(val[1])
|
|
values_dict[prop] = count
|
|
except Exception:
|
|
pass
|
|
|
|
for index in range(self.form.listWidgetSelector.count()):
|
|
item = self.form.listWidgetSelector.item(index)
|
|
widget = self.form.listWidgetSelector.itemWidget(item)
|
|
if widget.obj.Label in values_dict:
|
|
widget.set_value(values_dict[widget.obj.Label])
|
|
|
|
|
|
|
|
def listVarSets(self):
|
|
varSetsList = [obj for obj in FreeCAD.ActiveDocument.Objects if obj.TypeId == "App::VarSet"]
|
|
|
|
self.form.loadDropDown.clear()
|
|
|
|
self.form.loadDropDown.setPlaceholderText("Select a VarSet")
|
|
|
|
for varSet in varSetsList:
|
|
self.form.loadDropDown.addItem(varSet.Label, varSet.Name)
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
try:
|
|
mainWindow = WindowDialog()
|
|
except Exception as e:
|
|
print(e)
|