Macro Unfold Box/fr: Difference between revisions

From FreeCAD Documentation
mNo edit summary
No edit summary
(9 intermediate revisions by 2 users not shown)
Line 1: Line 1:
<languages/>
<languages/>
{{Macro/fr
{{Macro/fr|Icon=Text-x-python|Name=Macro Unfold Box|Name/fr=Macro Unfold Box|Description=La macro permet de déplier les surfaces d'un objet de n'importe quelle forme et la dessiner sur une page.|Author=Hervé B.|Version=1.0|Date=2013-09-20}}
|Name=Macro Unfold Box
|Icon=Macro_Unfold_Box.png
|Description=La macro permet de déplier les surfaces d'un objet de n'importe quelle forme et la dessiner sur une page.<br/>
|Author=Hervé B.
|Version=1.0.1
|Date=2020-03-100
|FCVersion=
|Download=[https://www.freecadweb.org/wiki/images/e/e4/Macro_Unfold_Box.png ToolBar Icon]
}}


==Description==
==Description==
La macro permet de déplier les surfaces d'un objet de n'importe quelle forme et la dessiner sur une page.
La macro permet de déplier les surfaces d'un objet de n'importe quelle forme et la dessiner sur une page.



[[File:Macro_unfoldBox1.png|480px|Macro_unfoldBox]]
[[File:Macro_unfoldBox1.png|480px|Macro_unfoldBox]]
{{Caption|Macro_unfoldBox}}


== Installation ==
== Installation ==
Line 18: Line 27:
Cf [http://forum.freecadweb.org/viewtopic.php?f=17&t=4587 Macro for unfolding box surfaces]
Cf [http://forum.freecadweb.org/viewtopic.php?f=17&t=4587 Macro for unfolding box surfaces]


== Options ==
<div class="mw-translate-fuzzy">
=== Options ===


* Échelle manuelle ou automatique
* Échelle manuelle ou automatique
Line 25: Line 33:
* Groupe les dessins dans la même page si possible.
* Groupe les dessins dans la même page si possible.
* Attache ou non les bords de la pièce.
* Attache ou non les bords de la pièce.
</div>


== Utilisation ==
<div class="mw-translate-fuzzy">
=== Utilisation ===


#Sélectionnez un volume créé avec par exemple Part::Loft tool
#Sélectionnez un volume créé avec par exemple Part::Loft tool
Line 34: Line 40:
#Sélectionnez la surface
#Sélectionnez la surface
#Exécuter la macro
#Exécuter la macro
</div>


== Script ==
<div class="mw-translate-fuzzy">
=== Script ===


ToolBar icon [[Image:Macro_Unfold_Box.png]]
'''Macro_unfoldBox.py'''
</div>


'''Macro_unfoldBox.FCMacro'''
'''Macro_unfoldBox.FCMacro'''


<!-- Do not remove the pair of <nowiki> </nowiki> tags because the special symbols won't be parsed correctly -->
{{Code|code=
{{MacroCode|code=

<nowiki>
#***************************************************************************
'''FreeCAD Macro unfoldBox.
#* *
#* Copyright (c) 2013 - DoNovae/Herve BAILLY <hbl13@donovae.com> *
#* *
#* This program is free software; you can redistribute it and/or modify *
#* it under the terms of the GNU Lesser General Public License (LGPL) *
#* as published by the Free Software Foundation; either version 2 of *
#* the License, or (at your option) any later version. *
#* for detail see the LICENCE text file. *
#* *
#* 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 Library General Public License for more details. *
#* *
#* You should have received a copy of the GNU Library General Public *
#* License along with this program; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************


Unroll of a ruled surface
'''
#####################################
#####################################
# SEE https://wiki.freecadweb.org/Macro_Unfold_Box.
# Macro unfoldBox
# ***************************************************************************
# Unroll of a ruled surface
# * *
#####################################
# * Copyright (c) 2013 - DoNovae/Herve BAILLY <hbl13@donovae.com> *
import FreeCAD , FreeCADGui , Part, Draft, math, Drawing , PySide, os
# * *
from PySide import QtGui,QtCore
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU Lesser General Public License (LGPL) *
# * as published by the Free Software Foundation; either version 2 of *
# * the License, or (at your option) any later version. *
# * for detail see the LICENCE text file. *
# * *
# * 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 Library General Public License for more details. *
# * *
# * You should have received a copy of the GNU Library General Public *
# * License along with this program; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
# * USA *
# * *
# ***************************************************************************

import os
import math

import FreeCAD, FreeCADGui, Draft
from PySide import QtGui, QtCore
from FreeCAD import Base
from FreeCAD import Base
fields_l = []
fields_l = []
unroll_l = []
unroll_l = []


#####################################
#####################################
# Functions
#####################################
#####################################
# Functions
#####################################
#####################################



#####################################
# Function errorDialog
#####################################
def errorDialog(msg):
def errorDialog(msg):
diag = QtGui.QMessageBox(QtGui.QMessageBox.Critical,u"Error Message",msg )
diag = QtGui.QMessageBox(QtGui.QMessageBox.Critical, u'Error Message',msg)
diag.setWindowFlags(PySide.QtCore.Qt.WindowStaysOnTopHint)
diag.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
diag.exec_()
diag.exec_()




#####################################
# Function proceed
#####################################
def proceed():
def proceed():
QtGui.qApp.setOverrideCursor(QtCore.Qt.WaitCursor)
QtGui.qApp.setOverrideCursor(QtCore.Qt.WaitCursor)


FreeCAD.Console.PrintMessage("===========================================\n")
FreeCAD.Console.PrintMessage('===========================================\n')
FreeCAD.Console.PrintMessage("unfoldBox: start.\n")
FreeCAD.Console.PrintMessage('unfoldBox: start.\n')
try:
try:
file_name = fields_l[0].text()
file_name = fields_l[0].text()
scale = float(fields_l[1].text())
scale = float(fields_l[1].text())
scale_auto = scale_check.isChecked()
scale_auto = scale_check.isChecked()
a3 = a3_check.isChecked()
a3 = a3_check.isChecked()
cartridge = cartridge_check.isChecked()
cartridge = cartridge_check.isChecked()
onedrawing = onedrawing_check.isChecked()
onedrawing = onedrawing_check.isChecked()
sewed = sewed_check.isChecked()
sewed = sewed_check.isChecked()
FreeCAD.Console.PrintMessage("unfoldBox.file_name: "+file_name+"\n")
FreeCAD.Console.PrintMessage('unfoldBox.file_name: '+file_name+'\n')
FreeCAD.Console.PrintMessage("unfoldBox.scale: "+str(scale)+"\n")
FreeCAD.Console.PrintMessage('unfoldBox.scale: '+str(scale)+'\n')
FreeCAD.Console.PrintMessage("unfoldBox.scale_check: "+str(scale_auto)+"\n")
FreeCAD.Console.PrintMessage('unfoldBox.scale_check: '+str(scale_auto)+'\n')
FreeCAD.Console.PrintMessage("unfoldBox.a3_check: "+str(a3)+"\n")
FreeCAD.Console.PrintMessage('unfoldBox.a3_check: '+str(a3)+'\n')
FreeCAD.Console.PrintMessage("unfoldBox.cartridge: "+str(cartridge)+"\n")
FreeCAD.Console.PrintMessage('unfoldBox.cartridge: '+str(cartridge)+'\n')
FreeCAD.Console.PrintMessage("unfoldBox.onedrawing: "+str(onedrawing)+"\n")
FreeCAD.Console.PrintMessage('unfoldBox.onedrawing: '+str(onedrawing)+'\n')
FreeCAD.Console.PrintMessage("unfoldBox.sewed: "+str(sewed)+"\n")
FreeCAD.Console.PrintMessage('unfoldBox.sewed: '+str(sewed)+'\n')
except:
except:
msg="unfoldBox: wrong inputs...\n"
msg = 'unfoldBox: wrong inputs...\n'
FreeCAD.Console.PrintError(msg)
FreeCAD.Console.PrintError(msg)
errorDialog(msg)
errorDialog(msg)


QtGui.qApp.restoreOverrideCursor()
QtGui.qApp.restoreOverrideCursor()
DialogBox.hide()
DialogBox.hide()
#
#
# Get selection
# Get selection
#
#
sel=FreeCADGui.Selection.getSelection()
sel = FreeCADGui.Selection.getSelection()
faceid=0
objnames_l=[]
grp = FreeCAD.activeDocument().addObject('App::DocumentObjectGroup', str(file_name))
objnames_l=[]
for objid in range(sel.__len__()):
tree_l=[]
obj = Draft.clone(sel[objid])
grp=FreeCAD.activeDocument().addObject("App::DocumentObjectGroup", str(file_name))
for objid in range( sel.__len__() ):
grp.addObject(obj)
obj=Draft.clone(sel[objid])
objnames_l.append([ obj, sel[objid].Name ])
grp.addObject(obj)
objnames_l.append( [ obj , sel[objid].Name ] )


unfold=unfoldBox()
unfold = unfoldBox()
if sewed :
if sewed :
objnames_l=unfold.done(objnames_l)
objnames_l = unfold.done(objnames_l)
grp.addObject(objnames_l[0][0])
grp.addObject(objnames_l[0][0])
else:
else:
for objid in range( objnames_l.__len__() ):
for objid in range(objnames_l.__len__()):
unfold.moveXY(objnames_l[objid][0])
unfold.moveXY(objnames_l[objid][0])


id=0
id_ = 0
while objnames_l.__len__() > 0:
while objnames_l.__len__() > 0:
draw=Drawing2d( scale, scale_auto , a3 , cartridge , onedrawing , FreeCAD.activeDocument().Name , "Page"+str(id) )
draw = Drawing2d(scale, scale_auto, a3, cartridge, onedrawing, FreeCAD.activeDocument().Name, 'Page'+str(id_))
objnames_l=draw.all( objnames_l )
objnames_l = draw.all_(objnames_l)
id=id+1
id_ = id_+1
FreeCAD.Console.PrintMessage("unfoldBox: obj_l= "+str(objnames_l.__len__())+"\n")
FreeCAD.Console.PrintMessage('unfoldBox: obj_l= '+str(objnames_l.__len__())+'\n')


FreeCAD.Console.PrintMessage("unfoldBox: end.\n")
FreeCAD.Console.PrintMessage('unfoldBox: end.\n')
FreeCAD.Console.PrintMessage("===========================================\n")
FreeCAD.Console.PrintMessage('===========================================\n')




#####################################
# Function close
#####################################
def close():
def close():
DialogBox.hide()
DialogBox.hide()



#####################################
# Class unfoldBox
#####################################
class unfoldBox:
class unfoldBox:
def __init__(self):
#####################################
FreeCAD.Console.PrintMessage('unfoldBox.unfoldBox\n')
# Function __init__
self.LIMIT = 0.0001
#####################################
def __init__(self):
FreeCAD.Console.PrintMessage("unfoldBox.unfoldBox\n")
self.LIMIT=0.0001


def done(self, objnames_l):
tree_l = self.makeTree(objnames_l)
for id_ in range(objnames_l.__len__()):
face = objnames_l[id_]
self.moveXY(face[0])
self.sew(objnames_l, tree_l)
return self.fusion(objnames_l)


def makeTree(self, objnames_l):
#####################################
# Initialisation of tree_l.
# Function done
tree_l = []
#####################################
def done(self,objnames_l):
for k in range(objnames_l.__len__()):
tree_l=self.makeTree(objnames_l)
facek = objnames_l[k][0]
for id in range( objnames_l.__len__() ):
facek_l = []
for i in range(facek.Shape.Edges.__len__()):
face=objnames_l[id]
if False and type(facek.Shape.Edges[i].Curve).__name__ != 'GeomLineSegment':
self.moveXY(face[0])
facek_l.append([-1, -1])
self.sew( objnames_l , tree_l )
return self.fusion(objnames_l)
else:
# Search face link to the ith edge
vki0 = facek.Shape.Edges[i].Curve.StartPoint
vki1 = facek.Shape.Edges[i].Curve.EndPoint
found = False
for l in range(k+1, objnames_l.__len__()):
facel = objnames_l[l][0]
for j in range(facel.Shape.Edges.__len__()):
vlj0 = facel.Shape.Edges[j].Curve.StartPoint
vlj1 = facel.Shape.Edges[j].Curve.EndPoint
if vki0.__eq__(vlj0) and vki1.__eq__(vlj1):
arelinked = False
isfacek = False
isfacel = False
for kk in range(k-1):
for ii in range(tree_l[kk].__len__()):
if tree_l[kk][ii][0] == k:
isfacek = True
if tree_l[kk][ii][0] == l:
isfacel = True
if isfacek and isfacel:
arelinked = True
break
if not arelinked:
facek_l.append([l, j])
found = True
break
if found:
break
if not found:
facek_l.append([-1, -1])
tree_l.append(facek_l)
return tree_l


def sew(self, objnames_l, tree_l):
placed_l = []
#####################################
for k in range(tree_l.__len__()):
# Function makeTree
iskplaced = False
#####################################
for p in range(placed_l.__len__()):
def makeTree(self,objnames_l):
if placed_l[p] == k:
#
iskplaced = True
# Initialisation of tree_l
if not iskplaced:
#
placed_l.append(k)
tree_l=[]
facek = tree_l[k]
for k in range( objnames_l.__len__() ):
facek=objnames_l[k][0]
objk = objnames_l[k][0]
for i in range(facek.__len__()):
facek_l=[]
edgeki = facek[i]
for i in range( facek.Shape.Edges.__len__() ):
l = edgeki[0]
if False and type(facek.Shape.Edges[i].Curve).__name__ != 'GeomLineSegment':
facek_l.append([-1,-1])
j = edgeki[1]
islplaced = False
else:
for p in range(placed_l.__len__()):
#
if placed_l[p] == l:
# Search face link to the ith edge
islplaced = True
#
break
vki0=facek.Shape.Edges[i].Curve.StartPoint
if not islplaced:
vki1=facek.Shape.Edges[i].Curve.EndPoint
placed_l.append(l)
found=False
if l >= 0 and not (islplaced and iskplaced):
for l in range( k+1 , objnames_l.__len__() ):
facel=objnames_l[l][0]
iskplaced = True
for j in range( facel.Shape.Edges.__len__() ):
# Move facel.edgelj to facek.edgeki.
vlj0=facel.Shape.Edges[j].Curve.StartPoint
objl = objnames_l[l][0]
vlj1=facel.Shape.Edges[j].Curve.EndPoint
vki0 = objk.Shape.Edges[i].Curve.StartPoint
if vki0.__eq__(vlj0) and vki1.__eq__(vlj1):
vki1 = objk.Shape.Edges[i].Curve.EndPoint
arelinked=False
vlj0 = objl.Shape.Edges[j].Curve.StartPoint
isfacek=False
vlj1 = objl.Shape.Edges[j].Curve.EndPoint
isfacel=False
vk = vki1.sub(vki0)
for kk in range( k-1 ):
vl = vlj1.sub(vlj0)
for ii in range( tree_l[kk].__len__() ):
alpk = vk.getAngle(vl)*180/math.pi
if tree_l[kk][ii][0]==k: isfacek=True
alpl = vl.getAngle(vk)*180/math.pi
if tree_l[kk][ii][0]==l: isfacel=True
self.isPlanZ(objk)
if isfacek and isfacel:
if islplaced:
arelinked=True
Draft.move(objk, vlj0.sub(vki0))
break
else:
Draft.move(objl, vki0.sub(vlj0))
if not arelinked:
facek_l.append([l,j])
self.isPlanZ(objk)
found=True
break
if found: break
if not found: facek_l.append([-1,-1])
tree_l.append(facek_l)
return tree_l


if math.fabs(vk.dot(FreeCAD.Base.Vector(-vl.y, vl.x, 0))) > self.LIMIT:
if islplaced:
Draft.rotate(objk, -alpl, vlj0, self.vecto(vl, vk))
else:
Draft.rotate(objl, -alpk, vki0, self.vecto(vk, vl))
elif vk.dot(vl) < 0:
if islplaced:
Draft.rotate(objk, 180, vlj0, self.vecto(vl, FreeCAD.Base.Vector(-vl.y, vl.x, 0)))
else:
Draft.rotate(objl, 180, vki0, self.vecto(vk, FreeCAD.Base.Vector(-vk.y, vk.x, 0)))
# Verifications.
vki0 = objk.Shape.Edges[i].Curve.StartPoint
vki1 = objk.Shape.Edges[i].Curve.EndPoint
vlj0 = objl.Shape.Edges[j].Curve.StartPoint
vlj1 = objl.Shape.Edges[j].Curve.EndPoint
vk = vki1.sub(vki0)
vl = vlj1.sub(vlj0)
self.isPlanZ(objk)


# Flip or not.
#####################################
L = max(objl.Shape.BoundBox.XMax, objk.Shape.BoundBox.XMax) - min(objl.Shape.BoundBox.XMin, objk.Shape.BoundBox.XMin)
# Function sew
W = max(objl.Shape.BoundBox.YMax, objk.Shape.BoundBox.YMax) - min(objl.Shape.BoundBox.YMin, objk.Shape.BoundBox.YMin)
#####################################
S1 = L*W
def sew( self,objnames_l , tree_l ):
if islplaced:
placed_l=[]
Draft.rotate(objk, 180, vlj0, vl)
for k in range( tree_l.__len__() ):
iskplaced=False
else:
Draft.rotate(objl, 180, vki0, vk)
for p in range( placed_l.__len__() ):
L = max(objl.Shape.BoundBox.XMax, objk.Shape.BoundBox.XMax) - min(objl.Shape.BoundBox.XMin, objk.Shape.BoundBox.XMin)
if placed_l[p] == k:
W = max(objl.Shape.BoundBox.YMax, objk.Shape.BoundBox.YMax) - min(objl.Shape.BoundBox.YMin, objk.Shape.BoundBox.YMin)
iskplaced=True
S2 = L * W
if not iskplaced:placed_l.append(k)
if (S2 <= S1):
facek=tree_l[k]
if islplaced:
objk=objnames_l[k][0]
Draft.rotate(objk, 180, vlj0, vl)
for i in range( facek.__len__() ):
edgeki=facek[i]
else:
Draft.rotate(objl, 180, vki0, vk)
l=edgeki[0]
j=edgeki[1]
self.isPlanZ(objk)
islplaced=False
for p in range( placed_l.__len__() ):
if placed_l[p] == l:
islplaced=True
break
if not islplaced: placed_l.append(l)
if l >= 0 and not ( islplaced and iskplaced ):
iskplaced=True
#
# Move facel.edgelj to facek.edgeki
#
objl=objnames_l[l][0]
vki0=objk.Shape.Edges[i].Curve.StartPoint
vki1=objk.Shape.Edges[i].Curve.EndPoint
vlj0=objl.Shape.Edges[j].Curve.StartPoint
vlj1=objl.Shape.Edges[j].Curve.EndPoint
vk=vki1.sub(vki0)
vl=vlj1.sub(vlj0)
alpk=vk.getAngle(vl)*180/math.pi
alpl=vl.getAngle(vk)*180/math.pi
self.isPlanZ(objk)
if islplaced:
Draft.move( objk , vlj0.sub(vki0) )
else:
Draft.move( objl , vki0.sub(vlj0) )
self.isPlanZ(objk)


def isPlanZ(self, obj):
if math.fabs( vk.dot(FreeCAD.Base.Vector(-vl.y,vl.x,0))) > self.LIMIT:
L = obj.Shape.BoundBox.XMax - obj.Shape.BoundBox.XMin
if islplaced:
W = obj.Shape.BoundBox.YMax - obj.Shape.BoundBox.YMin
Draft.rotate( objk , -alpl , vlj0 , self.vecto( vl , vk ))
H = obj.Shape.BoundBox.ZMax - obj.Shape.BoundBox.ZMin
else:
Draft.rotate( objl , -alpk , vki0 , self.vecto( vk , vl ))
if H < self.LIMIT:
return True
elif vk.dot(vl)<0:
if islplaced:
else:
return False
Draft.rotate( objk , 180 , vlj0 , self.vecto( vl , FreeCAD.Base.Vector(-vl.y,vl.x,0) ))
else:
Draft.rotate( objl , 180 , vki0 , self.vecto( vk , FreeCAD.Base.Vector(-vk.y,vk.x,0)))
#
# Verifications
#
vki0=objk.Shape.Edges[i].Curve.StartPoint
vki1=objk.Shape.Edges[i].Curve.EndPoint
vlj0=objl.Shape.Edges[j].Curve.StartPoint
vlj1=objl.Shape.Edges[j].Curve.EndPoint
vk=vki1.sub(vki0)
vl=vlj1.sub(vlj0)
self.isPlanZ(objk)


#
# Flip or not
#
L=max(objl.Shape.BoundBox.XMax,objk.Shape.BoundBox.XMax) - min( objl.Shape.BoundBox.XMin,objk.Shape.BoundBox.XMin)
W=max(objl.Shape.BoundBox.YMax,objk.Shape.BoundBox.YMax) - min( objl.Shape.BoundBox.YMin,objk.Shape.BoundBox.YMin)
S1=L*W
if islplaced:
dum=0
Draft.rotate( objk , 180 , vlj0 ,vl)
else:
dum=0
Draft.rotate( objl , 180 , vki0 ,vk)
L=max(objl.Shape.BoundBox.XMax,objk.Shape.BoundBox.XMax) - min( objl.Shape.BoundBox.XMin,objk.Shape.BoundBox.XMin)
W=max(objl.Shape.BoundBox.YMax,objk.Shape.BoundBox.YMax) - min( objl.Shape.BoundBox.YMin,objk.Shape.BoundBox.YMin)
S2=L*W
if (S2<=S1):
if islplaced:
dum=0
Draft.rotate( objk , 180 , vlj0 ,vl)
else:
dum=0
Draft.rotate( objl , 180 , vki0 ,vk)
self.isPlanZ(objk)


def fusion(self, objnames_l):
#####################################
# Init.
# Function isPlanZ
obj_l=[]
#####################################
objna_l=[]
def isPlanZ(self,obj):
obj0 = objnames_l[0][0];name=objnames_l[0][1]
L=obj.Shape.BoundBox.XMax - obj.Shape.BoundBox.XMin
objfuse = FreeCAD.activeDocument().addObject('Part::MultiFuse','Unfolding')
W=obj.Shape.BoundBox.YMax - obj.Shape.BoundBox.YMin
for k in range(objnames_l.__len__()):
H=obj.Shape.BoundBox.ZMax - obj.Shape.BoundBox.ZMin
if H < self.LIMIT:
objk = objnames_l[k][0]
return True
obj_l.append(objk)
objfuse.Shapes = obj_l
else:
FreeCAD.activeDocument().recompute()
return False
objna_l.append([objfuse, name])
return objna_l


def get2Vectors(self, shape):
v0 = FreeCAD.Base.Vector(0, 0, 0)
v1 = FreeCAD.Base.Vector(0, 0, 0)


edges= shape.Edges
#####################################
for id_ in range(edges.__len__()-1):
# Function fusion
va = edges[id_].Curve.EndPoint.sub(edges[id_].Curve.StartPoint)
#####################################
vb = edges[id_+1].Curve.EndPoint.sub(edges[id_+1].Curve.StartPoint)
def fusion(self,objnames_l):
if vb.sub(va).Length > v1.sub(v0).Length:
#
v0 = self.vect_copy(va);v1=self.vect_copy(vb)
# Init
#
return [v0, v1]
obj_l=[]
objna_l=[]
obj0=objnames_l[0][0];name=objnames_l[0][1]
objfuse=FreeCAD.activeDocument().addObject("Part::MultiFuse","Unfolding")
for k in range( objnames_l.__len__() ):
objk=objnames_l[k][0]
obj_l.append(objk)
objfuse.Shapes=obj_l
FreeCAD.activeDocument().recompute()
objna_l.append([objfuse,name])
return objna_l


def vecto(self, vect1, vect2):
#####################################
'''Function vecto.'''
# Function get2Vectors
v= FreeCAD.Base.Vector(0, 0, 0)
#####################################
v.x = vect1.y*vect2.z-vect1.z*vect2.y
def get2Vectors(self,shape):
v.y = vect1.z*vect2.x-vect1.x*vect2.z
v0=FreeCAD.Base.Vector(0,0,0)
v.z = vect1.x*vect2.y-vect1.y*vect2.x
v1=FreeCAD.Base.Vector(0,0,0)
return v


def vect_copy(self, vect):
edges= shape.Edges
'''Return a copy of vector.'''
for id in range( edges.__len__()-1):
v = vect.add(FreeCAD.Base.Vector(0, 0, 0))
va=edges[id].Curve.EndPoint.sub(edges[id].Curve.StartPoint)
return v
vb=edges[id+1].Curve.EndPoint.sub(edges[id+1].Curve.StartPoint)
if vb.sub(va).Length > v1.sub(v0).Length:
v0=self.vect_copy(va);v1=self.vect_copy(vb)
#FreeCAD.Console.PrintMessage("unfoldBox.get2Vectors: v0= {:s}, v1= {:s}\n".format(str(v0),str(v1)))
return [ v0 , v1 ]


def moveXY(self, obj):
#####################################
# Move to origin
# Function vecto
Draft.move(obj, FreeCAD.Base.Vector(-obj.Shape.BoundBox.XMin, -obj.Shape.BoundBox.YMin, -obj.Shape.BoundBox.ZMin))
# - vect1,2:
# - return abs(sin) angle between
# 2 vectors
#####################################
def vecto( self,vect1, vect2 ):
v= FreeCAD.Base.Vector(0,0,0)
v.x=vect1.y*vect2.z-vect1.z*vect2.y
v.y=vect1.z*vect2.x-vect1.x*vect2.z
v.z=vect1.x*vect2.y-vect1.y*vect2.x
return v


# Find 2 vectors defining the plan of surface
tab = self.get2Vectors(obj.Shape)
v0 = tab[0]
v1 = tab[1]
norm = self.vecto(v0, v1)
norm.normalize()


# Rotate.
#####################################
if math.fabs(norm.x) < self.LIMIT and math.fabs(norm.z) < self.LIMIT:
# Function vect_copy
Draft.rotate(obj, 90, FreeCAD.Base.Vector(0, 0, 0), FreeCAD.Base.Vector(1, 0, 0))
# - vect:
elif math.fabs(norm.y) < self.LIMIT and math.fabs(norm.z) < self.LIMIT:
# - return copy of vector
Draft.rotate(obj, 90, FreeCAD.Base.Vector(0, 0, 0), FreeCAD.Base.Vector(0, 1, 0))
#####################################
else:
def vect_copy( self,vect):
# Rotate following the angle to the normal direction of the plan.
v= vect.add( FreeCAD.Base.Vector(0,0,0) )
oz= FreeCAD.Base.Vector(0, 0, 1)
return v
alp = oz.getAngle(norm)*180/math.pi
Draft.rotate(obj, -alp, FreeCAD.Base.Vector(0, 0, 0), self.vecto(oz, norm))


# Move to z = 0.
Draft.move(obj, FreeCAD.Base.Vector(0, 0, -obj.Shape.BoundBox.ZMin))


#####################################
# Function movexy
#####################################
def moveXY( self,obj ):
#
# Move to origin
#
Draft.move( obj , FreeCAD.Base.Vector( -obj.Shape.BoundBox.XMin , -obj.Shape.BoundBox.YMin , -obj.Shape.BoundBox.ZMin ))
#
# Find 2 vectors defining the plan of surface
#
tab=self.get2Vectors( obj.Shape )
v0=tab[0];v1=tab[1]
norm=self.vecto(v0,v1)
norm.normalize()
#FreeCAD.Console.PrintMessage("unfoldBox.moveXY: norm= {:s}\n".format(str(norm)))


#
# Rotate
#
if math.fabs(norm.x) < self.LIMIT and math.fabs(norm.y) < self.LIMIT:
dum=0
elif math.fabs(norm.x) < self.LIMIT and math.fabs(norm.z) < self.LIMIT:
Draft.rotate( obj , 90 , FreeCAD.Base.Vector(0,0,0) , FreeCAD.Base.Vector(1,0,0) )
elif math.fabs(norm.y) < self.LIMIT and math.fabs(norm.z) < self.LIMIT:
Draft.rotate( obj , 90 , FreeCAD.Base.Vector(0,0,0) , FreeCAD.Base.Vector(0,1,0) )
else:
#
# Rotate following the angle to the normal direction of the plan
#
oz= FreeCAD.Base.Vector(0,0,1)
alp=oz.getAngle(norm)*180/math.pi
#FreeCAD.Console.PrintMessage("unfoldBox.moveXY: alp= "+str(alp)+"\n")
#FreeCAD.Console.PrintMessage("unfoldBox.moveXY: vecto= {:s}\n".format(str(self.vecto(oz,norm))))
Draft.rotate( obj , -alp , FreeCAD.Base.Vector(0,0,0) , self.vecto( oz, norm ))
#
# Move to z=0
#
Draft.move( obj , FreeCAD.Base.Vector( 0 , 0 , -obj.Shape.BoundBox.ZMin ))
L=obj.Shape.BoundBox.XMax - obj.Shape.BoundBox.XMin
W=obj.Shape.BoundBox.YMax - obj.Shape.BoundBox.YMin
H=obj.Shape.BoundBox.ZMax - obj.Shape.BoundBox.ZMin


#####################################
# Class Drawing2d
#####################################
class Drawing2d:
class Drawing2d:
def __init__(self, scale, scale_auto, a3, cartridge, onedrawing, drawing_name, page_name):
#####################################
# Function __init__
"""Function __init__
# - Scale
# - scale_auto
# - a3
# - cartridge
# - onedrawing
#####################################
def __init__( self, scale , scale_auto , a3 , cartridge , onedrawing , drawing_name , page_name ):
self.TopX_H=0
self.TopY_H=0
self.TopX_V=0
self.TopY_V=0
self.TopX_Hmax=0
self.TopY_Hmax=0
self.TopX_Vmax=0
self.TopY_Vmax=0
self.a3=a3
self.pts_nbr=100
self.scale=scale
self.scale_auto=scale_auto
self.cartridge=cartridge
self.onedrawing=onedrawing
if self.a3:
self.L=420
self.H=297
self.marge=6
else:
self.L=297
self.H=210
self.marge=6
self.page_name=page_name
self.drawing_name=drawing_name


- scale
#####################################
- scale_auto
# Function newPage
- a3
#####################################
- cartridge
def newPage( self ):
- onedrawing
freecad_dir=os.getenv('HOME')+"/.FreeCAD/Mod/unfoldBox"
"""
page = FreeCAD.activeDocument().addObject('Drawing::FeaturePage', self.page_name )
if self.a3:
self.TopX_H = 0
if self.cartridge:
self.TopY_H = 0
self.TopX_V = 0
page.Template = freecad_dir+'/A3_Landscape.svg'
self.TopY_V = 0
self.TopX_Hmax = 0
self.TopY_Hmax = 0
self.TopX_Vmax = 0
self.TopY_Vmax = 0
self.a3 = a3
self.pts_nbr = 100
self.scale = scale
self.scale_auto = scale_auto
self.cartridge = cartridge
self.onedrawing = onedrawing
if self.a3:
self.L = 420
self.H = 297
self.marge = 6
else:
else:
page.Template = freecad_dir+'/A3_Landscape_Empty.svg'
self.L = 297
self.H = 210
else:
if self.cartridge:
self.marge = 6
self.page_name = page_name
page.Template = freecad_dir+'/A4_Landscape.svg'
self.drawing_name = drawing_name

def newPage(self):
freecad_dir = os.getenv('HOME') + '/.FreeCAD/Mod/unfoldBox'
page = FreeCAD.activeDocument().addObject(
'Drawing::FeaturePage', self.page_name)
if self.a3:
if self.cartridge:
page.Template = freecad_dir+'/A3_Landscape.svg'
else:
page.Template = freecad_dir+'/A3_Landscape_Empty.svg'
else:
else:
if self.cartridge:
page.Template = freecad_dir+'/A4_Landscape_Empty.svg'
page.Template = freecad_dir+'/A4_Landscape.svg'
return page
else:
page.Template = freecad_dir+'/A4_Landscape_Empty.svg'
return page


def all_(self, objnames_l):
#####################################
obj_l = []
# Function all
for objid in range(objnames_l.__len__()):
#####################################
if objid == 0 or not self.onedrawing:
def all( self, objnames_l ):
self.newPage()
obj_l=[]
obj_l.extend(self.done(objid, objnames_l[objid]))
for objid in range( objnames_l.__len__() ):
return obj_l
if objid == 0 or not self.onedrawing:
page = self.newPage()
obj_l.extend( self.done( objid , objnames_l[objid] ))
return obj_l


def done(self, id_, objname):
# Init.
obj_l = []
obj = objname[0]
objname = objname[1]
xmax = obj.Shape.BoundBox.XMax-obj.Shape.BoundBox.XMin
ymax = obj.Shape.BoundBox.YMax-obj.Shape.BoundBox.YMin
if ymax > xmax:
Draft.rotate(obj, 90)
Draft.move(obj,
FreeCAD.Base.Vector(
-obj.Shape.BoundBox.XMin, -obj.Shape.BoundBox.YMin, 0))
xmax = obj.Shape.BoundBox.XMax-obj.Shape.BoundBox.XMin
ymax = obj.Shape.BoundBox.YMax-obj.Shape.BoundBox.YMin


scale = min((self.L-4*self.marge) / xmax, (self.H-4*self.marge) / ymax)


if (not self.scale_auto) or (self.onedrawing):
scale = self.scale


if id_ == 0 or not self.onedrawing:
#####################################
# Init.
# Function done
FreeCAD.Console.PrintMessage('Dawing2d: init\n')
#####################################
self.TopX_H = self.marge*2
def done( self, id , objname ):
self.TopY_H = self.marge*2
#
TopX = self.TopX_H
# Init
TopY = self.TopY_H
#
self.TopX_H = self.TopX_H + xmax * scale + self.marge
obj_l=[]
self.TopY_H = self.TopY_H
obj=objname[0]
self.TopX_Hmax = max(self.TopX_Hmax, self.TopX_H)
objname=objname[1]
self.TopY_Hmax = max(self.TopY_Hmax, self.TopY_H + ymax*scale + self.marge)
xmax=obj.Shape.BoundBox.XMax-obj.Shape.BoundBox.XMin
self.TopX_Vmax = max(self.TopX_Vmax, self.TopX_Hmax)
ymax=obj.Shape.BoundBox.YMax-obj.Shape.BoundBox.YMin
self.TopX_V = max(self.TopX_Vmax, self.TopX_V)
if ymax > xmax :
Draft.rotate( obj , 90 )
self.TopY_V = self.marge * 2
elif self.onedrawing:
Draft.move( obj , FreeCAD.Base.Vector( -obj.Shape.BoundBox.XMin , -obj.Shape.BoundBox.YMin , 0))
if self.TopX_H + xmax * scale < self.L:
xmax=obj.Shape.BoundBox.XMax-obj.Shape.BoundBox.XMin
if self.TopY_H + ymax * scale + self.marge*2 < self.H:
ymax=obj.Shape.BoundBox.YMax-obj.Shape.BoundBox.YMin
# H Add at right on same horizontal line.
FreeCAD.Console.PrintMessage('Dawing2d: horizontal\n')
TopX = self.TopX_H
TopY = self.TopY_H
self.TopX_H = self.TopX_H + xmax * scale + self.marge
self.TopX_Hmax = max(self.TopX_Hmax, self.TopX_H)
self.TopY_Hmax = max(self.TopY_Hmax, self.TopY_H + ymax*scale + self.marge)
self.TopX_Vmax = max(self.TopX_Hmax, self.TopX_Vmax)
self.TopX_Vmax = max(self.TopX_Vmax, self.TopX_Hmax)
self.TopX_V = max(self.TopX_Vmax, self.TopX_V)
else:
#
# V Add at right on same horizontal line
#
FreeCAD.Console.PrintMessage('Dawing2d: vertival\n')
if self.TopX_V + ymax * scale + 2 * self.marge < self.L and self.TopY_V + xmax * scale + 2*self.marge < self.H:
Draft.rotate(obj, 90)
Draft.move(obj, FreeCAD.Base.Vector(-obj.BoundBox.XMin, -obj.BoundBox.YMin, 0))
self.TopX_V = max(self.TopX_Vmax, self.TopX_V)
TopX = self.TopX_V
TopY = self.TopY_V
self.TopX_V = self.TopX_V + ymax * scale + self.marge
self.TopY_Vmax = max(self.TopY_Vmax, self.TopY_V + xmax * scale + self.marge)
else:
obj_l.append([obj, 'name'])
return obj_l
else:
# H Carriage return.
if (self.TopY_Hmax + ymax * scale + self.marge*2 < self.H):
FreeCAD.Console.PrintMessage('Dawing2d: carriage return: '+str(self.TopY_H + ymax * scale)+' > '+str(self.H)+'\n')
TopX = self.marge*2
TopY = self.TopY_Hmax
self.TopX_H = TopX + xmax * scale + self.marge
self.TopY_H = TopY
self.TopX_Hmax = max(self.TopX_Hmax, self.TopX_H)
self.TopY_Hmax = self.TopY_Hmax + ymax*scale+self.marge
self.TopX_Vmax = max(self.TopX_Vmax, self.TopX_Hmax)
self.TopX_V = max(self.TopX_Vmax, self.TopX_V)
else:
# V Add at right on same horizontal line.
FreeCAD.Console.PrintMessage('Dawing2d: vertical: ' + str(self.TopX_V) + ', ' + str(self.TopX_Vmax) + '\n')
if self.TopX_V + ymax * scale + 2*self.marge < self.L and self.TopY_V + xmax * scale + 2*self.marge < self.H :
Draft.rotate(obj, 90)
Draft.move(obj, FreeCAD.Base.Vector(-obj.BoundBox.XMin, -obj.BoundBox.YMin, 0))
TopX = self.TopX_V
TopY = self.TopY_V
self.TopX_V = self.TopX_V + ymax * scale + self.marge
self.TopY_Vmax = max(self.TopY_Vmax, self.TopY_V + xmax * scale + self.marge)
else:
obj_l.append([obj, 'name'])
return obj_l


page = FreeCAD.activeDocument().getObject(self.page_name)
scale=min((self.L-4*self.marge)/xmax,(self.H-4*self.marge)/ymax)


Text = FreeCAD.activeDocument().addObject('Drawing::FeatureViewAnnotation', objname + '_txt')
if ( not self.scale_auto ) or ( self.onedrawing ) :
scale=self.scale
Text.Text = objname
Text.X = TopX + xmax / 2 * scale
Text.Y = TopY + ymax / 2 * scale
Text.Scale = 1


TopView = FreeCAD.activeDocument().addObject('Drawing::FeatureViewPart', 'TopView')
TopView.Source = obj
TopView.Direction = (0.0, 0.0, 1)
TopView.Rotation = 0
TopView.X = TopX
TopView.Y = TopY
TopView.ShowHiddenLines = True
TopView.Scale = scale
page.addObject(TopView)
page.addObject(Text)
FreeCAD.activeDocument().recompute()
return obj_l


if id == 0 or not self.onedrawing:
#
# Init
#
FreeCAD.Console.PrintMessage("Dawing2d: init\n")
self.TopX_H=self.marge*2
self.TopY_H=self.marge*2
TopX=self.TopX_H
TopY=self.TopY_H
self.TopX_H=self.TopX_H + xmax * scale + self.marge
self.TopY_H=self.TopY_H
self.TopX_Hmax=max( self.TopX_Hmax , self.TopX_H )
self.TopY_Hmax=max( self.TopY_Hmax , self.TopY_H + ymax*scale+self.marge )
self.TopX_Vmax=max( self.TopX_Vmax , self.TopX_Hmax )
self.TopX_V=max(self.TopX_Vmax,self.TopX_V)
self.TopY_V=self.marge*2
elif self.onedrawing:
if self.TopX_H + xmax * scale < self.L :
if self.TopY_H + ymax * scale + self.marge*2 < self.H :
#
# H Add at right on same horizontal line
#
FreeCAD.Console.PrintMessage("Dawing2d: horizontal\n")
TopX=self.TopX_H
TopY=self.TopY_H
self.TopX_H=self.TopX_H + xmax * scale + self.marge
self.TopX_Hmax=max( self.TopX_Hmax , self.TopX_H )
self.TopY_Hmax=max( self.TopY_Hmax , self.TopY_H + ymax*scale+self.marge )
self.TopX_Vmax=max( self.TopX_Hmax , self.TopX_Vmax )
self.TopX_Vmax=max( self.TopX_Vmax , self.TopX_Hmax )
self.TopX_V=max(self.TopX_Vmax,self.TopX_V)
else:
#
# V Add at right on same horizontal line
#
FreeCAD.Console.PrintMessage("Dawing2d: vertival\n")
if self.TopX_V + ymax * scale +2* self.marge < self.L and self.TopY_V + xmax * scale + 2*self.marge < self.H :
Draft.rotate( obj , 90 )
Draft.move( obj , FreeCAD.Base.Vector( -obj.BoundBox.XMin , -obj.BoundBox.YMin , 0))
self.TopX_V=max(self.TopX_Vmax, self.TopX_V)
TopX=self.TopX_V
TopY=self.TopY_V
self.TopX_V = self.TopX_V + ymax * scale + self.marge
self.TopY_Vmax=max( self.TopY_Vmax , self.TopY_V + xmax * scale + self.marge )
else:
obj_l.append( [ obj , name ] )
return obj_l


else:
#
# H Carriage return
#
if ( self.TopY_Hmax + ymax * scale + self.marge*2 < self.H ):
FreeCAD.Console.PrintMessage("Dawing2d: carriage return: "+str(self.TopY_H + ymax * scale )+" > "+str(self.H)+"\n")
TopX=self.marge*2
TopY=self.TopY_Hmax
self.TopX_H=TopX + xmax * scale + self.marge
self.TopY_H=TopY
self.TopX_Hmax=max( self.TopX_Hmax , self.TopX_H )
self.TopY_Hmax=self.TopY_Hmax + ymax*scale+self.marge
self.TopX_Vmax=max( self.TopX_Vmax , self.TopX_Hmax )
self.TopX_V=max(self.TopX_Vmax,self.TopX_V)
else:
#
# V Add at right on same horizontal line
#
FreeCAD.Console.PrintMessage("Dawing2d: vertival: "+str(self.TopX_V)+" , "+str(self.TopX_Vmax)+"\n")
if self.TopX_V + ymax * scale + 2*self.marge < self.L and self.TopY_V + xmax * scale + 2*self.marge < self.H :
Draft.rotate( obj , 90 )
Draft.move( obj , FreeCAD.Base.Vector( -obj.BoundBox.XMin , -obj.BoundBox.YMin , 0))
TopX=self.TopX_V
TopY=self.TopY_V
self.TopX_V = self.TopX_V + ymax * scale + self.marge
self.TopY_Vmax=max( self.TopY_Vmax , self.TopY_V + xmax * scale + self.marge )
else:
obj_l.append( [ obj , name ] )
return obj_l

page=FreeCAD.activeDocument().getObject(self.page_name )

Text=FreeCAD.activeDocument().addObject('Drawing::FeatureViewAnnotation', objname+"_txt")
Text.Text=objname
Text.X=TopX+xmax/2*scale
Text.Y=TopY+ymax/2*scale
Text.Scale=1

TopView = FreeCAD.activeDocument().addObject('Drawing::FeatureViewPart','TopView')
TopView.Source = obj
TopView.Direction = (0.0,0.0,1)
TopView.Rotation = 0
TopView.X = TopX
TopView.Y = TopY
TopView.ShowHiddenLines = True
TopView.Scale = scale
page.addObject(TopView)
page.addObject(Text)
FreeCAD.activeDocument().recompute()
return obj_l




#####################################
#####################################
# Dialog Box
#####################################
#####################################
# Dialog Box
#####################################
#####################################
fields = [[ "Group Name" , "Unfolding" ]]
fields = [['Group Name', 'Unfolding']]
fields.append(["Scale","1" ])
fields.append(['Scale', '1'])


DialogBox = QtGui.QDialog()
DialogBox = QtGui.QDialog()
DialogBox.resize(250,250)
DialogBox.resize(250, 250)
DialogBox.setWindowTitle("unfoldBox")
DialogBox.setWindowTitle('unfoldBox')
la = QtGui.QVBoxLayout(DialogBox)
la = QtGui.QVBoxLayout(DialogBox)


# Input fields.
#
# Input fields
for id_ in range(len(fields)):
la.addWidget(QtGui.QLabel(fields[id_][0]))
#
for id in range(len( fields )):
fields_l.append(QtGui.QLineEdit(fields[id_][1]))
la.addWidget(QtGui.QLabel( fields[ id ][ 0 ] ))
la.addWidget(fields_l[id_])
fields_l.append( QtGui.QLineEdit( fields[ id ][ 1 ] ))
la.addWidget( fields_l[ id ] )


scale_check = QtGui.QCheckBox( DialogBox )
scale_check = QtGui.QCheckBox(DialogBox)
scale_check.setObjectName("checkBox")
scale_check.setObjectName('checkBox')
scale_check.setChecked(True)
scale_check.setChecked(True)
la.addWidget(QtGui.QLabel("Scale auto"))
la.addWidget(QtGui.QLabel('Scale auto'))
la.addWidget(scale_check)
la.addWidget(scale_check)


a3_check = QtGui.QCheckBox( DialogBox )
a3_check = QtGui.QCheckBox(DialogBox)
a3_check.setObjectName("checkBox")
a3_check.setObjectName('checkBox')
la.addWidget(QtGui.QLabel("A3 Format"))
la.addWidget(QtGui.QLabel('A3 Format'))
a3_check.setChecked(False)
a3_check.setChecked(False)
la.addWidget(a3_check)
la.addWidget(a3_check)


cartridge_check = QtGui.QCheckBox( DialogBox )
cartridge_check = QtGui.QCheckBox(DialogBox)
cartridge_check.setObjectName("checkBox")
cartridge_check.setObjectName('checkBox')
la.addWidget(QtGui.QLabel("Cartridge"))
la.addWidget(QtGui.QLabel('Cartridge'))
cartridge_check.setChecked(False)
cartridge_check.setChecked(False)
la.addWidget(cartridge_check)
la.addWidget(cartridge_check)


onedrawing_check = QtGui.QCheckBox( DialogBox )
onedrawing_check = QtGui.QCheckBox(DialogBox)
onedrawing_check.setObjectName("checkBox")
onedrawing_check.setObjectName('checkBox')
la.addWidget(QtGui.QLabel("Group drawings in page"))
la.addWidget(QtGui.QLabel('Group drawings in page'))
onedrawing_check.setChecked(True)
onedrawing_check.setChecked(True)
la.addWidget(onedrawing_check)
la.addWidget(onedrawing_check)


sewed_check = QtGui.QCheckBox( DialogBox )
sewed_check = QtGui.QCheckBox(DialogBox)
sewed_check.setObjectName("checkBox")
sewed_check.setObjectName('checkBox')
la.addWidget(QtGui.QLabel("Sewed surfaces"))
la.addWidget(QtGui.QLabel('Sewed surfaces'))
sewed_check.setChecked(True)
sewed_check.setChecked(True)
la.addWidget(sewed_check)
la.addWidget(sewed_check)
Line 688: Line 589:
box = QtGui.QDialogButtonBox(DialogBox)
box = QtGui.QDialogButtonBox(DialogBox)
box.setOrientation(QtCore.Qt.Horizontal)
box.setOrientation(QtCore.Qt.Horizontal)
box.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok)
box.setStandardButtons(QtGui.QDialogButtonBox.Cancel | QtGui.QDialogButtonBox.Ok)
la.addWidget(box)
la.addWidget(box)


QtCore.QObject.connect(box, QtCore.SIGNAL("accepted()"), proceed )
QtCore.QObject.connect(box, QtCore.SIGNAL('accepted()'), proceed)
QtCore.QObject.connect(box, QtCore.SIGNAL("rejected()"), close )
QtCore.QObject.connect(box, QtCore.SIGNAL('rejected()'), close)
QtCore.QMetaObject.connectSlotsByName(DialogBox)
QtCore.QMetaObject.connectSlotsByName(DialogBox)
DialogBox.show()
DialogBox.show()


</nowiki>
}}
}}

Revision as of 14:11, 23 May 2020

Other languages:

Macro Unfold Box

Description
La macro permet de déplier les surfaces d'un objet de n'importe quelle forme et la dessiner sur une page.


Version macro : 1.0.1
Date dernière modification : 2020-03-100
Téléchargement : ToolBar Icon
Auteur: Hervé B.
Auteur
Hervé B.
Téléchargement
ToolBar Icon
Liens
Version Macro
1.0.1
Dernière modification
2020-03-100
Version(s) FreeCAD
Raccourci clavier
None
Voir aussi
None

Description

La macro permet de déplier les surfaces d'un objet de n'importe quelle forme et la dessiner sur une page.

Macro_unfoldBox

Macro_unfoldBox

Installation

Copiez le fichier de la macro dans le répertoire :

  • Linux & Mac  : $home/.Freecad/Mod/unfoldBox.
  • Windows : C:\Program Files\FreeCAD0.13

Ajouter les templates : A3_Landscape_Empty.svg A3_Landscape.svg A4_Landscape_Empty.svg A4_Landscape.svg

Cf Macro for unfolding box surfaces

Options

  • Échelle manuelle ou automatique
  • Format de Page : A3 / A4, cartouche (cf FreeCAD modèles)
  • Groupe les dessins dans la même page si possible.
  • Attache ou non les bords de la pièce.

Utilisation

  1. Sélectionnez un volume créé avec par exemple Part::Loft tool
  2. Développe la forme dans le plan de la forme (cf Draft menu)
  3. Sélectionnez la surface
  4. Exécuter la macro

Script

ToolBar icon

Macro_unfoldBox.FCMacro

'''FreeCAD Macro unfoldBox.

Unroll of a ruled surface
'''
#####################################
# SEE https://wiki.freecadweb.org/Macro_Unfold_Box.
# ***************************************************************************
# *                                                                         *
# *   Copyright (c) 2013 - DoNovae/Herve BAILLY <hbl13@donovae.com>         *
# *                                                                         *
# *   This program is free software; you can redistribute it and/or modify  *
# *   it under the terms of the GNU Lesser General Public License (LGPL)    *
# *   as published by the Free Software Foundation; either version 2 of     *
# *   the License, or (at your option) any later version.                   *
# *   for detail see the LICENCE text file.                                 *
# *                                                                         *
# *   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 Library General Public License for more details.                  *
# *                                                                         *
# *   You should have received a copy of the GNU Library General Public     *
# *   License along with this program; if not, write to the Free Software   *
# *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
# *   USA                                                                   *
# *                                                                         *
# ***************************************************************************

import os
import math

import FreeCAD, FreeCADGui, Draft
from PySide import QtGui, QtCore
from FreeCAD import Base
fields_l = []
unroll_l = []

#####################################
# Functions
#####################################


def errorDialog(msg):
    diag = QtGui.QMessageBox(QtGui.QMessageBox.Critical, u'Error Message',msg)
    diag.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
    diag.exec_()


def proceed():
    QtGui.qApp.setOverrideCursor(QtCore.Qt.WaitCursor)

    FreeCAD.Console.PrintMessage('===========================================\n')
    FreeCAD.Console.PrintMessage('unfoldBox: start.\n')
    try:
        file_name = fields_l[0].text()
        scale = float(fields_l[1].text())
        scale_auto = scale_check.isChecked()
        a3 = a3_check.isChecked()
        cartridge = cartridge_check.isChecked()
        onedrawing = onedrawing_check.isChecked()
        sewed = sewed_check.isChecked()
        FreeCAD.Console.PrintMessage('unfoldBox.file_name: '+file_name+'\n')
        FreeCAD.Console.PrintMessage('unfoldBox.scale: '+str(scale)+'\n')
        FreeCAD.Console.PrintMessage('unfoldBox.scale_check: '+str(scale_auto)+'\n')
        FreeCAD.Console.PrintMessage('unfoldBox.a3_check: '+str(a3)+'\n')
        FreeCAD.Console.PrintMessage('unfoldBox.cartridge: '+str(cartridge)+'\n')
        FreeCAD.Console.PrintMessage('unfoldBox.onedrawing: '+str(onedrawing)+'\n')
        FreeCAD.Console.PrintMessage('unfoldBox.sewed: '+str(sewed)+'\n')
    except:
        msg = 'unfoldBox: wrong inputs...\n'
        FreeCAD.Console.PrintError(msg)
        errorDialog(msg)

    QtGui.qApp.restoreOverrideCursor()
    DialogBox.hide()
    #
    # Get selection
    #
    sel = FreeCADGui.Selection.getSelection()
    objnames_l=[]
    grp = FreeCAD.activeDocument().addObject('App::DocumentObjectGroup', str(file_name))
    for objid in range(sel.__len__()):
        obj = Draft.clone(sel[objid])
        grp.addObject(obj)
        objnames_l.append([ obj, sel[objid].Name ])

    unfold = unfoldBox()
    if sewed :
        objnames_l = unfold.done(objnames_l)
        grp.addObject(objnames_l[0][0])
    else:
        for objid in range(objnames_l.__len__()):
            unfold.moveXY(objnames_l[objid][0])

    id_ = 0
    while objnames_l.__len__() > 0:
        draw = Drawing2d(scale, scale_auto, a3, cartridge, onedrawing, FreeCAD.activeDocument().Name, 'Page'+str(id_))
        objnames_l = draw.all_(objnames_l)
        id_ = id_+1
        FreeCAD.Console.PrintMessage('unfoldBox: obj_l= '+str(objnames_l.__len__())+'\n')

    FreeCAD.Console.PrintMessage('unfoldBox: end.\n')
    FreeCAD.Console.PrintMessage('===========================================\n')


def close():
    DialogBox.hide()


class unfoldBox:
    def __init__(self):
        FreeCAD.Console.PrintMessage('unfoldBox.unfoldBox\n')
        self.LIMIT = 0.0001

    def done(self, objnames_l):
        tree_l = self.makeTree(objnames_l)
        for id_ in range(objnames_l.__len__()):
            face = objnames_l[id_]
            self.moveXY(face[0])
        self.sew(objnames_l, tree_l)
        return self.fusion(objnames_l)

    def makeTree(self, objnames_l):
        # Initialisation of tree_l.
        tree_l = []
        for k in range(objnames_l.__len__()):
            facek = objnames_l[k][0]
            facek_l = []
            for i in range(facek.Shape.Edges.__len__()):
                if False and type(facek.Shape.Edges[i].Curve).__name__ != 'GeomLineSegment':
                    facek_l.append([-1, -1])
                else:
                    # Search face link to the ith edge
                    vki0 = facek.Shape.Edges[i].Curve.StartPoint
                    vki1 = facek.Shape.Edges[i].Curve.EndPoint
                    found = False
                    for l in range(k+1, objnames_l.__len__()):
                        facel = objnames_l[l][0]
                        for j in range(facel.Shape.Edges.__len__()):
                            vlj0 = facel.Shape.Edges[j].Curve.StartPoint
                            vlj1 = facel.Shape.Edges[j].Curve.EndPoint
                            if vki0.__eq__(vlj0) and vki1.__eq__(vlj1):
                                arelinked = False
                                isfacek = False
                                isfacel = False
                                for kk in range(k-1):
                                    for ii in range(tree_l[kk].__len__()):
                                        if tree_l[kk][ii][0] == k:
                                            isfacek = True
                                        if tree_l[kk][ii][0] == l:
                                            isfacel = True
                                    if isfacek and isfacel:
                                        arelinked = True
                                        break
                                if not arelinked:
                                    facek_l.append([l, j])
                                    found = True
                                    break
                            if found:
                                break
                if not found:
                    facek_l.append([-1, -1])
            tree_l.append(facek_l)
        return tree_l

    def sew(self, objnames_l, tree_l):
        placed_l = []
        for k in range(tree_l.__len__()):
            iskplaced = False
            for p in range(placed_l.__len__()):
                if placed_l[p] == k:
                    iskplaced = True
            if not iskplaced:
                placed_l.append(k)
            facek = tree_l[k]
            objk = objnames_l[k][0]
            for i in range(facek.__len__()):
                edgeki = facek[i]
                l = edgeki[0]
                j = edgeki[1]
                islplaced = False
                for p in range(placed_l.__len__()):
                    if placed_l[p] == l:
                        islplaced = True
                        break
                if not islplaced:
                    placed_l.append(l)
                if l >= 0 and not (islplaced and iskplaced):
                    iskplaced = True
                    # Move facel.edgelj to facek.edgeki.
                    objl = objnames_l[l][0]
                    vki0 = objk.Shape.Edges[i].Curve.StartPoint
                    vki1 = objk.Shape.Edges[i].Curve.EndPoint
                    vlj0 = objl.Shape.Edges[j].Curve.StartPoint
                    vlj1 = objl.Shape.Edges[j].Curve.EndPoint
                    vk = vki1.sub(vki0)
                    vl = vlj1.sub(vlj0)
                    alpk = vk.getAngle(vl)*180/math.pi
                    alpl = vl.getAngle(vk)*180/math.pi
                    self.isPlanZ(objk)
                    if islplaced:
                        Draft.move(objk, vlj0.sub(vki0))
                    else:
                        Draft.move(objl, vki0.sub(vlj0))
                    self.isPlanZ(objk)

                    if math.fabs(vk.dot(FreeCAD.Base.Vector(-vl.y, vl.x, 0))) > self.LIMIT:
                        if islplaced:
                            Draft.rotate(objk, -alpl, vlj0, self.vecto(vl, vk))
                        else:
                            Draft.rotate(objl, -alpk, vki0, self.vecto(vk, vl))
                    elif vk.dot(vl) < 0:
                        if islplaced:
                            Draft.rotate(objk, 180, vlj0, self.vecto(vl, FreeCAD.Base.Vector(-vl.y, vl.x, 0)))
                        else:
                            Draft.rotate(objl, 180, vki0, self.vecto(vk, FreeCAD.Base.Vector(-vk.y, vk.x, 0)))
                    # Verifications.
                    vki0 = objk.Shape.Edges[i].Curve.StartPoint
                    vki1 = objk.Shape.Edges[i].Curve.EndPoint
                    vlj0 = objl.Shape.Edges[j].Curve.StartPoint
                    vlj1 = objl.Shape.Edges[j].Curve.EndPoint
                    vk = vki1.sub(vki0)
                    vl = vlj1.sub(vlj0)
                    self.isPlanZ(objk)

                    # Flip or not.
                    L = max(objl.Shape.BoundBox.XMax, objk.Shape.BoundBox.XMax) - min(objl.Shape.BoundBox.XMin, objk.Shape.BoundBox.XMin)
                    W = max(objl.Shape.BoundBox.YMax, objk.Shape.BoundBox.YMax) - min(objl.Shape.BoundBox.YMin, objk.Shape.BoundBox.YMin)
                    S1 = L*W
                    if islplaced:
                        Draft.rotate(objk, 180, vlj0, vl)
                    else:
                        Draft.rotate(objl, 180, vki0, vk)
                    L = max(objl.Shape.BoundBox.XMax, objk.Shape.BoundBox.XMax) - min(objl.Shape.BoundBox.XMin, objk.Shape.BoundBox.XMin)
                    W = max(objl.Shape.BoundBox.YMax, objk.Shape.BoundBox.YMax) - min(objl.Shape.BoundBox.YMin, objk.Shape.BoundBox.YMin)
                    S2 = L * W
                    if (S2 <= S1):
                        if islplaced:
                            Draft.rotate(objk, 180, vlj0, vl)
                        else:
                            Draft.rotate(objl, 180, vki0, vk)
                    self.isPlanZ(objk)

    def isPlanZ(self, obj):
        L = obj.Shape.BoundBox.XMax - obj.Shape.BoundBox.XMin
        W = obj.Shape.BoundBox.YMax - obj.Shape.BoundBox.YMin
        H = obj.Shape.BoundBox.ZMax - obj.Shape.BoundBox.ZMin
        if H < self.LIMIT:
            return True
        else:
            return False


    def fusion(self, objnames_l):
        # Init.
        obj_l=[]
        objna_l=[]
        obj0 = objnames_l[0][0];name=objnames_l[0][1]
        objfuse = FreeCAD.activeDocument().addObject('Part::MultiFuse','Unfolding')
        for k in range(objnames_l.__len__()):
            objk = objnames_l[k][0]
            obj_l.append(objk)
        objfuse.Shapes = obj_l
        FreeCAD.activeDocument().recompute()
        objna_l.append([objfuse, name])
        return objna_l

    def get2Vectors(self, shape):
        v0 = FreeCAD.Base.Vector(0, 0, 0)
        v1 = FreeCAD.Base.Vector(0, 0, 0)

        edges= shape.Edges
        for id_ in range(edges.__len__()-1):
            va = edges[id_].Curve.EndPoint.sub(edges[id_].Curve.StartPoint)
            vb = edges[id_+1].Curve.EndPoint.sub(edges[id_+1].Curve.StartPoint)
            if vb.sub(va).Length > v1.sub(v0).Length:
                v0 = self.vect_copy(va);v1=self.vect_copy(vb)
        return [v0, v1]

    def vecto(self, vect1, vect2):
        '''Function vecto.'''
        v= FreeCAD.Base.Vector(0, 0, 0)
        v.x = vect1.y*vect2.z-vect1.z*vect2.y
        v.y = vect1.z*vect2.x-vect1.x*vect2.z
        v.z = vect1.x*vect2.y-vect1.y*vect2.x
        return v

    def vect_copy(self, vect):
        '''Return a copy of vector.'''
        v = vect.add(FreeCAD.Base.Vector(0, 0, 0))
        return v

    def moveXY(self, obj):
        # Move to origin
        Draft.move(obj, FreeCAD.Base.Vector(-obj.Shape.BoundBox.XMin, -obj.Shape.BoundBox.YMin, -obj.Shape.BoundBox.ZMin))

        # Find 2 vectors defining the plan of surface
        tab = self.get2Vectors(obj.Shape)
        v0 = tab[0]
        v1 = tab[1]
        norm = self.vecto(v0, v1)
        norm.normalize()

        # Rotate.
        if math.fabs(norm.x) < self.LIMIT and math.fabs(norm.z) < self.LIMIT:
            Draft.rotate(obj, 90, FreeCAD.Base.Vector(0, 0, 0), FreeCAD.Base.Vector(1, 0, 0))
        elif math.fabs(norm.y) < self.LIMIT and math.fabs(norm.z) < self.LIMIT:
            Draft.rotate(obj, 90, FreeCAD.Base.Vector(0, 0, 0), FreeCAD.Base.Vector(0, 1, 0))
        else:
            # Rotate following the angle to the normal direction of the plan.
            oz= FreeCAD.Base.Vector(0, 0, 1)
            alp = oz.getAngle(norm)*180/math.pi
            Draft.rotate(obj, -alp, FreeCAD.Base.Vector(0, 0, 0), self.vecto(oz, norm))

        # Move to z = 0.
        Draft.move(obj, FreeCAD.Base.Vector(0, 0, -obj.Shape.BoundBox.ZMin))


class Drawing2d:
    def __init__(self, scale, scale_auto, a3, cartridge, onedrawing, drawing_name, page_name):
        """Function __init__

        - scale
        - scale_auto
        - a3
        - cartridge
        - onedrawing
        """
        self.TopX_H = 0
        self.TopY_H = 0
        self.TopX_V = 0
        self.TopY_V = 0
        self.TopX_Hmax = 0
        self.TopY_Hmax = 0
        self.TopX_Vmax = 0
        self.TopY_Vmax = 0
        self.a3 = a3
        self.pts_nbr = 100
        self.scale = scale
        self.scale_auto = scale_auto
        self.cartridge = cartridge
        self.onedrawing = onedrawing
        if self.a3:
            self.L = 420
            self.H = 297
            self.marge = 6
        else:
            self.L = 297
            self.H = 210
            self.marge = 6
        self.page_name = page_name
        self.drawing_name = drawing_name

    def newPage(self):
        freecad_dir = os.getenv('HOME') + '/.FreeCAD/Mod/unfoldBox'
        page = FreeCAD.activeDocument().addObject(
            'Drawing::FeaturePage', self.page_name)
        if self.a3:
            if self.cartridge:
                page.Template = freecad_dir+'/A3_Landscape.svg'
            else:
                page.Template = freecad_dir+'/A3_Landscape_Empty.svg'
        else:
            if self.cartridge:
                page.Template = freecad_dir+'/A4_Landscape.svg'
            else:
                page.Template = freecad_dir+'/A4_Landscape_Empty.svg'
        return page

    def all_(self, objnames_l):
        obj_l = []
        for objid in range(objnames_l.__len__()):
            if objid == 0 or not self.onedrawing:
                self.newPage()
            obj_l.extend(self.done(objid, objnames_l[objid]))
        return obj_l

    def done(self, id_, objname):
        # Init.
        obj_l = []
        obj = objname[0]
        objname = objname[1]
        xmax = obj.Shape.BoundBox.XMax-obj.Shape.BoundBox.XMin
        ymax = obj.Shape.BoundBox.YMax-obj.Shape.BoundBox.YMin
        if ymax > xmax:
            Draft.rotate(obj, 90)
        Draft.move(obj,
                   FreeCAD.Base.Vector(
                       -obj.Shape.BoundBox.XMin, -obj.Shape.BoundBox.YMin, 0))
        xmax = obj.Shape.BoundBox.XMax-obj.Shape.BoundBox.XMin
        ymax = obj.Shape.BoundBox.YMax-obj.Shape.BoundBox.YMin

        scale = min((self.L-4*self.marge) / xmax, (self.H-4*self.marge) / ymax)

        if (not self.scale_auto) or (self.onedrawing):
            scale = self.scale

        if id_ == 0 or not self.onedrawing:
            # Init.
            FreeCAD.Console.PrintMessage('Dawing2d: init\n')
            self.TopX_H = self.marge*2
            self.TopY_H = self.marge*2
            TopX = self.TopX_H
            TopY = self.TopY_H
            self.TopX_H = self.TopX_H + xmax * scale + self.marge
            self.TopY_H = self.TopY_H
            self.TopX_Hmax = max(self.TopX_Hmax, self.TopX_H)
            self.TopY_Hmax = max(self.TopY_Hmax, self.TopY_H + ymax*scale + self.marge)
            self.TopX_Vmax = max(self.TopX_Vmax, self.TopX_Hmax)
            self.TopX_V = max(self.TopX_Vmax, self.TopX_V)
            self.TopY_V = self.marge * 2
        elif self.onedrawing:
            if self.TopX_H + xmax * scale < self.L:
                if self.TopY_H + ymax * scale + self.marge*2 < self.H:
                    # H Add at right on same horizontal line.
                    FreeCAD.Console.PrintMessage('Dawing2d: horizontal\n')
                    TopX = self.TopX_H
                    TopY = self.TopY_H
                    self.TopX_H = self.TopX_H + xmax * scale + self.marge
                    self.TopX_Hmax = max(self.TopX_Hmax, self.TopX_H)
                    self.TopY_Hmax = max(self.TopY_Hmax, self.TopY_H + ymax*scale + self.marge)
                    self.TopX_Vmax = max(self.TopX_Hmax, self.TopX_Vmax)
                    self.TopX_Vmax = max(self.TopX_Vmax, self.TopX_Hmax)
                    self.TopX_V = max(self.TopX_Vmax, self.TopX_V)
                else:
                    #
                    # V Add at right on same horizontal line
                    #
                    FreeCAD.Console.PrintMessage('Dawing2d: vertival\n')
                    if self.TopX_V + ymax * scale + 2 * self.marge < self.L and self.TopY_V + xmax * scale + 2*self.marge < self.H:
                        Draft.rotate(obj, 90)
                        Draft.move(obj, FreeCAD.Base.Vector(-obj.BoundBox.XMin, -obj.BoundBox.YMin, 0))
                        self.TopX_V = max(self.TopX_Vmax, self.TopX_V)
                        TopX = self.TopX_V
                        TopY = self.TopY_V
                        self.TopX_V = self.TopX_V + ymax * scale + self.marge
                        self.TopY_Vmax = max(self.TopY_Vmax, self.TopY_V + xmax * scale + self.marge)
                    else:
                        obj_l.append([obj, 'name'])
                        return obj_l
            else:
                # H Carriage return.
                if (self.TopY_Hmax + ymax * scale + self.marge*2 < self.H):
                    FreeCAD.Console.PrintMessage('Dawing2d: carriage return: '+str(self.TopY_H + ymax * scale)+' > '+str(self.H)+'\n')
                    TopX = self.marge*2
                    TopY = self.TopY_Hmax
                    self.TopX_H = TopX + xmax * scale + self.marge
                    self.TopY_H = TopY
                    self.TopX_Hmax = max(self.TopX_Hmax, self.TopX_H)
                    self.TopY_Hmax = self.TopY_Hmax + ymax*scale+self.marge
                    self.TopX_Vmax = max(self.TopX_Vmax, self.TopX_Hmax)
                    self.TopX_V = max(self.TopX_Vmax, self.TopX_V)
                else:
                    # V Add at right on same horizontal line.
                    FreeCAD.Console.PrintMessage('Dawing2d: vertical: ' + str(self.TopX_V) + ', ' + str(self.TopX_Vmax) + '\n')
                    if self.TopX_V + ymax * scale + 2*self.marge < self.L and self.TopY_V + xmax * scale + 2*self.marge < self.H :
                        Draft.rotate(obj, 90)
                        Draft.move(obj, FreeCAD.Base.Vector(-obj.BoundBox.XMin, -obj.BoundBox.YMin, 0))
                        TopX = self.TopX_V
                        TopY = self.TopY_V
                        self.TopX_V = self.TopX_V + ymax * scale + self.marge
                        self.TopY_Vmax = max(self.TopY_Vmax, self.TopY_V + xmax * scale + self.marge)
                    else:
                        obj_l.append([obj, 'name'])
                        return obj_l

        page = FreeCAD.activeDocument().getObject(self.page_name)

        Text = FreeCAD.activeDocument().addObject('Drawing::FeatureViewAnnotation', objname + '_txt')
        Text.Text = objname
        Text.X = TopX + xmax / 2 * scale
        Text.Y = TopY + ymax / 2 * scale
        Text.Scale = 1

        TopView = FreeCAD.activeDocument().addObject('Drawing::FeatureViewPart', 'TopView')
        TopView.Source = obj
        TopView.Direction = (0.0, 0.0, 1)
        TopView.Rotation = 0
        TopView.X = TopX
        TopView.Y = TopY
        TopView.ShowHiddenLines = True
        TopView.Scale = scale
        page.addObject(TopView)
        page.addObject(Text)
        FreeCAD.activeDocument().recompute()
        return obj_l


#####################################
# Dialog Box
#####################################
fields = [['Group Name', 'Unfolding']]
fields.append(['Scale', '1'])

DialogBox = QtGui.QDialog()
DialogBox.resize(250, 250)
DialogBox.setWindowTitle('unfoldBox')
la = QtGui.QVBoxLayout(DialogBox)

# Input fields.
for id_ in range(len(fields)):
    la.addWidget(QtGui.QLabel(fields[id_][0]))
    fields_l.append(QtGui.QLineEdit(fields[id_][1]))
    la.addWidget(fields_l[id_])

scale_check = QtGui.QCheckBox(DialogBox)
scale_check.setObjectName('checkBox')
scale_check.setChecked(True)
la.addWidget(QtGui.QLabel('Scale auto'))
la.addWidget(scale_check)

a3_check = QtGui.QCheckBox(DialogBox)
a3_check.setObjectName('checkBox')
la.addWidget(QtGui.QLabel('A3 Format'))
a3_check.setChecked(False)
la.addWidget(a3_check)

cartridge_check = QtGui.QCheckBox(DialogBox)
cartridge_check.setObjectName('checkBox')
la.addWidget(QtGui.QLabel('Cartridge'))
cartridge_check.setChecked(False)
la.addWidget(cartridge_check)

onedrawing_check = QtGui.QCheckBox(DialogBox)
onedrawing_check.setObjectName('checkBox')
la.addWidget(QtGui.QLabel('Group drawings in page'))
onedrawing_check.setChecked(True)
la.addWidget(onedrawing_check)

sewed_check = QtGui.QCheckBox(DialogBox)
sewed_check.setObjectName('checkBox')
la.addWidget(QtGui.QLabel('Sewed surfaces'))
sewed_check.setChecked(True)
la.addWidget(sewed_check)

box = QtGui.QDialogButtonBox(DialogBox)

box = QtGui.QDialogButtonBox(DialogBox)
box.setOrientation(QtCore.Qt.Horizontal)
box.setStandardButtons(QtGui.QDialogButtonBox.Cancel | QtGui.QDialogButtonBox.Ok)
la.addWidget(box)

QtCore.QObject.connect(box, QtCore.SIGNAL('accepted()'), proceed)
QtCore.QObject.connect(box, QtCore.SIGNAL('rejected()'), close)
QtCore.QMetaObject.connectSlotsByName(DialogBox)
DialogBox.show()