Macro Unroll Ruled Surface/it: Difference between revisions
Renatorivo (talk | contribs) (Created page with "=== Opzioni === * Numero di generatrici * Scala manuale o automatica * Formato della pagina: A3, A4 * Tabella (secondo i modelli di FreeCAD) * Raggruppamento dei disegni nell...") |
(Updating to match new version of source page) |
||
(36 intermediate revisions by 4 users not shown) | |||
Line 1: | Line 1: | ||
<languages/> |
|||
{{Macro/it|Icon=Text-x-python|Name=Macro_unrollRuledSurface|Name/it=Macro_unrollRuledSurface|Description=Permette di srotolare le superfici rigate e di disegnarle in una pagina.|Author=Hervé B.}} |
|||
<div class="mw-translate-fuzzy"> |
|||
{{Macro/it |
|||
|Name=Macro Unroll Ruled Surface |
|||
|Translate=Sviluppa superfici rigate |
|||
|Description=Permette di srotolare le superfici rigate e di disegnarle in una pagina. |
|||
|Author=Hervé B. |
|||
|Version=1.0 |
|||
|Date=2013-09-14 |
|||
|Download=[https://www.freecadweb.org/wiki/images/b/ba/Macro_Unroll_Ruled_Surface.png ToolBar icon] |
|||
}} |
|||
</div> |
|||
== Description == |
|||
<div class="mw-translate-fuzzy"> |
|||
== Descrizione == |
|||
Questa macro permette di creare lo sviluppo di una superficie rigata e di disegnarlo in una pagina, e di inserire il disegno in un foglio A3 o A4. |
Questa macro permette di creare lo sviluppo di una superficie rigata e di disegnarlo in una pagina, e di inserire il disegno in un foglio A3 o A4. |
||
</div> |
|||
[[File:Macro_unrollRuledSurface_00.png|480px]] |
|||
<div class="mw-translate-fuzzy"> |
|||
[[File:Macro_unrollRuledSurface_00.png|480px]] |
|||
{{Caption|Macro_unrollRuledSurface}} |
|||
</div> |
|||
== Installation == |
|||
[[File:Macro_unrollRuledSurface_00.png]] |
|||
{{clear}} |
|||
<div class="mw-translate-fuzzy"> |
|||
== Installazione == |
|||
Secondo quanto descritto in [http://forum.freecadweb.org/viewtopic.php?f=17&t=4563&p=35737#p35737 Macro for unrolling ruled surfaces] |
Secondo quanto descritto in [http://forum.freecadweb.org/viewtopic.php?f=17&t=4563&p=35737#p35737 Macro for unrolling ruled surfaces] |
||
Copiare il file del codice nella directory : |
Copiare il file del codice nella directory : |
||
Line 15: | Line 37: | ||
Aggiungere i modelli del foglio : A3_Landscape_Empty.svg A3_Landscape.svg A4_Landscape_Empty.svg A4_Landscape.svg |
Aggiungere i modelli del foglio : A3_Landscape_Empty.svg A3_Landscape.svg A4_Landscape_Empty.svg A4_Landscape.svg |
||
</div> |
|||
See also: [http://forum.freecadweb.org/viewtopic.php?f=17&t=4563&p=35737#p35737 Macro for unrolling ruled surfaces]. |
|||
== Options == |
|||
<div class="mw-translate-fuzzy"> |
|||
=== Opzioni === |
|||
== Opzioni == |
|||
* Numero di generatrici |
* Numero di generatrici |
||
Line 23: | Line 51: | ||
* Tabella (secondo i modelli di FreeCAD) |
* Tabella (secondo i modelli di FreeCAD) |
||
* Raggruppamento dei disegni nella stessa pagina se è possibile. |
* Raggruppamento dei disegni nella stessa pagina se è possibile. |
||
</div> |
|||
[[File: |
[[File:Macro_UnrollRuledSurface_start_form.png|Macro_unrollRuledSurface]] |
||
== Usage == |
|||
# Select ruled surfaces |
|||
# Explode them (cf Draft menu) |
|||
# Select the surfaces |
|||
# Execute the macro |
|||
<div class="mw-translate-fuzzy"> |
|||
=== Python Code === |
|||
== Utlizzo == |
|||
'''Macro_unrollRuledSurface.py''' |
|||
# Selezionare una supeficie in un oggetto |
|||
# Scomporre l'oggetto (menu Draft -> Retrocedere) |
|||
# Selezionare le supefici da sviluppare |
|||
# Eseguire la macro |
|||
</div> |
|||
<span id="Script"></span> |
|||
== Codice == |
|||
<div class="mw-translate-fuzzy"> |
|||
L'ultima versione della macro si trova in [https://github.com/FreeCAD/FreeCAD-macros/blob/master/Drawing/UnrollRuledSurface.FCMacro UnrollRuledSurface.FCMacro] ma il modo più semplice per installare questa macro è attraverso [[Std_AddonMgr/it|Addon manager]] [[File:Std_AddonMgr.svg|24px|Std_AddonMgr]] |
|||
. |
|||
</div> |
|||
ToolBar Icon [[Image:Macro_Unroll_Ruled_Surface.png]] |
|||
'''Macro_unrollRuledSurface.py''' |
|||
<!--DO NOT CHANGE THE <pre> </pre> TAG, code contains pipe or space character which breaks the wiki macro--> |
|||
<syntaxhighlight> |
|||
<pre> |
|||
# -*- coding: utf-8 -*- |
|||
#*************************************************************************** |
#*************************************************************************** |
||
#* * |
#* * |
||
#* Copyright (c) 2013 - DoNovae/Herve BAILLY <hbl13@donovae.com> |
#* Copyright (c) 2013 - DoNovae/Herve BAILLY <hbl13@donovae.com> * |
||
#* Copyright (c) 2022 - heda <heda@fc-forum> * |
|||
#* * |
#* * |
||
#* This program is free software; you can redistribute it and/or modify * |
#* This program is free software; you can redistribute it and/or modify * |
||
Line 58: | Line 103: | ||
#* * |
#* * |
||
#*************************************************************************** |
#*************************************************************************** |
||
__Name__ = 'Unroll Ruled Surface' |
|||
__Comment__ = 'Unroll of a ruled surface and draw it on a page.' |
|||
__Author__ = 'Hervé B., heda' |
|||
__Version__ = '1.1' |
|||
__Date__ = '2022-07-24' |
|||
__License__ = 'LGPL-2.0-or-later' |
|||
__Web__ = 'https://wiki.freecad.org/Macro_Unroll_Ruled_Surface' |
|||
__Wiki__ = 'https://wiki.freecad.org/Macro_Unroll_Ruled_Surface' |
|||
__Icon__ = '' |
|||
__Help__ = ('Select ruled surfaces, Explode them (cf Draft menu), ' |
|||
'Select the surfaces, Execute the macro') |
|||
__Status__ = '' |
|||
__Requires__ = '' |
|||
__Communication__ = '' |
|||
__Files__ = '' |
|||
__doc__ = """ |
|||
##################################### |
|||
select a face, or several and run the macro. |
|||
# Macro UnrollRuledSurface |
|||
a shell/solid needs to be draft/downgraded to get the faces as separate objects |
|||
# Unroll of a ruled surface |
|||
##################################### |
|||
import FreeCAD , FreeCADGui , Part, Draft, math, Drawing , PyQt4, os |
|||
from PyQt4 import QtGui,QtCore |
|||
from FreeCAD import Base |
|||
from unrollRuledSurface.unfoldBox import unfoldBoxClass |
|||
fields_l = [] |
|||
unroll_l = [] |
|||
the macro is intended to unroll lofted faces, |
|||
function beyond that is (in current version) a bonus |
|||
settings are not context aware, all settings not applicable are ignored. |
|||
as example, using autoscaling ignores the scale-value in the form |
|||
v1.1 (2022-07-24) py3/qt5 compat, cosmetic code changes, minor code tweaks, |
|||
used gridlayout for form, added option to skip drawing, |
|||
made new layout engine with techdraw, |
|||
the drawing wb layout engine is now called "legacy" |
|||
v1.0.1 (2019-02-01) - on git |
|||
v1.0 (2013-09-14) - on wiki |
|||
note: |
|||
- unfolding sometimes works and sometimes not |
|||
- not all surfaces are theoretically possible to unroll |
|||
""" |
|||
import os |
|||
from PySide import QtGui, QtCore |
|||
import FreeCAD, FreeCADGui |
|||
import Part, Draft |
|||
Vector = FreeCAD.Base.Vector |
|||
PrintMessage = FreeCAD.Console.PrintMessage |
|||
PrintError = FreeCAD.Console.PrintError |
|||
settings = dict() |
|||
unroll_l = [] |
|||
dwgtpllegacy = 'Mod/Drawing/Templates' |
|||
dwgtpl = 'Mod/TechDraw/Templates' |
|||
##################################### |
|||
##################################### |
|||
# Functions |
|||
##################################### |
|||
##################################### |
|||
##################################### |
##################################### |
||
### Functions |
|||
# Function errorDialog |
|||
##################################### |
##################################### |
||
def errorDialog(msg): |
def errorDialog(msg): |
||
diag = QtGui.QMessageBox(QtGui.QMessageBox.Critical, |
diag = QtGui.QMessageBox(QtGui.QMessageBox.Critical, "Error Message", msg) |
||
diag.setWindowFlags( |
diag.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint) |
||
diag.exec_() |
diag.exec_() |
||
def ending(): |
|||
PrintMessage("UnrollRuledSurface: end.\n") |
|||
PrintMessage("===========================================\n") |
|||
FreeCAD.ActiveDocument.recompute() |
|||
##################################### |
|||
# Function proceed |
|||
##################################### |
|||
def proceed(): |
def proceed(): |
||
QtGui. |
QtGui.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) |
||
PrintMessage("===========================================\n") |
|||
PrintMessage("UnrollRuledSurface: start.\n") |
|||
try: |
try: |
||
sts = lambda s: settings.get(s) |
|||
pts_nbr = float(fields_l[1].text()) |
|||
scale = float(fields_l[2].text()) |
|||
scale_auto = scale_check.isChecked() |
|||
edge0 = edge0_check.isChecked() |
|||
a3 = a3_check.isChecked() |
|||
cartridge = cartridge_check.isChecked() |
|||
onedrawing = onedrawing_check.isChecked() |
|||
FreeCAD.Console.PrintMessage("UnrollRuledSurface.file_name: "+file_name+"\n") |
|||
FreeCAD.Console.PrintMessage("UnrollRuledSurface.pts_nbr: "+str(pts_nbr)+"\n") |
|||
FreeCAD.Console.PrintMessage("UnrollRuledSurface.scale: "+str(scale)+"\n") |
|||
FreeCAD.Console.PrintMessage("UnrollRuledSurface.scale_check: "+str(scale_auto)+"\n") |
|||
FreeCAD.Console.PrintMessage("UnrollRuledSurface.edge0_check: "+str(edge0)+"") |
|||
FreeCAD.Console.PrintMessage("UnrollRuledSurface.a3_check: "+str(a3)+"\n") |
|||
FreeCAD.Console.PrintMessage("UnrollRuledSurface.cartridge: "+str(cartridge)+"\n") |
|||
FreeCAD.Console.PrintMessage("UnrollRuledSurface.onedrawing: "+str(onedrawing)+"\n") |
|||
except: |
|||
msg="UnrollRuledSurface: wrong inputs...\n" |
|||
FreeCAD.Console.PrintError(msg) |
|||
errorDialog(msg) |
|||
file_name = sts("fname").text() |
|||
QtGui.qApp.restoreOverrideCursor() |
|||
pts_nbr = float(sts("dpts").text()) |
|||
DialogBox.hide() |
|||
makedwg = sts("mkdwg").isChecked() |
|||
unrollRS=unrollRuledSurface( file_name , pts_nbr , edge0 ) |
|||
leglay = sts("legacylayout").isChecked() |
|||
# |
|||
scale = float(sts("scale").text()) # ignored if autoscale is set |
|||
# Get selection |
|||
scale_auto = sts("autoscale").isChecked() |
|||
# |
|||
edge0 = sts("edge").checkedId() == -2 |
|||
sel=FreeCADGui.Selection.getSelection() |
|||
a3 = sts("papersize").checkedId() == -3 |
|||
faceid=0 |
|||
cartridge = sts("cartridge").isChecked() |
|||
objnames_l=[] |
|||
onedrawing = sts("groupdwg").isChecked() |
|||
objnames0_l=[] |
|||
grp=FreeCAD.activeDocument().addObject("App::DocumentObjectGroup", str(file_name)+"_objs") |
|||
PrintMessage("UnrollRuledSurface.file_name: {}\n".format(file_name)) |
|||
for objid in range( sel.__len__() ): |
|||
PrintMessage("UnrollRuledSurface.pts_nbr: {}\n".format(pts_nbr)) |
|||
shape=sel[objid].Shape |
|||
PrintMessage("UnrollRuledSurface.edge0: {}\n".format(edge0)) |
|||
faces=shape.Faces |
|||
PrintMessage("UnrollRuledSurface.makedwg: {}\n".format(makedwg)) |
|||
for id in range( faces.__len__() ): |
|||
PrintMessage("UnrollRuledSurface.leglay: {}\n".format(leglay)) |
|||
PrintMessage("UnrollRuledSurface.scale_check: {}\n".format(scale_auto)) |
|||
if faces.__len__() > 1: |
|||
PrintMessage("UnrollRuledSurface.scale: {}\n".format(scale)) |
|||
name=sel[objid].Name+".faces "+str(id) |
|||
PrintMessage("UnrollRuledSurface.a3_check: {}\n".format(a3)) |
|||
else: |
|||
PrintMessage("UnrollRuledSurface.cartridge: {}\n".format(cartridge)) |
|||
name=sel[objid].Name |
|||
PrintMessage("UnrollRuledSurface.onedrawing: {}\n".format(onedrawing)) |
|||
obj=unrollRS.unroll(faces[id],name) |
|||
except: |
|||
obj.ViewObject.Visibility=False |
|||
msg = "UnrollRuledSurface: wrong inputs...\n" |
|||
grp.addObject(obj) |
|||
PrintError(msg) |
|||
objnames_l.append( [ obj , name ] ) |
|||
errorDialog(msg) |
|||
objnames0_l.append( [ sel[objid] , name ] ) |
|||
QtGui.QApplication.restoreOverrideCursor() |
|||
faceid=faceid+1 |
|||
DialogBox.hide() |
|||
id=0 |
|||
ending() |
|||
while objnames_l.__len__() > 0: |
|||
return |
|||
draw=Drawing2d( scale, scale_auto , a3 , cartridge , onedrawing,str(file_name)+"_page"+str(id) ) |
|||
objnames_l=draw.all( objnames_l ) |
|||
QtGui.QApplication.restoreOverrideCursor() |
|||
id=id+1 |
|||
DialogBox.hide() |
|||
FreeCAD.Console.PrintMessage("UnrollRuledSurface: obj_l= "+str(objnames_l.__len__())+"\n") |
|||
unrollRS = unrollRuledSurface(file_name, pts_nbr, edge0) |
|||
## Get selection |
|||
sel = FreeCADGui.Selection.getSelection() |
|||
if not sel: |
|||
PrintMessage("UnrollRuledSurface: no selection...\n") |
|||
ending() |
|||
return |
|||
faceid = 0 |
|||
objnames_l, objnames0_l = [], [] |
|||
grp = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", |
|||
"{}_objs".format(file_name)) |
|||
for objid, obji in enumerate(sel): |
|||
shape = obji.Shape |
|||
faces = shape.Faces |
|||
for idx in range(len(faces)): |
|||
msg = "UnrollRuledSurface.proceed: ObjId = {}, faceId = {}\n" |
|||
PrintMessage(msg.format(objid, faceid)) |
|||
name = obji.Name |
|||
if len(faces) > 1: |
|||
name = "{}.face_{}".format(name, idx) |
|||
obj = unrollRS.unroll(faces[idx], name) |
|||
obj.ViewObject.Visibility = not makedwg |
|||
grp.addObject(obj) |
|||
objnames_l.append([obj, name]) |
|||
objnames0_l.append([obji, name]) |
|||
faceid += 1 |
|||
if not makedwg: |
|||
ending() |
|||
return |
|||
if leglay: |
|||
idx = 0 |
|||
while len(objnames_l) > 0: |
|||
draw = Drawing2dLegacy(scale, scale_auto, a3, |
|||
cartridge, onedrawing, |
|||
"{}_page{:02}".format(file_name, idx)) |
|||
objnames_l = draw.all2d(objnames_l) |
|||
idx += 1 |
|||
msg = "UnrollRuledSurface: obj_l = {}\n" |
|||
PrintMessage(msg.format(len(objnames_l))) |
|||
else: |
|||
draw = Drawing2d(scale, scale_auto, a3, cartridge) |
|||
n = 4 if onedrawing else 1 |
|||
chunks = [objnames_l[i:i + n] for i in range(0, len(objnames_l), n)] |
|||
for i, chunk in enumerate(chunks, start=1): |
|||
draw.drawpage(chunk, "{}_page{:02}".format(file_name, i)) |
|||
ending() |
|||
FreeCAD.Console.PrintMessage("UnrollRuledSurface: end.\n") |
|||
FreeCAD.Console.PrintMessage("===========================================\n") |
|||
##################################### |
|||
# Function close |
|||
##################################### |
|||
def close(): |
def close(): |
||
DialogBox.hide() |
DialogBox.hide() |
||
def getType(obj): |
|||
return type(obj).__name__ |
|||
##################################### |
|||
# Class unrollRuledSurface |
|||
# - file_name : ouput file |
|||
# - pts_nbr : nbr point of |
|||
# discretization |
|||
##################################### |
|||
class unrollRuledSurface: |
class unrollRuledSurface: |
||
""" |
|||
def __init__( self, file_name, pts_nbr , edge0 ): |
|||
unroll ruled surfaces |
|||
self.doc = FreeCAD.activeDocument() |
|||
:file_name: ouput file |
|||
:pts_nbr: nbr point of discretization |
|||
""" |
|||
self.edge0 = edge0 |
|||
def __init__(self, file_name, pts_nbr, edge0): |
|||
self.doc = FreeCAD.ActiveDocument |
|||
self.file_name = file_name |
|||
self.pts_nbr = int(pts_nbr) |
|||
self.edge0 = edge0 |
|||
msg = "UnrollRuledSurface.unroll - file_name: {}, pts_nbr: {}\n" |
|||
PrintMessage(msg.format(file_name, pts_nbr)) |
|||
def discretize(self, curve): |
|||
##################################### |
|||
"""discretize a curve""" |
|||
if getType(curve) in ('GeomLineSegment', 'GeomCircle'): |
|||
##################################### |
|||
sd = curve.discretize(self.pts_nbr) |
|||
elif getType(curve) == 'GeomBSplineCurve': |
|||
nodes = curve.getPoles() |
|||
spline = Part.BSplineCurve() |
|||
elif type(curve).__name__=='GeomBSplineCurve': |
|||
spline.buildFromPoles(nodes) |
|||
sd = spline.discretize(self.pts_nbr) |
|||
else: |
|||
spline.buildFromPoles( nodes ) |
|||
sd= |
sd = curve.discretize(self.pts_nbr) |
||
return sd |
|||
elif type(curve).__name__=='GeomCircle': |
|||
sd=curve.discretize( self.pts_nbr ) |
|||
else: |
|||
sd=curve.discretize( self.pts_nbr ) |
|||
return sd |
|||
def nbpoles(self, curve): |
|||
##################################### |
|||
"""find number of poles for a curve""" |
|||
# Function nbpoles |
|||
if getType(curve) == 'GeomLineSegment': |
|||
##################################### |
|||
nbpol=1 |
|||
def nbpoles(self,curve): |
|||
elif getType(curve) == 'GeomBSplineCurve': |
|||
nbpol= |
nbpol=curve.NbPoles |
||
elif |
elif getType(curve) == 'GeomCircle': |
||
nbpol= |
nbpol=2 |
||
elif |
elif getType(curve) == 'GeomBezierCurve': |
||
nbpol= |
nbpol=4 |
||
else: |
|||
elif type(curve).__name__=='GeomBezierCurve': |
|||
nbpol= |
nbpol=0 |
||
else: |
|||
nbpol=0 |
|||
FreeCAD.Console.PrintMessage("UnrollRulrdSurface.nbpole {:s} = {:d}\n".format(type(curve).__name__,nbpol)) |
|||
return nbpol |
|||
msg = "UnrollRulrdSurface.nbpole {:s} = {:d}\n" |
|||
##################################### |
|||
PrintMessage(msg.format(getType(curve), nbpol)) |
|||
# Function unroll |
|||
return nbpol |
|||
##################################### |
|||
# Unroll of a face |
|||
# composed of 2 or 4 edges |
|||
##################################### |
|||
def unroll(self,face,name): |
|||
FreeCAD.Console.PrintMessage("UnrollRuledSurface.unroll: Ege Nbr= "+str( face.Edges.__len__())+"\n") |
|||
if face.Edges.__len__() == 2: |
|||
e1=face.Edges[0] |
|||
e2=face.Edges[1] |
|||
sd1=e1.Curve.discretize( self.pts_nbr ) |
|||
sd2=e2.Curve.discretize( self.pts_nbr ) |
|||
elif face.Edges.__len__() == 3: |
|||
e1=face.Edges[0] |
|||
e2=face.Edges[2] |
|||
sd1=e1.Curve.discretize( self.pts_nbr ) |
|||
sd2=e2.Curve.discretize( self.pts_nbr ) |
|||
else: |
|||
E0=face.Edges[0] |
|||
E1=face.Edges[1] |
|||
E2=face.Edges[2] |
|||
E3=face.Edges[3] |
|||
# |
|||
# Choose more complexe curve as edge |
|||
# |
|||
nbpol0=self.nbpoles(E0.Curve) |
|||
nbpol1=self.nbpoles(E1.Curve) |
|||
nbpol2=self.nbpoles(E2.Curve) |
|||
nbpol3=self.nbpoles(E3.Curve) |
|||
FreeCAD.Console.PrintMessage("UnrollRuledSurface.unroll: nbpol0= {:d}, nbpol1= {:d}, nbpol2= {:d}, nbpol3= {:d}\n".format(nbpol0,nbpol1,nbpol2,nbpol3)) |
|||
def unroll(self, face, name): |
|||
if self.edge0: |
|||
"""unrolls a face composed of 2 to 4 edges""" |
|||
e1=E0 |
|||
nbredges = len(face.Edges) |
|||
msg = "UnrollRuledSurface.unroll: Edge Nbr = {}\n" |
|||
PrintMessage(msg.format(nbredges)) |
|||
v0=v[0] |
|||
v1=v[self.pts_nbr-1] |
|||
else: |
|||
e1=E1 |
|||
e2=E3 |
|||
v=self.discretize( E2 ) |
|||
v0=v[0] |
|||
v1=v[self.pts_nbr-1] |
|||
if nbredges == 2: |
|||
sd1=self.discretize( e1 ) |
|||
e1, e2 = face.Edges |
|||
sd1 = e1.Curve.discretize(self.pts_nbr) |
|||
# |
|||
sd2 = e2.Curve.discretize(self.pts_nbr) |
|||
# Reverse if curves cross over |
|||
# |
|||
if not ( sd2[0].__eq__( v0 ) or not sd2[0].__eq__( v1 ) ): |
|||
sd2.reverse() |
|||
elif nbredges == 3: |
|||
# |
|||
e1, _, e2 = face.Edges |
|||
# Create a polygon object and set its nodes |
|||
sd1 = e1.Curve.discretize(self.pts_nbr) |
|||
# |
|||
sd2 = e2.Curve.discretize(self.pts_nbr) |
|||
devlxy_l=self.devlxyz( sd1 , sd2 ) |
|||
FreeCAD.Console.PrintMessage("UnrollRuledSurface.unroll: size devlxy_l: "+str( devlxy_l.__len__())+"\n") |
|||
p=self.doc.addObject("Part::Polygon",name) |
|||
p.Nodes=devlxy_l |
|||
self.doc.recompute() |
|||
FreeCADGui.SendMsgToActiveView("ViewFit") |
|||
return p |
|||
else: |
|||
##################################### |
|||
E0, E1, E2, E3, *_ = face.Edges |
|||
# Function vect_copy |
|||
## Choose more complexe curve as edge |
|||
# - vect: |
|||
nbpol0 = self.nbpoles(E0.Curve) |
|||
# - return copy of vector |
|||
nbpol1 = self.nbpoles(E1.Curve) |
|||
##################################### |
|||
nbpol2 = self.nbpoles(E2.Curve) |
|||
def vect_copy( self, vect): |
|||
nbpol3 = self.nbpoles(E3.Curve) |
|||
v= vect.add( FreeCAD.Base.Vector(0,0,0) ) |
|||
msg = ("UnrollRuledSurface.unroll: nbpol0= {:d}, nbpol1= {:d}," |
|||
return v |
|||
" nbpol2= {:d}, nbpol3= {:d}\n") |
|||
PrintMessage(msg.format(nbpol0, nbpol1, nbpol2, nbpol3)) |
|||
if self.edge0: |
|||
##################################### |
|||
e1, e2 = E0, E2 |
|||
# Function vect_cos |
|||
v = self.discretize(E1) |
|||
# - vect1,2: |
|||
v0, v1 = v[0], v[self.pts_nbr-1] |
|||
# - return cos angle between |
|||
else: |
|||
e1, e2 = E1, E3 |
|||
##################################### |
|||
v = self.discretize(E2) |
|||
def vect_cos( self , vect1, vect2 ): |
|||
v0, v1 = v[0], v[self.pts_nbr-1] |
|||
cosalp=vect1.dot(vect2)/vect1.Length/vect2.Length |
|||
return cosalp |
|||
##################################### |
|||
# Function vect_sin |
|||
# - vect1,2: |
|||
# - return abs(sin) angle between |
|||
# 2 vectors |
|||
##################################### |
|||
def vect_sin( 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 |
|||
sinalp=v.Length/vect1.Length/vect2.Length |
|||
return sinalp |
|||
sd1 = self.discretize(e1) |
|||
sd2 = self.discretize(e2) |
|||
##################################### |
|||
## Reverse if curves cross over |
|||
# Function devlxyz |
|||
if not (sd2[0].__eq__(v0) or not sd2[0].__eq__(v1)): |
|||
# - vect1,2: 2 edges of the shape |
|||
sd2.reverse() |
|||
##################################### |
|||
# unroll of a face |
|||
# composed of 4 edges |
|||
##################################### |
|||
def devlxyz( self , vect1 , vect2 ): |
|||
# |
|||
# Init |
|||
# |
|||
if ( vect1.__len__() != vect2.__len__()) or ( vect1.__len__() != self.pts_nbr ) or ( vect2.__len__() != self.pts_nbr ): |
|||
msg="UnrollRuledSurface.devlxyz: incompatility of sizes vect1 , vect2, pts_nbr- "+str( vect1.__len__())+" , "+str( vect2.__len__())+" , "+str( self.pts_nbr )+"\n" |
|||
FreeCAD.Console.PrintError(msg) |
|||
errorDialog(msg) |
|||
## Create a polygon object and set its nodes |
|||
devlxy_l=[] |
|||
devlxy_l = self.devlxyz(sd1, sd2) |
|||
devl1xy_l=[] |
|||
msg = "UnrollRuledSurface.unroll: size devlxy_l: {}\n" |
|||
devl2xy_l=[] |
|||
PrintMessage(msg.format(len(devlxy_l))) |
|||
errormax=0.0 |
|||
p = self.doc.addObject("Part::Polygon", name) |
|||
# |
|||
p.Nodes = devlxy_l |
|||
# Init unroll |
|||
self.doc.recompute() |
|||
# AB |
|||
FreeCADGui.ActiveDocument.ActiveView.fitAll() |
|||
# |
|||
return p |
|||
a1b1=vect2[0].sub(vect1[0]) |
|||
oa1=FreeCAD.Vector(0,0,0) |
|||
devl1xy_l.append( oa1 ) #A1 |
|||
ob1=FreeCAD.Vector(a1b1.Length,0,0) |
|||
devl2xy_l.append( ob1 ) #B1 |
|||
#self.draw_line( devl1xy_l[0] , devl2xy_l[0] ) |
|||
#self.draw_line( vect1[0] , vect2[0] ) |
|||
for j in range( 1 , self.pts_nbr ) : |
|||
# |
|||
# AB |
|||
# |
|||
ab=vect2[j-1].sub(vect1[j-1]) |
|||
#self.draw_line( vect1[j-1] , vect2[j-1] ) |
|||
# |
|||
# AC |
|||
# |
|||
ac=vect1[j].sub(vect1[j-1]) |
|||
# |
|||
# BD |
|||
# |
|||
bd=vect2[j].sub(vect2[j-1]) |
|||
# |
|||
# CD |
|||
# |
|||
cd=vect2[j].sub(vect1[j]) |
|||
# |
|||
# A1B1 in unroll plan |
|||
# |
|||
a1b1=devl2xy_l[j-1].sub(devl1xy_l[j-1]) |
|||
a1b1n=self.vect_copy(a1b1) |
|||
a1b1n.normalize() |
|||
a1b1on=FreeCAD.Vector(-a1b1n.y,a1b1n.x,0) |
|||
# |
|||
# A1C1 |
|||
# |
|||
cosalp=self.vect_cos( ab , ac ) |
|||
sinalp=self.vect_sin( ab , ac ) |
|||
a1c1=self.vect_copy(a1b1n) |
|||
a1c1.multiply(cosalp*ac.Length) |
|||
v=self.vect_copy(a1b1on) |
|||
v.multiply(sinalp*ac.Length) |
|||
a1c1=a1c1.add(v) |
|||
#FreeCAD.Console.PrintMessage("UnrollRuledSurface.alp a1b1: "+str(a1b1n.getAngle(a1b1on))+"\n") |
|||
#FreeCAD.Console.PrintMessage("UnrollRuledSurface.alp oc1: "+str(a1b1n.getAngle(a1c1)-alp)+"\n") |
|||
#FreeCAD.Console.PrintMessage("UnrollRuledSurface.length oc1: "+str(a1c1.Length-ac.Length)+"\n") |
|||
oa1=self.vect_copy(devl1xy_l[j-1]) |
|||
oc1=oa1.add(a1c1) |
|||
devl1xy_l.append(oc1) |
|||
# |
|||
# B1D1 |
|||
# |
|||
cosalp=self.vect_cos( ab , bd ) |
|||
sinalp=self.vect_sin( ab , bd ) |
|||
b1d1=self.vect_copy(a1b1n) |
|||
b1d1.multiply(cosalp*bd.Length) |
|||
v=self.vect_copy(a1b1on) |
|||
v.multiply(sinalp*bd.Length) |
|||
b1d1=b1d1.add(v) |
|||
#FreeCAD.Console.PrintMessage("UnrollRuledSurface.alp od1: "+str(b1a1n.getAngle(b1d1)-alp)+"\n") |
|||
#FreeCAD.Console.PrintMessage("UnrollRuledSurface.length od1: "+str(b1d1.Length-bd.Length)+"\n") |
|||
ob1=self.vect_copy(devl2xy_l[j-1]) |
|||
od1=ob1.add(b1d1) |
|||
devl2xy_l.append(od1) |
|||
# |
|||
# Draw generatrice |
|||
# |
|||
#self.draw_line( devl1xy_l[j] , devl2xy_l[j] ) |
|||
c1d1=devl2xy_l[j].sub( devl1xy_l[j] ) |
|||
if ab.Length <> 0 : |
|||
errormax=max(errormax,math.fabs(ab.Length-c1d1.Length)/ab.Length) |
|||
# |
|||
# The end |
|||
# |
|||
FreeCAD.Console.PrintMessage("UnrollRuledSurface Error cd,c1d1: {:.1f} %\n".format(errormax*100)) |
|||
def vect_copy(self, vect): |
|||
# |
|||
"""returns copy of vector""" |
|||
# Close polygone |
|||
return vect.add(Vector()) |
|||
# |
|||
devlxy_l = devl1xy_l |
|||
devl2xy_l.reverse() |
|||
devlxy_l.extend( devl2xy_l ) |
|||
v=FreeCAD.Vector(0,0,0) |
|||
devlxy_l.append( v ) |
|||
def vect_cos(self, vect1, vect2): |
|||
return devlxy_l |
|||
"""returns cosine angle between 2 vectors""" |
|||
return vect1.dot(vect2) / vect1.Length / vect2.Length |
|||
def vect_sin(self, vect1, vect2): |
|||
"""returns absolute sinus angle between 2 vectors""" |
|||
v = Vector() |
|||
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.Length / vect1.Length / vect2.Length |
|||
def devlxyz(self, vect1, vect2): |
|||
##################################### |
|||
""" |
|||
# Function draw_line |
|||
unrolls a face composed of 4 edges |
|||
# - vect0,1: two points |
|||
args: vect1, vect2 --> 2 edges of the shape |
|||
##################################### |
|||
returns: dvlxy |
|||
def draw_line( self , vect0 , vect1 ): |
|||
""" |
|||
lenv1, lenv2 = len(vect1), len(vect2) |
|||
l.StartPoint=vect0 |
|||
if lenv1 != lenv2 or lenv1 != self.pts_nbr or lenv2 != self.pts_nbr: |
|||
l.EndPoint=vect1 |
|||
msg = ("UnrollRuledSurface.devlxyz: incompatility of sizes vect1," |
|||
self.doc.addObject("Part::Feature","Line").Shape=l.toShape() |
|||
" vect2, pts_nbr: ({}, {}, {})\n") |
|||
PrintError(msg.format(lenv1, lenv2, self.pts_nbr)) |
|||
errorDialog(msg) |
|||
devlxy_l, devl1xy_l, devl2xy_l = [], [], [] |
|||
errormax = 0.0 |
|||
## Init unroll |
|||
## AB |
|||
a1b1 = vect2[0].sub(vect1[0]) |
|||
oa1 = Vector(0, 0, 0) |
|||
devl1xy_l.append(oa1) #A1 |
|||
ob1 = Vector(a1b1.Length, 0, 0) |
|||
devl2xy_l.append(ob1) #B1 |
|||
#self.draw_line(devl1xy_l[0], devl2xy_l[0]) |
|||
#self.draw_line(vect1[0], vect2[0]) |
|||
for j in range(1, self.pts_nbr): |
|||
## AB |
|||
ab = vect2[j-1].sub(vect1[j-1]) |
|||
#self.draw_line(vect1[j-1], vect2[j-1]) |
|||
## AC |
|||
ac = vect1[j].sub(vect1[j-1]) |
|||
## BD |
|||
bd = vect2[j].sub(vect2[j-1]) |
|||
## CD |
|||
cd = vect2[j].sub(vect1[j]) |
|||
## A1B1 in unroll plan |
|||
a1b1 = devl2xy_l[j-1].sub(devl1xy_l[j-1]) |
|||
a1b1n = self.vect_copy(a1b1) |
|||
a1b1n.normalize() |
|||
a1b1on = Vector(-a1b1n.y, a1b1n.x, 0) |
|||
## A1C1 |
|||
cosalp = self.vect_cos(ab, ac) |
|||
sinalp = self.vect_sin(ab, ac) |
|||
a1c1 = self.vect_copy(a1b1n) |
|||
a1c1.multiply(cosalp * ac.Length) |
|||
v = self.vect_copy(a1b1on) |
|||
v.multiply(sinalp * ac.Length) |
|||
a1c1 = a1c1.add(v) |
|||
oa1 = self.vect_copy(devl1xy_l[j-1]) |
|||
oc1 = oa1.add(a1c1) |
|||
devl1xy_l.append(oc1) |
|||
## B1D1 |
|||
cosalp = self.vect_cos(ab, bd) |
|||
sinalp = self.vect_sin(ab, bd) |
|||
b1d1 = self.vect_copy(a1b1n) |
|||
b1d1.multiply(cosalp * bd.Length) |
|||
v = self.vect_copy(a1b1on) |
|||
v.multiply(sinalp * bd.Length) |
|||
b1d1 = b1d1.add(v) |
|||
ob1 = self.vect_copy(devl2xy_l[j-1]) |
|||
od1 = ob1.add(b1d1) |
|||
devl2xy_l.append(od1) |
|||
## Draw generatrice |
|||
#self.draw_line(devl1xy_l[j], devl2xy_l[j]) |
|||
c1d1 = devl2xy_l[j].sub(devl1xy_l[j]) |
|||
if ab.Length != 0: |
|||
abl = ab.Length |
|||
errormax = max(errormax, abs(abl - c1d1.Length) / abl) |
|||
msg = "UnrollRuledSurface Error cd,c1d1: {:.1f} %\n" |
|||
PrintMessage(msg.format(errormax*100)) |
|||
## Close polygone |
|||
devlxy_l = devl1xy_l |
|||
devl2xy_l.reverse() |
|||
devlxy_l.extend(devl2xy_l) |
|||
v = Vector() |
|||
devlxy_l.append(v) |
|||
return devlxy_l |
|||
def draw_line(self, vect0, vect1): |
|||
"""draws a Part.Line between vect0 & vect1""" |
|||
l = Part.LineSegment() |
|||
l.StartPoint = vect0 |
|||
l.EndPoint = vect1 |
|||
self.doc.addObject("Part::Feature", "Line").Shape = l.toShape() |
|||
class Scale: |
|||
"""keeps autoscaling to integers""" |
|||
def __init__(self, scale): |
|||
self.scale = scale if scale >= 1 else 1 / scale |
|||
self.scale = int(self.scale) |
|||
self.inverted = scale >= 1 |
|||
def get(self): |
|||
if self.inverted: |
|||
return self.scale, '{}:1'.format(self.scale) |
|||
else: |
|||
return 1/self.scale, '1:{}'.format(self.scale) |
|||
##################################### |
|||
# Class Drawing2d |
|||
# -obj_l: listes of object |
|||
# -topxh1 |
|||
# -topyh1 |
|||
# -topxv1 |
|||
# -topyv1 |
|||
# -topxvmax1 |
|||
# -topyvmax1 |
|||
# -topxvmax1 |
|||
# -topyvmax1 |
|||
##################################### |
|||
class Drawing2d: |
class Drawing2d: |
||
""" |
|||
##################################### |
|||
TechDraw wb layout engine, diffeent logic compared to drawing wb layout. |
|||
# Function __init__ |
|||
serves basic purpose... |
|||
""" |
|||
# - scale_auto |
|||
def __init__(self, scale, scale_auto, a3, cartridge): |
|||
# - a3 |
|||
# - cartridge |
|||
# - onedrawing |
|||
##################################### |
|||
def __init__( self, scale , scale_auto , a3 , cartridge , onedrawing , page_str ): |
|||
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.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.name=page_str |
|||
self.a3 = a3 |
|||
##################################### |
|||
self.scale = scale |
|||
# Function newPage |
|||
self.scale_auto = scale_auto |
|||
##################################### |
|||
self.cartridge = cartridge |
|||
if a3: |
|||
freecad_dir=os.getenv('HOME')+"/.FreeCAD/Mod/unrollRuledSurface" |
|||
self.WH = 420, 297 |
|||
page = FreeCAD.activeDocument().addObject('Drawing::FeaturePage', self.name ) |
|||
if self.a3: |
|||
if self.cartridge: |
|||
page.Template = freecad_dir+'/A3_Landscape.svg' |
|||
else: |
else: |
||
self.WH = 297, 210 |
|||
self.doc = FreeCAD.ActiveDocument |
|||
else: |
|||
if self.cartridge: |
|||
page.Template = freecad_dir+'/A4_Landscape.svg' |
|||
def _mkquadrants(self, nbr): |
|||
"""centers of quadrants w/o margin""" |
|||
w, h = self.WH |
|||
w2, h2 = w/2, h/2 |
|||
w4, h4 = w/4, h/4 |
|||
q = {1: [[w, h], [[w2, h2]]], |
|||
2: [[w2, h], [[w2 - w4, h2], [w2 + w4, h2]]], |
|||
3: [[w2, h2], [[w2 - w4, h2 + h4], [w2 + w4, h2 + h4], |
|||
[w2 - w4, h2 - h4]]], |
|||
4: [[w2, h2], [[w2 - w4, h2 + h4], [w2 + w4, h2 + h4], |
|||
[w2 - w4, h2 - h4], [w2 + w4, h2 - h4]]]} |
|||
return q.get(nbr) |
|||
def newPage(self, doc, page_name): |
|||
freecad_dir = os.path.join(FreeCAD.getResourceDir(), dwgtpl) |
|||
page = doc.addObject('TechDraw::DrawPage', page_name) |
|||
template = self.doc.addObject('TechDraw::DrawSVGTemplate', 'Template') |
|||
size = 'A3' if self.a3 else 'A4' |
|||
frame = 'TD' if self.cartridge else '_blank' |
|||
template.Template = freecad_dir + '/{}_Landscape{}.svg'.format(size, frame) |
|||
page.Template = template |
|||
return page |
|||
def drawpage(self, faces, page_name): |
|||
"""max 4 per page, simple layout with halfs or quadrants""" |
|||
page = self.newPage(self.doc, page_name) |
|||
[W, H], ll = self._mkquadrants(len(faces)) |
|||
for [face, name], [x0, y0] in zip(faces, ll): |
|||
bb = face.Shape.BoundBox |
|||
xr, yr = W / bb.XLength, H / bb.YLength |
|||
adjust = 0.7 if self.cartridge else 0.85 |
|||
scale = min(xr, yr) * adjust if self.scale_auto else self.scale |
|||
scale, scr = Scale(scale).get() |
|||
bb.scale(scale, scale, 1) # unrolled faces in xy-plane |
|||
TopView = self.doc.addObject('TechDraw::DrawViewPart', name + ' view') |
|||
page.addView(TopView) |
|||
TopView.Source = face |
|||
TopView.Direction = (0, 0, 1) |
|||
TopView.XDirection = (1, 0, 0) |
|||
TopView.Scale = scale |
|||
TopView.X = x0 |
|||
TopView.Y = y0 |
|||
Text = self.doc.addObject('TechDraw::DrawViewAnnotation', name + ' txt') |
|||
page.addView(Text) |
|||
Text.Text = '{} [{}]'.format(name, scr) |
|||
Text.recompute() # for size |
|||
Text.X = x0 |
|||
yt = y0 - bb.YLength / 2 - Text.TextSize.Value * 1.5 |
|||
Text.Y = max(1, yt) |
|||
self.doc.recompute() |
|||
page.ViewObject.doubleClicked() |
|||
FreeCADGui.runCommand('TechDraw_ToggleFrame', 0) |
|||
class Drawing2dLegacy: |
|||
""" |
|||
makes 2d drawing with Drawing wb (original layout engine, now legacy) |
|||
- obj_l: list of objects |
|||
""" |
|||
## untouched logic in v1.1 |
|||
def __init__(self, scale, scale_auto, a3, cartridge, onedrawing, page_str): |
|||
self.TopX_H = self.TopY_H = 0 |
|||
self.TopX_V = self.TopY_V = 0 |
|||
self.TopX_Hmax = self.TopY_Hmax = 0 |
|||
self.TopX_Vmax = self.TopY_Vmax = 0 |
|||
self.a3 = a3 |
|||
self.scale = scale |
|||
self.scale_auto = scale_auto |
|||
self.cartridge = cartridge |
|||
self.onedrawing = onedrawing |
|||
self.marge = 6 |
|||
if self.a3: |
|||
self.L, self.H = 420, 297 |
|||
else: |
else: |
||
self.L, self.H = 297, 210 |
|||
self.name = page_str |
|||
return page |
|||
def newPage(self): |
|||
freecad_dir = os.path.join(FreeCAD.getResourceDir(), dwgtpllegacy) |
|||
doc = FreeCAD.ActiveDocument |
|||
page = doc.addObject('Drawing::FeaturePage', self.name) |
|||
size = 'A3' if self.a3 else 'A4' |
|||
frame = '' if self.cartridge else '_plain' |
|||
page.Template = freecad_dir + '/{}_Landscape{}.svg'.format(size, frame) |
|||
return page |
|||
##################################### |
|||
# Function all |
|||
##################################### |
|||
def all( self, objname_l ): |
|||
obj1_l=[] |
|||
for objid in range( objname_l.__len__() ): |
|||
if objid == 0 or not self.onedrawing: |
|||
page = self.newPage() |
|||
obj1_l.extend( self.done( objid , objname_l[objid] )) |
|||
return obj1_l |
|||
def all2d(self, objname_l): |
|||
##################################### |
|||
obj1_l = [] |
|||
# Function all |
|||
for objid in range(len(objname_l)): |
|||
##################################### |
|||
if objid == 0 or not self.onedrawing: |
|||
def done( self, id , objname ): |
|||
page = self.newPage() |
|||
# |
|||
obj1_l.extend(self.done(objid, objname_l[objid])) |
|||
# Init |
|||
return obj1_l |
|||
# |
|||
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 |
|||
def done(self, idx, objname): |
|||
scale=min((self.L-4*self.marge)/xmax,(self.H-4*self.marge)/ymax) |
|||
obj_l = [] |
|||
obj, objname = objname |
|||
marge = self.marge |
|||
bb = obj.Shape.BoundBox |
|||
xmax = bb.XMax - bb.XMin |
|||
ymax = bb.YMax - bb.YMin |
|||
if ymax > xmax: |
|||
Draft.rotate(obj, 90) |
|||
Draft.move(obj, Vector(-bb.XMin, -bb.YMin, 0)) |
|||
xmax = bb.XMax - bb.XMin |
|||
ymax = bb.YMax - bb.YMin |
|||
scale = min((self.L-4 * marge) / xmax, (self.H-4 * marge) / ymax) |
|||
FreeCAD.Console.PrintMessage("UnrollRuledSurface.drawing: scale= {:.2f}\n".format(scale)) |
|||
if (not self.scale_auto) or self.onedrawing: |
|||
scale = self.scale |
|||
# |
|||
# 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.Shape.BoundBox.XMin , -obj.Shape.BoundBox.YMin , 0)) |
|||
x0=xmax;xmax=ymax,ymax=x0 |
|||
self.TopX_V=max(self.TopX_Vmax, self.TopX_V) |
|||
TopX=self.TopX_V |
|||
TopY=self.TopY_V |
|||
self.TopX_V = self.TopX_V + xmax * scale + self.marge |
|||
self.TopY_Vmax=max( self.TopY_Vmax , self.TopY_V + ymax * scale + self.marge ) |
|||
else: |
|||
obj_l.append( [ obj , name ] ) |
|||
return obj_l |
|||
PrintMessage("UnrollRuledSurface.drawing: scale= {:.2f}\n".format(scale)) |
|||
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.Shape.BoundBox.XMin , -obj.Shape.BoundBox.YMin , 0)) |
|||
x0=xmax;xmax=ymax,ymax=x0 |
|||
TopX=self.TopX_V |
|||
TopY=self.TopY_V |
|||
self.TopX_V = self.TopX_V + xmax * scale + self.marge |
|||
self.TopY_Vmax=max( self.TopY_Vmax , self.TopY_V + ymax * scale + self.marge ) |
|||
else: |
|||
obj_l.append( [ obj , objname ] ) |
|||
return obj_l |
|||
page=FreeCAD.activeDocument().getObject(self.name ) |
|||
if idx == 0 or not self.onedrawing: |
|||
Text=FreeCAD.activeDocument().addObject('Drawing::FeatureViewAnnotation', objname+"_txt") |
|||
PrintMessage("Drawing2d: init\n") |
|||
Text.Text=objname |
|||
TopX = self.TopX_H = marge * 2 |
|||
Text.X=TopX+xmax*scale/2 |
|||
TopY = self.TopY_H = marge * 2 |
|||
Text.Y=TopY+ymax*scale/2 |
|||
self.TopX_H = self.TopX_H + xmax * scale + marge |
|||
Text.Scale=2 |
|||
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 + marge) |
|||
self.TopX_Vmax = max(self.TopX_Vmax, self.TopX_Hmax) |
|||
self.TopX_V = max(self.TopX_Vmax, self.TopX_V) |
|||
self.TopY_V = marge * 2 |
|||
elif self.onedrawing: |
|||
TopView = FreeCAD.activeDocument().addObject('Drawing::FeatureViewPart',objname) |
|||
if self.TopX_H + xmax * scale < self.L: |
|||
TopView.Source = obj |
|||
if self.TopY_H + ymax * scale + marge * 2 < self.H: |
|||
TopView.Direction = (0.0,0.0,1) |
|||
## H Add at right on same horizontal line |
|||
TopView.Rotation = 0 |
|||
PrintMessage("Drawing2d: horizontal\n") |
|||
TopView.X = TopX |
|||
TopX, TopY = self.TopX_H, self.TopY_H |
|||
TopView.Y = TopY |
|||
self.TopX_H = self.TopX_H + xmax * scale + marge |
|||
TopView.ShowHiddenLines = False |
|||
self.TopX_Hmax = max(self.TopX_Hmax, self.TopX_H) |
|||
TopView.Scale = scale |
|||
self.TopY_Hmax = max(self.TopY_Hmax, |
|||
page.addObject(TopView) |
|||
self.TopY_H + ymax * scale + marge) |
|||
page.addObject(Text) |
|||
self.TopX_Vmax = max(self.TopX_Hmax, self.TopX_Vmax) |
|||
FreeCAD.activeDocument().recompute() |
|||
self.TopX_Vmax = max(self.TopX_Vmax, self.TopX_Hmax) |
|||
return obj_l |
|||
self.TopX_V = max(self.TopX_Vmax, self.TopX_V) |
|||
else: |
|||
## V Add at right on same horizontal line |
|||
PrintMessage("Drawing2d: vertival\n") |
|||
if (self.TopX_V + ymax * scale + 2 * marge < self.L |
|||
and self.TopY_V + xmax * scale + 2 * marge < self.H): |
|||
Draft.rotate(obj, 90) |
|||
Draft.move(obj, Vector(-bb.XMin, -bb.YMin, 0)) |
|||
x0 = xmax; xmax = ymax; ymax = x0 |
|||
self.TopX_V = max(self.TopX_Vmax, self.TopX_V) |
|||
TopX, TopY = self.TopX_V, self.TopY_V |
|||
self.TopX_V = self.TopX_V + xmax * scale + marge |
|||
self.TopY_Vmax = max(self.TopY_Vmax, |
|||
self.TopY_V + ymax * scale + marge) |
|||
else: |
|||
obj_l.append([obj, self.name]) |
|||
return obj_l |
|||
else: |
|||
## H Carriage return |
|||
if self.TopY_Hmax + ymax * scale + self.marge*2 < self.H: |
|||
msg = "Drawing2d: carriage return: {} > {}\n" |
|||
PrintMessage(msg.format(self.TopY_H + ymax * scale, self.H)) |
|||
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 |
|||
msg = "Drawing2d: vertival: {} , {}\n" |
|||
PrintMessage(msg.format(self.TopX_V, self.TopX_Vmax)) |
|||
if (self.TopX_V + ymax * scale + 2 * marge < self.L |
|||
and self.TopY_V + xmax * scale + 2 * marge < self.H): |
|||
Draft.rotate(obj, 90) |
|||
Draft.move(obj, Vector(-bb.XMin, -bb.YMin, 0)) |
|||
x0 = xmax; xmax = ymax; ymax = x0 |
|||
TopX, TopY = self.TopX_V, self.TopY_V |
|||
self.TopX_V = self.TopX_V + xmax * scale + marge |
|||
self.TopY_Vmax = max(self.TopY_Vmax, |
|||
self.TopY_V + ymax * scale + marge) |
|||
else: |
|||
obj_l.append([obj, objname]) |
|||
return obj_l |
|||
doc = FreeCAD.ActiveDocument |
|||
page = doc.getObject(self.name) |
|||
Text = doc.addObject('Drawing::FeatureViewAnnotation', f"{objname}_txt") |
|||
Text.Text = objname |
|||
Text.X = TopX + xmax * scale / 2 |
|||
Text.Y = TopY + ymax * scale / 2 |
|||
Text.Scale = 2 |
|||
TopView = doc.addObject('Drawing::FeatureViewPart', objname) |
|||
TopView.Source = obj |
|||
TopView.Direction = (0, 0, 1) |
|||
TopView.Rotation = 0 |
|||
TopView.X = TopX |
|||
TopView.Y = TopY |
|||
TopView.ShowHiddenLines = False |
|||
TopView.Scale = scale |
|||
page.addObject(TopView) |
|||
page.addObject(Text) |
|||
doc.recompute() |
|||
page.ViewObject.doubleClicked() |
|||
return obj_l |
|||
##################################### |
##################################### |
||
### Dialog Box |
|||
##################################### |
##################################### |
||
# Dialog Box |
|||
##################################### |
|||
##################################### |
|||
fields = [[ "File Name" , "UnrollSurface" ]] |
|||
fields.append(["Dicretization Points Nbr","100" ]) |
|||
fields.append(["Scale","1" ]) |
|||
DialogBox = QtGui.QDialog() |
DialogBox = QtGui.QDialog() |
||
DialogBox.resize(250,250) |
|||
DialogBox.setWindowTitle("UnrollRuledSurface") |
DialogBox.setWindowTitle("UnrollRuledSurface") |
||
la = QtGui. |
la = QtGui.QGridLayout(DialogBox) |
||
la.setSpacing(7) |
|||
buttonGrp1 = QtGui.QButtonGroup(DialogBox) |
|||
buttonGrpEdge = QtGui.QButtonGroup(DialogBox) |
|||
buttonGrpFormat = QtGui.QButtonGroup(DialogBox) |
|||
cols = 4 |
|||
# |
|||
la.addWidget(QtGui.QLabel("File Name"), 0, 0, 1, cols) |
|||
# Input fields |
|||
fname = QtGui.QLineEdit("UnrollSurface") |
|||
# |
|||
la.addWidget(fname, 1, 0, 1, cols) |
|||
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 ] ) |
|||
la.addWidget(QtGui.QLabel("Discretization Points Nbr"), 2, 0, 1, cols) |
|||
scale_check = QtGui.QCheckBox( DialogBox ) |
|||
dpts = QtGui.QLineEdit("30") |
|||
scale_check.setObjectName("checkBox") |
|||
la.addWidget(dpts, 3, 0, 1, 2) |
|||
scale_check.setChecked(True) |
|||
la.addWidget(QtGui.QLabel("Scale auto")) |
|||
la.addWidget(scale_check) |
|||
### |
|||
line3 = QtGui.QFrame(DialogBox) |
|||
la.addWidget(QtGui.QLabel("Generatrices from edge:"), 4, 0, 1, 2) |
|||
line3.setFrameShape(QtGui.QFrame.HLine) |
|||
edgezero = QtGui.QRadioButton("0 to 3") |
|||
line3.setFrameShadow(QtGui.QFrame.Sunken) |
|||
la.addWidget( |
la.addWidget(edgezero, 4, 2) |
||
edgeone = QtGui.QRadioButton("1 to 4") |
|||
la.addWidget(edgeone, 4, 3) |
|||
buttonGrpEdge.addButton(edgezero); buttonGrpEdge.addButton(edgeone) |
|||
edgezero.setChecked(True) |
|||
### |
|||
edge0_check = QtGui.QRadioButton( DialogBox ) |
|||
mkdwg = QtGui.QCheckBox("Make drawing") |
|||
la.addWidget(QtGui.QLabel("Generatrices from edge 1 to 4" )) |
|||
mkdwg.setChecked(True) |
|||
la.addWidget( |
la.addWidget(mkdwg, 5, 0, 1, 2) |
||
edge1_check = QtGui.QRadioButton( DialogBox ) |
|||
la.addWidget(QtGui.QLabel("Generatrices from edge 0 to 3" )) |
|||
edge1_check.setChecked(True) |
|||
buttonGrp1.addButton(edge0_check) |
|||
buttonGrp1.addButton(edge1_check) |
|||
la.addWidget(edge1_check) |
|||
legacylayout = QtGui.QCheckBox("Legacy layout") |
|||
legacylayout.setChecked(False) |
|||
line4.setFrameShape(QtGui.QFrame.HLine) |
|||
la.addWidget(legacylayout, 5, 2, 1, 2) |
|||
line4.setFrameShadow(QtGui.QFrame.Sunken) |
|||
la.addWidget(line4) |
|||
a3_check = QtGui.QRadioButton( DialogBox ) |
|||
la.addWidget(QtGui.QLabel("A3" )) |
|||
a3_check.setChecked(False) |
|||
la.addWidget(a3_check) |
|||
a4_check = QtGui.QRadioButton( DialogBox ) |
|||
la.addWidget(QtGui.QLabel("A4")) |
|||
a4_check.setChecked(True) |
|||
buttonGrp2.addButton(a3_check) |
|||
buttonGrp2.addButton(a4_check) |
|||
la.addWidget(a4_check) |
|||
### |
|||
cartridge_check = QtGui.QCheckBox( DialogBox ) |
|||
autoscale = QtGui.QCheckBox("Auto scale") |
|||
cartridge_check.setObjectName("checkBox") |
|||
autoscale.setChecked(True) |
|||
la.addWidget(QtGui.QLabel("Cartridge")) |
|||
la.addWidget(autoscale, 6, 0, 1, 2) |
|||
cartridge_check.setChecked(False) |
|||
la.addWidget(cartridge_check) |
|||
la.addWidget(QtGui.QLabel("Scale"), 6, 2) |
|||
line6 = QtGui.QFrame(DialogBox) |
|||
scale = QtGui.QLineEdit("1") |
|||
line6.setFrameShape(QtGui.QFrame.HLine) |
|||
la.addWidget(scale, 6, 3) |
|||
line6.setFrameShadow(QtGui.QFrame.Sunken) |
|||
la.addWidget(line6) |
|||
### |
|||
onedrawing_check = QtGui.QCheckBox( DialogBox ) |
|||
la.addWidget(QtGui.QLabel("Paper size:"), 7, 0, 1, 2) |
|||
onedrawing_check.setObjectName("checkBox") |
|||
rba4 = QtGui.QRadioButton("A4") |
|||
la.addWidget(QtGui.QLabel("Group drawings in page")) |
|||
la.addWidget(rba4, 7, 2) |
|||
onedrawing_check.setChecked(True) |
|||
rba3 = QtGui.QRadioButton("A3") |
|||
la.addWidget(onedrawing_check) |
|||
la.addWidget(rba3, 7, 3) |
|||
buttonGrpFormat.addButton(rba4); buttonGrpFormat.addButton(rba3) |
|||
rba4.setChecked(True) |
|||
### |
|||
line7 = QtGui.QFrame(DialogBox) |
|||
cartridge = QtGui.QCheckBox("Cartridge") |
|||
line7.setFrameShape(QtGui.QFrame.HLine) |
|||
cartridge.setChecked(False) |
|||
line7.setFrameShadow(QtGui.QFrame.Sunken) |
|||
la.addWidget( |
la.addWidget(cartridge, 8, 0, 1, 2) |
||
groupdwg = QtGui.QCheckBox("Group drawings in page") |
|||
groupdwg.setChecked(True) |
|||
la.addWidget(groupdwg, 8, 2, 1, 2) |
|||
### |
|||
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, 9, 0, 1, cols) |
||
### |
|||
QtCore.QObject.connect(box, QtCore.SIGNAL("accepted()"), proceed ) |
|||
settings.update(dict(fname=fname, dpts=dpts, edge=buttonGrpEdge, |
|||
QtCore.QObject.connect(box, QtCore.SIGNAL("rejected()"), close ) |
|||
mkdwg=mkdwg, legacylayout=legacylayout, |
|||
autoscale=autoscale, scale=scale, |
|||
papersize=buttonGrpFormat, |
|||
cartridge=cartridge, groupdwg=groupdwg)) |
|||
QtCore.QObject.connect(box, QtCore.SIGNAL("accepted()"), proceed) |
|||
QtCore.QObject.connect(box, QtCore.SIGNAL("rejected()"), close) |
|||
QtCore.QMetaObject.connectSlotsByName(DialogBox) |
QtCore.QMetaObject.connectSlotsByName(DialogBox) |
||
DialogBox.show() |
DialogBox.show() |
||
</pre> |
|||
</syntaxhighlight> |
|||
{{clear}} |
|||
<languages/> |
Latest revision as of 22:29, 28 December 2023
Descrizione |
---|
Permette di srotolare le superfici rigate e di disegnarle in una pagina. Versione macro: 1.0 Ultima modifica: 2013-09-14 Download: ToolBar icon Autore: Hervé B. |
Autore |
Hervé B. |
Download |
ToolBar icon |
Link |
Raccolta di macro Come installare le macro Personalizzare la toolbar |
Versione macro |
1.0 |
Data ultima modifica |
2013-09-14 |
Versioni di FreeCAD |
None |
Scorciatoia |
Nessuna |
Vedere anche |
Nessuno |
Description
Descrizione
Questa macro permette di creare lo sviluppo di una superficie rigata e di disegnarlo in una pagina, e di inserire il disegno in un foglio A3 o A4.
Installation
Installazione
Secondo quanto descritto in Macro for unrolling ruled surfaces
Copiare il file del codice nella directory :
- Linux & Mac : $home/.Freecad/Mod/UnrollRuledSurface.
- Windows : C:\Program Files\FreeCAD0.13
Aggiungere i modelli del foglio : A3_Landscape_Empty.svg A3_Landscape.svg A4_Landscape_Empty.svg A4_Landscape.svg
See also: Macro for unrolling ruled surfaces.
Options
Opzioni
- Numero di generatrici
- Scala manuale o automatica
- Formato della pagina: A3, A4
- Tabella (secondo i modelli di FreeCAD)
- Raggruppamento dei disegni nella stessa pagina se è possibile.
Usage
Utlizzo
- Selezionare una supeficie in un oggetto
- Scomporre l'oggetto (menu Draft -> Retrocedere)
- Selezionare le supefici da sviluppare
- Eseguire la macro
Codice
L'ultima versione della macro si trova in UnrollRuledSurface.FCMacro ma il modo più semplice per installare questa macro è attraverso Addon manager
.
Macro_unrollRuledSurface.py
# -*- coding: utf-8 -*- #*************************************************************************** #* * #* Copyright (c) 2013 - DoNovae/Herve BAILLY <hbl13@donovae.com> * #* Copyright (c) 2022 - heda <heda@fc-forum> * #* * #* 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 * #* * #*************************************************************************** __Name__ = 'Unroll Ruled Surface' __Comment__ = 'Unroll of a ruled surface and draw it on a page.' __Author__ = 'Hervé B., heda' __Version__ = '1.1' __Date__ = '2022-07-24' __License__ = 'LGPL-2.0-or-later' __Web__ = 'https://wiki.freecad.org/Macro_Unroll_Ruled_Surface' __Wiki__ = 'https://wiki.freecad.org/Macro_Unroll_Ruled_Surface' __Icon__ = '' __Help__ = ('Select ruled surfaces, Explode them (cf Draft menu), ' 'Select the surfaces, Execute the macro') __Status__ = '' __Requires__ = '' __Communication__ = '' __Files__ = '' __doc__ = """ select a face, or several and run the macro. a shell/solid needs to be draft/downgraded to get the faces as separate objects the macro is intended to unroll lofted faces, function beyond that is (in current version) a bonus settings are not context aware, all settings not applicable are ignored. as example, using autoscaling ignores the scale-value in the form v1.1 (2022-07-24) py3/qt5 compat, cosmetic code changes, minor code tweaks, used gridlayout for form, added option to skip drawing, made new layout engine with techdraw, the drawing wb layout engine is now called "legacy" v1.0.1 (2019-02-01) - on git v1.0 (2013-09-14) - on wiki note: - unfolding sometimes works and sometimes not - not all surfaces are theoretically possible to unroll """ import os from PySide import QtGui, QtCore import FreeCAD, FreeCADGui import Part, Draft Vector = FreeCAD.Base.Vector PrintMessage = FreeCAD.Console.PrintMessage PrintError = FreeCAD.Console.PrintError settings = dict() unroll_l = [] dwgtpllegacy = 'Mod/Drawing/Templates' dwgtpl = 'Mod/TechDraw/Templates' ##################################### ### Functions ##################################### def errorDialog(msg): diag = QtGui.QMessageBox(QtGui.QMessageBox.Critical, "Error Message", msg) diag.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint) diag.exec_() def ending(): PrintMessage("UnrollRuledSurface: end.\n") PrintMessage("===========================================\n") FreeCAD.ActiveDocument.recompute() def proceed(): QtGui.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) PrintMessage("===========================================\n") PrintMessage("UnrollRuledSurface: start.\n") try: sts = lambda s: settings.get(s) file_name = sts("fname").text() pts_nbr = float(sts("dpts").text()) makedwg = sts("mkdwg").isChecked() leglay = sts("legacylayout").isChecked() scale = float(sts("scale").text()) # ignored if autoscale is set scale_auto = sts("autoscale").isChecked() edge0 = sts("edge").checkedId() == -2 a3 = sts("papersize").checkedId() == -3 cartridge = sts("cartridge").isChecked() onedrawing = sts("groupdwg").isChecked() PrintMessage("UnrollRuledSurface.file_name: {}\n".format(file_name)) PrintMessage("UnrollRuledSurface.pts_nbr: {}\n".format(pts_nbr)) PrintMessage("UnrollRuledSurface.edge0: {}\n".format(edge0)) PrintMessage("UnrollRuledSurface.makedwg: {}\n".format(makedwg)) PrintMessage("UnrollRuledSurface.leglay: {}\n".format(leglay)) PrintMessage("UnrollRuledSurface.scale_check: {}\n".format(scale_auto)) PrintMessage("UnrollRuledSurface.scale: {}\n".format(scale)) PrintMessage("UnrollRuledSurface.a3_check: {}\n".format(a3)) PrintMessage("UnrollRuledSurface.cartridge: {}\n".format(cartridge)) PrintMessage("UnrollRuledSurface.onedrawing: {}\n".format(onedrawing)) except: msg = "UnrollRuledSurface: wrong inputs...\n" PrintError(msg) errorDialog(msg) QtGui.QApplication.restoreOverrideCursor() DialogBox.hide() ending() return QtGui.QApplication.restoreOverrideCursor() DialogBox.hide() unrollRS = unrollRuledSurface(file_name, pts_nbr, edge0) ## Get selection sel = FreeCADGui.Selection.getSelection() if not sel: PrintMessage("UnrollRuledSurface: no selection...\n") ending() return faceid = 0 objnames_l, objnames0_l = [], [] grp = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", "{}_objs".format(file_name)) for objid, obji in enumerate(sel): shape = obji.Shape faces = shape.Faces for idx in range(len(faces)): msg = "UnrollRuledSurface.proceed: ObjId = {}, faceId = {}\n" PrintMessage(msg.format(objid, faceid)) name = obji.Name if len(faces) > 1: name = "{}.face_{}".format(name, idx) obj = unrollRS.unroll(faces[idx], name) obj.ViewObject.Visibility = not makedwg grp.addObject(obj) objnames_l.append([obj, name]) objnames0_l.append([obji, name]) faceid += 1 if not makedwg: ending() return if leglay: idx = 0 while len(objnames_l) > 0: draw = Drawing2dLegacy(scale, scale_auto, a3, cartridge, onedrawing, "{}_page{:02}".format(file_name, idx)) objnames_l = draw.all2d(objnames_l) idx += 1 msg = "UnrollRuledSurface: obj_l = {}\n" PrintMessage(msg.format(len(objnames_l))) else: draw = Drawing2d(scale, scale_auto, a3, cartridge) n = 4 if onedrawing else 1 chunks = [objnames_l[i:i + n] for i in range(0, len(objnames_l), n)] for i, chunk in enumerate(chunks, start=1): draw.drawpage(chunk, "{}_page{:02}".format(file_name, i)) ending() def close(): DialogBox.hide() def getType(obj): return type(obj).__name__ class unrollRuledSurface: """ unroll ruled surfaces :file_name: ouput file :pts_nbr: nbr point of discretization """ def __init__(self, file_name, pts_nbr, edge0): self.doc = FreeCAD.ActiveDocument self.file_name = file_name self.pts_nbr = int(pts_nbr) self.edge0 = edge0 msg = "UnrollRuledSurface.unroll - file_name: {}, pts_nbr: {}\n" PrintMessage(msg.format(file_name, pts_nbr)) def discretize(self, curve): """discretize a curve""" if getType(curve) in ('GeomLineSegment', 'GeomCircle'): sd = curve.discretize(self.pts_nbr) elif getType(curve) == 'GeomBSplineCurve': nodes = curve.getPoles() spline = Part.BSplineCurve() spline.buildFromPoles(nodes) sd = spline.discretize(self.pts_nbr) else: sd = curve.discretize(self.pts_nbr) return sd def nbpoles(self, curve): """find number of poles for a curve""" if getType(curve) == 'GeomLineSegment': nbpol=1 elif getType(curve) == 'GeomBSplineCurve': nbpol=curve.NbPoles elif getType(curve) == 'GeomCircle': nbpol=2 elif getType(curve) == 'GeomBezierCurve': nbpol=4 else: nbpol=0 msg = "UnrollRulrdSurface.nbpole {:s} = {:d}\n" PrintMessage(msg.format(getType(curve), nbpol)) return nbpol def unroll(self, face, name): """unrolls a face composed of 2 to 4 edges""" nbredges = len(face.Edges) msg = "UnrollRuledSurface.unroll: Edge Nbr = {}\n" PrintMessage(msg.format(nbredges)) if nbredges == 2: e1, e2 = face.Edges sd1 = e1.Curve.discretize(self.pts_nbr) sd2 = e2.Curve.discretize(self.pts_nbr) elif nbredges == 3: e1, _, e2 = face.Edges sd1 = e1.Curve.discretize(self.pts_nbr) sd2 = e2.Curve.discretize(self.pts_nbr) else: E0, E1, E2, E3, *_ = face.Edges ## Choose more complexe curve as edge nbpol0 = self.nbpoles(E0.Curve) nbpol1 = self.nbpoles(E1.Curve) nbpol2 = self.nbpoles(E2.Curve) nbpol3 = self.nbpoles(E3.Curve) msg = ("UnrollRuledSurface.unroll: nbpol0= {:d}, nbpol1= {:d}," " nbpol2= {:d}, nbpol3= {:d}\n") PrintMessage(msg.format(nbpol0, nbpol1, nbpol2, nbpol3)) if self.edge0: e1, e2 = E0, E2 v = self.discretize(E1) v0, v1 = v[0], v[self.pts_nbr-1] else: e1, e2 = E1, E3 v = self.discretize(E2) v0, v1 = v[0], v[self.pts_nbr-1] sd1 = self.discretize(e1) sd2 = self.discretize(e2) ## Reverse if curves cross over if not (sd2[0].__eq__(v0) or not sd2[0].__eq__(v1)): sd2.reverse() ## Create a polygon object and set its nodes devlxy_l = self.devlxyz(sd1, sd2) msg = "UnrollRuledSurface.unroll: size devlxy_l: {}\n" PrintMessage(msg.format(len(devlxy_l))) p = self.doc.addObject("Part::Polygon", name) p.Nodes = devlxy_l self.doc.recompute() FreeCADGui.ActiveDocument.ActiveView.fitAll() return p def vect_copy(self, vect): """returns copy of vector""" return vect.add(Vector()) def vect_cos(self, vect1, vect2): """returns cosine angle between 2 vectors""" return vect1.dot(vect2) / vect1.Length / vect2.Length def vect_sin(self, vect1, vect2): """returns absolute sinus angle between 2 vectors""" v = Vector() 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.Length / vect1.Length / vect2.Length def devlxyz(self, vect1, vect2): """ unrolls a face composed of 4 edges args: vect1, vect2 --> 2 edges of the shape returns: dvlxy """ lenv1, lenv2 = len(vect1), len(vect2) if lenv1 != lenv2 or lenv1 != self.pts_nbr or lenv2 != self.pts_nbr: msg = ("UnrollRuledSurface.devlxyz: incompatility of sizes vect1," " vect2, pts_nbr: ({}, {}, {})\n") PrintError(msg.format(lenv1, lenv2, self.pts_nbr)) errorDialog(msg) devlxy_l, devl1xy_l, devl2xy_l = [], [], [] errormax = 0.0 ## Init unroll ## AB a1b1 = vect2[0].sub(vect1[0]) oa1 = Vector(0, 0, 0) devl1xy_l.append(oa1) #A1 ob1 = Vector(a1b1.Length, 0, 0) devl2xy_l.append(ob1) #B1 #self.draw_line(devl1xy_l[0], devl2xy_l[0]) #self.draw_line(vect1[0], vect2[0]) for j in range(1, self.pts_nbr): ## AB ab = vect2[j-1].sub(vect1[j-1]) #self.draw_line(vect1[j-1], vect2[j-1]) ## AC ac = vect1[j].sub(vect1[j-1]) ## BD bd = vect2[j].sub(vect2[j-1]) ## CD cd = vect2[j].sub(vect1[j]) ## A1B1 in unroll plan a1b1 = devl2xy_l[j-1].sub(devl1xy_l[j-1]) a1b1n = self.vect_copy(a1b1) a1b1n.normalize() a1b1on = Vector(-a1b1n.y, a1b1n.x, 0) ## A1C1 cosalp = self.vect_cos(ab, ac) sinalp = self.vect_sin(ab, ac) a1c1 = self.vect_copy(a1b1n) a1c1.multiply(cosalp * ac.Length) v = self.vect_copy(a1b1on) v.multiply(sinalp * ac.Length) a1c1 = a1c1.add(v) oa1 = self.vect_copy(devl1xy_l[j-1]) oc1 = oa1.add(a1c1) devl1xy_l.append(oc1) ## B1D1 cosalp = self.vect_cos(ab, bd) sinalp = self.vect_sin(ab, bd) b1d1 = self.vect_copy(a1b1n) b1d1.multiply(cosalp * bd.Length) v = self.vect_copy(a1b1on) v.multiply(sinalp * bd.Length) b1d1 = b1d1.add(v) ob1 = self.vect_copy(devl2xy_l[j-1]) od1 = ob1.add(b1d1) devl2xy_l.append(od1) ## Draw generatrice #self.draw_line(devl1xy_l[j], devl2xy_l[j]) c1d1 = devl2xy_l[j].sub(devl1xy_l[j]) if ab.Length != 0: abl = ab.Length errormax = max(errormax, abs(abl - c1d1.Length) / abl) msg = "UnrollRuledSurface Error cd,c1d1: {:.1f} %\n" PrintMessage(msg.format(errormax*100)) ## Close polygone devlxy_l = devl1xy_l devl2xy_l.reverse() devlxy_l.extend(devl2xy_l) v = Vector() devlxy_l.append(v) return devlxy_l def draw_line(self, vect0, vect1): """draws a Part.Line between vect0 & vect1""" l = Part.LineSegment() l.StartPoint = vect0 l.EndPoint = vect1 self.doc.addObject("Part::Feature", "Line").Shape = l.toShape() class Scale: """keeps autoscaling to integers""" def __init__(self, scale): self.scale = scale if scale >= 1 else 1 / scale self.scale = int(self.scale) self.inverted = scale >= 1 def get(self): if self.inverted: return self.scale, '{}:1'.format(self.scale) else: return 1/self.scale, '1:{}'.format(self.scale) class Drawing2d: """ TechDraw wb layout engine, diffeent logic compared to drawing wb layout. serves basic purpose... """ def __init__(self, scale, scale_auto, a3, cartridge): self.a3 = a3 self.scale = scale self.scale_auto = scale_auto self.cartridge = cartridge if a3: self.WH = 420, 297 else: self.WH = 297, 210 self.doc = FreeCAD.ActiveDocument def _mkquadrants(self, nbr): """centers of quadrants w/o margin""" w, h = self.WH w2, h2 = w/2, h/2 w4, h4 = w/4, h/4 q = {1: [[w, h], [[w2, h2]]], 2: [[w2, h], [[w2 - w4, h2], [w2 + w4, h2]]], 3: [[w2, h2], [[w2 - w4, h2 + h4], [w2 + w4, h2 + h4], [w2 - w4, h2 - h4]]], 4: [[w2, h2], [[w2 - w4, h2 + h4], [w2 + w4, h2 + h4], [w2 - w4, h2 - h4], [w2 + w4, h2 - h4]]]} return q.get(nbr) def newPage(self, doc, page_name): freecad_dir = os.path.join(FreeCAD.getResourceDir(), dwgtpl) page = doc.addObject('TechDraw::DrawPage', page_name) template = self.doc.addObject('TechDraw::DrawSVGTemplate', 'Template') size = 'A3' if self.a3 else 'A4' frame = 'TD' if self.cartridge else '_blank' template.Template = freecad_dir + '/{}_Landscape{}.svg'.format(size, frame) page.Template = template return page def drawpage(self, faces, page_name): """max 4 per page, simple layout with halfs or quadrants""" page = self.newPage(self.doc, page_name) [W, H], ll = self._mkquadrants(len(faces)) for [face, name], [x0, y0] in zip(faces, ll): bb = face.Shape.BoundBox xr, yr = W / bb.XLength, H / bb.YLength adjust = 0.7 if self.cartridge else 0.85 scale = min(xr, yr) * adjust if self.scale_auto else self.scale scale, scr = Scale(scale).get() bb.scale(scale, scale, 1) # unrolled faces in xy-plane TopView = self.doc.addObject('TechDraw::DrawViewPart', name + ' view') page.addView(TopView) TopView.Source = face TopView.Direction = (0, 0, 1) TopView.XDirection = (1, 0, 0) TopView.Scale = scale TopView.X = x0 TopView.Y = y0 Text = self.doc.addObject('TechDraw::DrawViewAnnotation', name + ' txt') page.addView(Text) Text.Text = '{} [{}]'.format(name, scr) Text.recompute() # for size Text.X = x0 yt = y0 - bb.YLength / 2 - Text.TextSize.Value * 1.5 Text.Y = max(1, yt) self.doc.recompute() page.ViewObject.doubleClicked() FreeCADGui.runCommand('TechDraw_ToggleFrame', 0) class Drawing2dLegacy: """ makes 2d drawing with Drawing wb (original layout engine, now legacy) - obj_l: list of objects """ ## untouched logic in v1.1 def __init__(self, scale, scale_auto, a3, cartridge, onedrawing, page_str): self.TopX_H = self.TopY_H = 0 self.TopX_V = self.TopY_V = 0 self.TopX_Hmax = self.TopY_Hmax = 0 self.TopX_Vmax = self.TopY_Vmax = 0 self.a3 = a3 self.scale = scale self.scale_auto = scale_auto self.cartridge = cartridge self.onedrawing = onedrawing self.marge = 6 if self.a3: self.L, self.H = 420, 297 else: self.L, self.H = 297, 210 self.name = page_str def newPage(self): freecad_dir = os.path.join(FreeCAD.getResourceDir(), dwgtpllegacy) doc = FreeCAD.ActiveDocument page = doc.addObject('Drawing::FeaturePage', self.name) size = 'A3' if self.a3 else 'A4' frame = '' if self.cartridge else '_plain' page.Template = freecad_dir + '/{}_Landscape{}.svg'.format(size, frame) return page def all2d(self, objname_l): obj1_l = [] for objid in range(len(objname_l)): if objid == 0 or not self.onedrawing: page = self.newPage() obj1_l.extend(self.done(objid, objname_l[objid])) return obj1_l def done(self, idx, objname): obj_l = [] obj, objname = objname marge = self.marge bb = obj.Shape.BoundBox xmax = bb.XMax - bb.XMin ymax = bb.YMax - bb.YMin if ymax > xmax: Draft.rotate(obj, 90) Draft.move(obj, Vector(-bb.XMin, -bb.YMin, 0)) xmax = bb.XMax - bb.XMin ymax = bb.YMax - bb.YMin scale = min((self.L-4 * marge) / xmax, (self.H-4 * marge) / ymax) if (not self.scale_auto) or self.onedrawing: scale = self.scale PrintMessage("UnrollRuledSurface.drawing: scale= {:.2f}\n".format(scale)) if idx == 0 or not self.onedrawing: PrintMessage("Drawing2d: init\n") TopX = self.TopX_H = marge * 2 TopY = self.TopY_H = marge * 2 self.TopX_H = self.TopX_H + xmax * scale + 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 + marge) self.TopX_Vmax = max(self.TopX_Vmax, self.TopX_Hmax) self.TopX_V = max(self.TopX_Vmax, self.TopX_V) self.TopY_V = marge * 2 elif self.onedrawing: if self.TopX_H + xmax * scale < self.L: if self.TopY_H + ymax * scale + marge * 2 < self.H: ## H Add at right on same horizontal line PrintMessage("Drawing2d: horizontal\n") TopX, TopY = self.TopX_H, self.TopY_H self.TopX_H = self.TopX_H + xmax * scale + marge self.TopX_Hmax = max(self.TopX_Hmax, self.TopX_H) self.TopY_Hmax = max(self.TopY_Hmax, self.TopY_H + ymax * scale + 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 PrintMessage("Drawing2d: vertival\n") if (self.TopX_V + ymax * scale + 2 * marge < self.L and self.TopY_V + xmax * scale + 2 * marge < self.H): Draft.rotate(obj, 90) Draft.move(obj, Vector(-bb.XMin, -bb.YMin, 0)) x0 = xmax; xmax = ymax; ymax = x0 self.TopX_V = max(self.TopX_Vmax, self.TopX_V) TopX, TopY = self.TopX_V, self.TopY_V self.TopX_V = self.TopX_V + xmax * scale + marge self.TopY_Vmax = max(self.TopY_Vmax, self.TopY_V + ymax * scale + marge) else: obj_l.append([obj, self.name]) return obj_l else: ## H Carriage return if self.TopY_Hmax + ymax * scale + self.marge*2 < self.H: msg = "Drawing2d: carriage return: {} > {}\n" PrintMessage(msg.format(self.TopY_H + ymax * scale, self.H)) 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 msg = "Drawing2d: vertival: {} , {}\n" PrintMessage(msg.format(self.TopX_V, self.TopX_Vmax)) if (self.TopX_V + ymax * scale + 2 * marge < self.L and self.TopY_V + xmax * scale + 2 * marge < self.H): Draft.rotate(obj, 90) Draft.move(obj, Vector(-bb.XMin, -bb.YMin, 0)) x0 = xmax; xmax = ymax; ymax = x0 TopX, TopY = self.TopX_V, self.TopY_V self.TopX_V = self.TopX_V + xmax * scale + marge self.TopY_Vmax = max(self.TopY_Vmax, self.TopY_V + ymax * scale + marge) else: obj_l.append([obj, objname]) return obj_l doc = FreeCAD.ActiveDocument page = doc.getObject(self.name) Text = doc.addObject('Drawing::FeatureViewAnnotation', f"{objname}_txt") Text.Text = objname Text.X = TopX + xmax * scale / 2 Text.Y = TopY + ymax * scale / 2 Text.Scale = 2 TopView = doc.addObject('Drawing::FeatureViewPart', objname) TopView.Source = obj TopView.Direction = (0, 0, 1) TopView.Rotation = 0 TopView.X = TopX TopView.Y = TopY TopView.ShowHiddenLines = False TopView.Scale = scale page.addObject(TopView) page.addObject(Text) doc.recompute() page.ViewObject.doubleClicked() return obj_l ##################################### ### Dialog Box ##################################### DialogBox = QtGui.QDialog() DialogBox.setWindowTitle("UnrollRuledSurface") la = QtGui.QGridLayout(DialogBox) la.setSpacing(7) buttonGrpEdge = QtGui.QButtonGroup(DialogBox) buttonGrpFormat = QtGui.QButtonGroup(DialogBox) cols = 4 la.addWidget(QtGui.QLabel("File Name"), 0, 0, 1, cols) fname = QtGui.QLineEdit("UnrollSurface") la.addWidget(fname, 1, 0, 1, cols) la.addWidget(QtGui.QLabel("Discretization Points Nbr"), 2, 0, 1, cols) dpts = QtGui.QLineEdit("30") la.addWidget(dpts, 3, 0, 1, 2) ### la.addWidget(QtGui.QLabel("Generatrices from edge:"), 4, 0, 1, 2) edgezero = QtGui.QRadioButton("0 to 3") la.addWidget(edgezero, 4, 2) edgeone = QtGui.QRadioButton("1 to 4") la.addWidget(edgeone, 4, 3) buttonGrpEdge.addButton(edgezero); buttonGrpEdge.addButton(edgeone) edgezero.setChecked(True) ### mkdwg = QtGui.QCheckBox("Make drawing") mkdwg.setChecked(True) la.addWidget(mkdwg, 5, 0, 1, 2) legacylayout = QtGui.QCheckBox("Legacy layout") legacylayout.setChecked(False) la.addWidget(legacylayout, 5, 2, 1, 2) ### autoscale = QtGui.QCheckBox("Auto scale") autoscale.setChecked(True) la.addWidget(autoscale, 6, 0, 1, 2) la.addWidget(QtGui.QLabel("Scale"), 6, 2) scale = QtGui.QLineEdit("1") la.addWidget(scale, 6, 3) ### la.addWidget(QtGui.QLabel("Paper size:"), 7, 0, 1, 2) rba4 = QtGui.QRadioButton("A4") la.addWidget(rba4, 7, 2) rba3 = QtGui.QRadioButton("A3") la.addWidget(rba3, 7, 3) buttonGrpFormat.addButton(rba4); buttonGrpFormat.addButton(rba3) rba4.setChecked(True) ### cartridge = QtGui.QCheckBox("Cartridge") cartridge.setChecked(False) la.addWidget(cartridge, 8, 0, 1, 2) groupdwg = QtGui.QCheckBox("Group drawings in page") groupdwg.setChecked(True) la.addWidget(groupdwg, 8, 2, 1, 2) ### box = QtGui.QDialogButtonBox(DialogBox) box.setOrientation(QtCore.Qt.Horizontal) box.setStandardButtons(QtGui.QDialogButtonBox.Cancel | QtGui.QDialogButtonBox.Ok) la.addWidget(box, 9, 0, 1, cols) ### settings.update(dict(fname=fname, dpts=dpts, edge=buttonGrpEdge, mkdwg=mkdwg, legacylayout=legacylayout, autoscale=autoscale, scale=scale, papersize=buttonGrpFormat, cartridge=cartridge, groupdwg=groupdwg)) QtCore.QObject.connect(box, QtCore.SIGNAL("accepted()"), proceed) QtCore.QObject.connect(box, QtCore.SIGNAL("rejected()"), close) QtCore.QMetaObject.connectSlotsByName(DialogBox) DialogBox.show()