Macro Unfold Box/fr: Difference between revisions

From FreeCAD Documentation
(Updating to match new version of source page)
No edit summary
(2 intermediate revisions by 2 users not shown)
Line 1: Line 1:
<languages/>
<languages/>
<div class="mw-translate-fuzzy">
{{Macro/fr
{{Macro/fr
|Name=Macro Unfold Box
|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.
|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.
|Author=Hervé B.
|Version=1.0
|Version=1.0.1
|Date=2013-09-20
|Date=2020-03-100
|FCVersion=
|Download=[https://www.freecadweb.org/wiki/images/e/e4/Macro_Unfold_Box.png ToolBar Icon]
}}
}}
</div>


==Description==
==Description==
Line 46: Line 47:
'''Macro_unfoldBox.FCMacro'''
'''Macro_unfoldBox.FCMacro'''


<!--NOT CHANGE THE <pre> </pre> BALISE CAUSE THE PIPE IN MACRO CODE CUT THE MACRO-->
<!-- Do not remove the pair of <nowiki> </nowiki> tags because the special symbols won't be parsed correctly -->
{{MacroCode|code=
<pre>
<nowiki>
'''FreeCAD Macro unfoldBox.


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

# Unroll of a ruled surface
import FreeCAD, FreeCADGui, Draft
#####################################
from PySide import QtGui, QtCore
import FreeCAD , FreeCADGui , Part, Draft, math, Drawing , PySide, os
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 691: 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()


</pre>
</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()