1
#***************************************************************************
2
#* Copyright (c) 2011 Yorik van Havre <yorik@uncreated.net> *
4
#* This program is free software; you can redistribute it and/or modify *
5
#* it under the terms of the GNU Lesser General Public License (LGPL) *
6
#* as published by the Free Software Foundation; either version 2 of *
7
#* the License, or (at your option) any later version. *
8
#* for detail see the LICENCE text file. *
10
#* This program is distributed in the hope that it will be useful, *
11
#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
12
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13
#* GNU Library General Public License for more details. *
15
#* You should have received a copy of the GNU Library General Public *
16
#* License along with this program; if not, write to the Free Software *
17
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
20
#***************************************************************************
30
from FreeCAD import Vector
31
from draftutils import params
35
from PySide import QtCore, QtGui
36
from draftutils.translate import translate
37
from PySide.QtCore import QT_TRANSLATE_NOOP
40
def translate(ctxt,txt):
42
def QT_TRANSLATE_NOOP(ctxt,txt):
48
# \brief The Panel object and tools
50
# This module provides tools to build Panel objects.
51
# Panels consist of a closed shape that gets extruded to
52
# produce a flat object.
54
__title__ = "FreeCAD Panel"
55
__author__ = "Yorik van Havre"
56
__url__ = "https://www.freecad.org"
59
class _Panel(ArchComponent.Component):
63
def __init__(self,obj):
65
ArchComponent.Component.__init__(self,obj)
66
self.setProperties(obj)
69
def setProperties(self,obj):
71
pl = obj.PropertiesList
72
if not "Length" in pl:
73
obj.addProperty("App::PropertyLength","Length","Panel", QT_TRANSLATE_NOOP("App::Property","The length of this element, if not based on a profile"))
75
obj.addProperty("App::PropertyLength","Width","Panel", QT_TRANSLATE_NOOP("App::Property","The width of this element, if not based on a profile"))
76
if not "Thickness" in pl:
77
obj.addProperty("App::PropertyLength","Thickness","Panel",QT_TRANSLATE_NOOP("App::Property","The thickness or extrusion depth of this element"))
78
if not "Sheets" in pl:
79
obj.addProperty("App::PropertyInteger","Sheets","Panel", QT_TRANSLATE_NOOP("App::Property","The number of sheets to use"))
81
if not "Offset" in pl:
82
obj.addProperty("App::PropertyDistance","Offset","Panel", QT_TRANSLATE_NOOP("App::Property","The offset between this panel and its baseline"))
83
if not "WaveLength" in pl:
84
obj.addProperty("App::PropertyLength","WaveLength","Panel", QT_TRANSLATE_NOOP("App::Property","The length of waves for corrugated elements"))
85
if not "WaveHeight" in pl:
86
obj.addProperty("App::PropertyLength","WaveHeight","Panel", QT_TRANSLATE_NOOP("App::Property","The height of waves for corrugated elements"))
87
if not "WaveOffset" in pl:
88
obj.addProperty("App::PropertyDistance","WaveOffset","Panel", QT_TRANSLATE_NOOP("App::Property","The horizontal offset of waves for corrugated elements"))
89
if not "WaveDirection" in pl:
90
obj.addProperty("App::PropertyAngle","WaveDirection","Panel", QT_TRANSLATE_NOOP("App::Property","The direction of waves for corrugated elements"))
91
if not "WaveType" in pl:
92
obj.addProperty("App::PropertyEnumeration","WaveType","Panel", QT_TRANSLATE_NOOP("App::Property","The type of waves for corrugated elements"))
93
obj.WaveType = ["Curved","Trapezoidal","Spikes"]
94
if not "WaveBottom" in pl:
95
obj.addProperty("App::PropertyBool","WaveBottom","Panel", QT_TRANSLATE_NOOP("App::Property","If the wave also affects the bottom side or not"))
97
obj.addProperty("App::PropertyArea","Area","Panel", QT_TRANSLATE_NOOP("App::Property","The area of this panel"))
98
if not "FaceMaker" in pl:
99
obj.addProperty("App::PropertyEnumeration","FaceMaker","Panel",QT_TRANSLATE_NOOP("App::Property","The facemaker type to use to build the profile of this object"))
100
obj.FaceMaker = ["None","Simple","Cheese","Bullseye"]
101
if not "Normal" in pl:
102
obj.addProperty("App::PropertyVector","Normal","Panel",QT_TRANSLATE_NOOP("App::Property","The normal extrusion direction of this object (keep (0,0,0) for automatic normal)"))
104
obj.setEditorMode("VerticalArea",2)
105
obj.setEditorMode("HorizontalArea",2)
107
def onDocumentRestored(self,obj):
109
ArchComponent.Component.onDocumentRestored(self,obj)
110
self.setProperties(obj)
112
def execute(self,obj):
114
"creates the panel shape"
126
if hasattr(obj.Base,'Shape'):
127
if obj.Base.Shape.isNull():
129
elif obj.Base.isDerivedFrom("Mesh::Feature"):
130
if not obj.Base.Mesh.isSolid():
134
length = obj.Length.Value
138
width = obj.Width.Value
141
if obj.Thickness.Value:
142
thickness = obj.Thickness.Value
146
elif hasattr(obj.Base,'Shape'):
147
if not obj.Base.Shape.Solids:
149
if hasattr(obj,"Material"):
151
if hasattr(obj.Material,"Materials"):
153
thicknesses = [t for t in obj.Material.Thicknesses if t >= 0]
154
restwidth = thickness - sum(thicknesses)
156
varwidth = [t for t in thicknesses if t == 0]
158
varwidth = restwidth/len(varwidth)
159
for t in obj.Material.Thicknesses:
163
layers.append(varwidth)
164
# creating base shape
168
if hasattr(obj,"Normal"):
169
if obj.Normal.Length > 0:
170
normal = Vector(obj.Normal)
172
normal.multiply(thickness)
175
base = obj.Base.Shape.copy()
177
# p = FreeCAD.Placement(obj.Base.Placement)
181
normal = baseprofile.Faces[0].normalAt(0,0).multiply(thickness)
187
n = Vector(normal).normalize().multiply(abs(l))
190
o = Vector(normal).normalize().multiply(layeroffset)
193
layeroffset += abs(l)
194
base = Part.makeCompound(shps)
196
base = base.extrude(normal)
199
if hasattr(obj,"FaceMaker"):
200
if obj.FaceMaker != "None":
202
baseprofile = Part.makeFace(base.Wires,"Part::FaceMaker"+str(obj.FaceMaker))
205
FreeCAD.Console.PrintError(translate("Arch","Facemaker returned an error")+"\n")
213
baseprofile = ArchCommands.makeFace(base.Wires)
215
normal = baseprofile.normalAt(0,0).multiply(thickness)
221
n = Vector(normal).normalize().multiply(abs(l))
222
b = baseprofile.extrude(n)
224
o = Vector(normal).normalize().multiply(layeroffset)
227
layeroffset += abs(l)
228
base = Part.makeCompound(shps)
230
base = baseprofile.extrude(normal)
231
elif obj.Base.isDerivedFrom("Mesh::Feature"):
232
if obj.Base.Mesh.isSolid():
233
if obj.Base.Mesh.countComponents() == 1:
234
sh = ArchCommands.getShapeFromMesh(obj.Base.Mesh)
235
if sh.isClosed() and sh.isValid() and sh.Solids:
244
n = Vector(normal).normalize().multiply(l)
246
n = Vector(0,0,1).multiply(abs(l))
249
v1 = Vector(-l2,-w2,layeroffset)
250
v2 = Vector(l2,-w2,layeroffset)
251
v3 = Vector(l2,w2,layeroffset)
252
v4 = Vector(-l2,w2,layeroffset)
253
base = Part.makePolygon([v1,v2,v3,v4,v1])
254
baseprofile = Part.Face(base)
255
base = baseprofile.extrude(n)
257
layeroffset += abs(l)
258
base = Part.makeCompound(shps)
261
normal = Vector(0,0,1).multiply(thickness)
264
v1 = Vector(-l2,-w2,0)
265
v2 = Vector(l2,-w2,0)
267
v4 = Vector(-l2,w2,0)
268
base = Part.makePolygon([v1,v2,v3,v4,v1])
269
baseprofile = Part.Face(base)
270
base = baseprofile.extrude(normal)
272
if hasattr(obj,"Area"):
274
obj.Area = baseprofile.Area
276
if hasattr(obj,"WaveLength"):
277
if baseprofile and obj.WaveLength.Value and obj.WaveHeight.Value:
279
bb = baseprofile.BoundBox
280
bb.enlarge(bb.DiagonalLength)
282
if hasattr(obj,"WaveBottom"):
283
if not obj.WaveBottom:
284
if obj.WaveType == "Curved":
285
if obj.Thickness.Value > obj.WaveHeight.Value:
286
downsegment = obj.Thickness.Value
288
downsegment = obj.WaveHeight.Value + obj.Thickness.Value
290
downsegment = obj.Thickness.Value
292
p5 = Vector(obj.WaveLength.Value*2,0,0)
293
if obj.WaveType == "Curved":
294
p2 = Vector(obj.WaveLength.Value/2,0,obj.WaveHeight.Value)
295
p3 = Vector(obj.WaveLength.Value,0,0)
296
e1 = Part.Arc(p1,p2,p3).toShape()
297
p4 = Vector(obj.WaveLength.Value*1.5,0,-obj.WaveHeight.Value)
298
e2 = Part.Arc(p3,p4,p5).toShape()
299
upsegment = Part.Wire([e1,e2])
301
if obj.Thickness.Value < e1.Curve.Radius:
303
c3.Radius = e1.Curve.Radius-obj.Thickness.Value
304
e3 = Part.Arc(c3,e1.FirstParameter,e1.LastParameter).toShape()
306
c4.Radius = e2.Curve.Radius+obj.Thickness.Value
307
e4 = Part.Arc(c4,e2.FirstParameter,e2.LastParameter).toShape()
308
downsegment = Part.Wire([e3,e4])
310
r = e2.Curve.Radius+obj.Thickness.Value
311
z = math.sqrt(r^2 - obj.WaveLength.Value^2)
312
p6 = e2.Curve.Center.add(Vector(-obj.WaveLength,0,-z))
313
p7 = e2.Curve.Center.add(Vector(0,0,-r))
314
p8 = e2.Curve.Center.add(Vector(obj.WaveLength,0,-z))
315
downsegment = Part.Arc(p6,p7,p8).toShape()
317
elif obj.WaveType == "Trapezoidal":
318
p2 = Vector(obj.WaveLength.Value/4,0,obj.WaveHeight.Value)
319
p3 = Vector(obj.WaveLength.Value,0,obj.WaveHeight.Value)
320
p4 = Vector(obj.WaveLength.Value*1.25,0,0)
321
upsegment = Part.makePolygon([p1,p2,p3,p4,p5])
323
a = ((p1.sub(p2)).getAngle(p3.sub(p2)))/2
324
tx = obj.Thickness.Value/math.tan(a)
325
d1 = Vector(tx,0,-obj.Thickness.Value)
326
d2 = Vector(-tx,0,-obj.Thickness.Value)
328
if tx >= p3.sub(p2).Length/2:
331
d3.multiply((0.625*obj.WaveLength.Value)/d3.x)
332
d4 = Vector(d3.x,0,-d3.z)
336
downsegment = Part.makePolygon([p6,p7,p8,p9])
337
elif tx <= 0.625*obj.WaveLength.Value:
342
downsegment = Part.makePolygon([p6,p7,p8,p9,p10])
344
downsegment = obj.Thickness.Value
347
p2 = Vector(obj.WaveHeight.Value,0,obj.WaveHeight.Value)
348
p3 = Vector(obj.WaveHeight.Value*2,0,0)
349
upsegment = Part.makePolygon([p1,p2,p3,p5])
351
downsegment = obj.Thickness.Value
353
upsegment.translate(Vector(bb.getPoint(0).x,bb.getPoint(0).y,bb.Center.z))
354
if isinstance(downsegment,Part.Shape):
355
downsegment.translate(Vector(bb.getPoint(0).x,bb.getPoint(0).y,bb.Center.z))
356
if hasattr(obj,"WaveOffset"):
357
if obj.WaveOffset.Value:
358
upsegment.translate(Vector(obj.WaveOffset.Value,0,0))
359
if isinstance(downsegment,Part.Shape):
360
downsegment.translate(Vector(obj.WaveOffset.Value,0,0))
364
for i in range(int(bb.XLength/(obj.WaveLength.Value*2))):
365
w1 = upsegment.copy()
366
w1.translate(Vector(obj.WaveLength.Value*2*i,0,0))
367
upedges.extend(w1.Edges)
368
if isinstance(downsegment,Part.Shape):
369
w2 = downsegment.copy()
370
w2.translate(Vector(obj.WaveLength.Value*2*i,0,0))
371
downedges.extend(w2.Edges)
372
upwire = Part.Wire(upedges)
373
FreeCAD.upwire = upwire # REMOVE
374
if isinstance(downsegment,Part.Shape):
375
downwire = Part.Wire(downedges)
376
FreeCAD.downwire = downwire # REMOVE
377
e1 = Part.LineSegment(upwire.Vertexes[0].Point,downwire.Vertexes[0].Point).toShape()
378
e2 = Part.LineSegment(upwire.Vertexes[-1].Point,downwire.Vertexes[-1].Point).toShape()
379
basewire = Part.Wire(upwire.Edges+[e1,e2]+downwire.Edges)
381
z = obj.Thickness.Value
382
if obj.WaveType == "Curved":
383
z += obj.WaveHeight.Value
384
p1 = upwire.Vertexes[0].Point
385
p2 = p1.add(Vector(0,0,-z))
386
p3 = Vector(upwire.Vertexes[-1].Point.x,upwire.Vertexes[-1].Point.y,p2.z)
387
p4 = upwire.Vertexes[-1].Point
388
w = Part.makePolygon([p1,p2,p3,p4])
389
basewire = Part.Wire(upwire.Edges+w.Edges)
391
FreeCAD.basewire = basewire
392
if not basewire.isClosed():
393
print("Error closing base wire - check FreeCAD.basewire")
396
baseface = Part.Face(basewire)
397
base = baseface.extrude(Vector(0,bb.YLength,0))
398
rot = FreeCAD.Rotation(FreeCAD.Vector(0,0,1),normal)
399
base.rotate(bb.Center,rot.Axis,math.degrees(rot.Angle))
400
if obj.WaveDirection.Value:
401
base.rotate(bb.Center,normal,obj.WaveDirection.Value)
402
n1 = normal.negative().normalize().multiply(obj.WaveHeight.Value*2)
403
self.vol = baseprofile.copy()
404
self.vol.translate(n1)
405
self.vol = self.vol.extrude(n1.negative().multiply(2))
406
base = self.vol.common(base)
407
base = base.removeSplitter()
409
FreeCAD.Console.PrintError(translate("Arch","Error computing shape of")+" "+obj.Label+"\n")
412
if base and (obj.Sheets > 1) and normal and thickness:
414
for i in range(1,obj.Sheets):
415
n = FreeCAD.Vector(normal).normalize().multiply(i*thickness)
419
base = Part.makeCompound(bases)
421
if base and normal and hasattr(obj,"Offset"):
423
v = DraftVecUtils.scaleTo(normal,obj.Offset.Value)
427
base = self.processSubShapes(obj,base,pl)
431
if not base.isNull():
432
if base.isValid() and base.Solids:
433
if len(base.Solids) == 1:
437
FreeCAD.Console.PrintError(translate("Arch","Couldn't compute a shape"))
439
base = base.removeSplitter()
445
class _ViewProviderPanel(ArchComponent.ViewProviderComponent):
447
"A View Provider for the Panel object"
449
def __init__(self,vobj):
451
ArchComponent.ViewProviderComponent.__init__(self,vobj)
452
vobj.ShapeColor = ArchCommands.getDefaultColor("Panel")
457
if hasattr(self,"Object"):
458
if hasattr(self.Object,"CloneOf"):
459
if self.Object.CloneOf:
460
return ":/icons/Arch_Panel_Clone.svg"
461
return ":/icons/Arch_Panel_Tree.svg"
463
def updateData(self,obj,prop):
465
if prop in ["Placement","Shape","Material"]:
466
if hasattr(obj,"Material"):
468
if hasattr(obj.Material,"Materials"):
469
activematerials = [obj.Material.Materials[i] for i in range(len(obj.Material.Materials)) if obj.Material.Thicknesses[i] >= 0]
470
if len(activematerials) == len(obj.Shape.Solids):
472
for i,mat in enumerate(activematerials):
473
c = obj.ViewObject.ShapeColor
474
c = (c[0],c[1],c[2],obj.ViewObject.Transparency/100.0)
475
if 'DiffuseColor' in mat.Material:
476
if "(" in mat.Material['DiffuseColor']:
477
c = tuple([float(f) for f in mat.Material['DiffuseColor'].strip("()").split(",")])
478
if 'Transparency' in mat.Material:
479
c = (c[0],c[1],c[2],float(mat.Material['Transparency']))
480
cols.extend([c for j in range(len(obj.Shape.Solids[i].Faces))])
481
if obj.ViewObject.DiffuseColor != cols:
482
obj.ViewObject.DiffuseColor = cols
483
ArchComponent.ViewProviderComponent.updateData(self,obj,prop)
486
class PanelCut(Draft.DraftObject):
488
"A flat, 2D view of an Arch Panel"
490
def __init__(self, obj):
491
Draft.DraftObject.__init__(self,obj)
494
# setProperties of ArchComponent will be overwritten
495
# thus setProperties from ArchComponent will be explicit called to get the properties
496
ArchComponent.ViewProviderComponent.setProperties(self, obj)
498
self.setProperties(obj)
500
def setProperties(self,obj):
502
pl = obj.PropertiesList
503
if not "Source" in pl:
504
obj.addProperty("App::PropertyLink","Source","PanelCut",QT_TRANSLATE_NOOP("App::Property","The linked object"))
505
if not "TagText" in pl:
506
obj.addProperty("App::PropertyString","TagText","PanelCut",QT_TRANSLATE_NOOP("App::Property","The text to display. Can be %tag%, %label% or %description% to display the panel tag or label"))
507
obj.TagText = "%tag%"
508
if not "TagSize" in pl:
509
obj.addProperty("App::PropertyLength","TagSize","PanelCut",QT_TRANSLATE_NOOP("App::Property","The size of the tag text"))
511
if not "TagPosition" in pl:
512
obj.addProperty("App::PropertyVector","TagPosition","PanelCut",QT_TRANSLATE_NOOP("App::Property","The position of the tag text. Keep (0,0,0) for center position"))
513
if not "TagRotation" in pl:
514
obj.addProperty("App::PropertyAngle","TagRotation","PanelCut",QT_TRANSLATE_NOOP("App::Property","The rotation of the tag text"))
515
if not "FontFile" in pl:
516
obj.addProperty("App::PropertyFile","FontFile","PanelCut",QT_TRANSLATE_NOOP("App::Property","The font of the tag text"))
517
obj.FontFile = params.get_param("FontFile")
518
if not "MakeFace" in pl:
519
obj.addProperty("App::PropertyBool","MakeFace","PanelCut",QT_TRANSLATE_NOOP("App::Property","If True, the object is rendered as a face, if possible."))
520
if not "AllowedAngles" in pl:
521
obj.addProperty("App::PropertyFloatList","AllowedAngles","PanelCut",QT_TRANSLATE_NOOP("App::Property","The allowed angles this object can be rotated to when placed on sheets"))
522
self.Type = "PanelCut"
523
if not "CutOffset" in pl:
524
obj.addProperty("App::PropertyDistance","CutOffset","PanelCut",QT_TRANSLATE_NOOP("App::Property","An offset value to move the cut plane from the center point"))
526
def onDocumentRestored(self,obj):
528
self.setProperties(obj)
530
def execute(self, obj):
536
if Draft.getType(obj.Source) == "Panel":
537
import DraftGeomUtils
540
if obj.Source.CloneOf:
541
baseobj = obj.Source.CloneOf.Base
543
baseobj = obj.Source.Base
545
if hasattr(baseobj,'Shape'):
546
if baseobj.Shape.Solids:
547
center = baseobj.Shape.BoundBox.Center
548
diag = baseobj.Shape.BoundBox.DiagonalLength
549
if obj.Source.Normal.Length:
550
n = obj.Source.Normal
551
elif baseobj.isDerivedFrom("Part::Extrusion"):
555
if hasattr(obj,"CutOffset") and obj.CutOffset.Value:
556
l = obj.CutOffset.Value
559
center = center.add(d)
560
plane = Part.makePlane(diag,diag,center,n)
561
plane.translate(center.sub(plane.BoundBox.Center))
563
for sol in baseobj.Shape.Solids:
564
s = sol.section(plane)
565
wires.extend(DraftGeomUtils.findWires(s.Edges))
567
base = self.buildCut(obj,wires)
569
base = self.buildCut(obj,baseobj.Shape.Wires)
571
n = DraftGeomUtils.getNormal(w)
577
base.translate(base.BoundBox.Center.negative())
578
r = FreeCAD.Rotation(n,Vector(0,0,1))
579
base.rotate(Vector(0,0,0),r.Axis,math.degrees(r.Angle))
580
elif baseobj.isDerivedFrom("Mesh::Feature"):
583
l2 = obj.Source.Length/2
584
w2 = obj.Source.Width/2
585
v1 = Vector(-l2,-w2,0)
586
v2 = Vector(l2,-w2,0)
588
v4 = Vector(-l2,w2,0)
589
base = Part.makePolygon([v1,v2,v3,v4,v1])
592
if obj.FontFile and obj.TagText and obj.TagSize.Value:
593
if obj.TagPosition.Length == 0:
594
pos = base.BoundBox.Center
596
pos = obj.TagPosition
597
if obj.TagText == "%tag%":
598
string = obj.Source.Tag
599
elif obj.TagText == "%label%":
600
string = obj.Source.Label
601
elif obj.TagText == "%description%":
602
string = obj.Source.Description
606
for char in Part.makeWireString(string,obj.FontFile,obj.TagSize.Value,0):
608
textshape = Part.Compound(chars)
609
textshape.translate(pos.sub(textshape.BoundBox.Center))
610
textshape.rotate(textshape.BoundBox.Center,Vector(0,0,1),obj.TagRotation.Value)
612
base = Part.Compound([base,textshape])
614
base = Part.Compound([base])
618
def buildCut(self,obj,wires):
620
"""buildCut(obj,wires): builds the object shape"""
623
if hasattr(obj,"MakeFace"):
630
if w.BoundBox.DiagonalLength > d:
631
d = w.BoundBox.DiagonalLength
636
if w.hashCode() != ow.hashCode():
638
face = face.cut(wface)
640
face = Part.Face(wires[0])
643
return Part.makeCompound(wires)
645
def getWires(self, obj):
647
"""getWires(obj): returns a tuple containing 3 shapes
648
that define the panel outline, the panel holes, and
649
tags (engravings): (outline,holes,tags). Any of these can
650
be None if nonexistent"""
655
if not hasattr(self,"outline"):
657
if not hasattr(self,"outline"):
659
outl = self.outline.copy()
660
if hasattr(self,"tag"):
661
tag = self.tag.copy()
663
tag.Placement = obj.Placement.multiply(tag.Placement)
665
outl = self.outline.copy()
666
outl.Placement = obj.Placement.multiply(outl.Placement)
667
if len(outl.Wires) > 1:
672
if w.BoundBox.DiagonalLength > d:
673
d = w.BoundBox.DiagonalLength
676
inl = Part.Compound([w for w in outl.Wires if w.hashCode() != ow.hashCode()])
677
outl = Part.Compound([ow])
680
outl = Part.Compound([outl.Wires[0]])
681
return (outl, inl, tag)
684
class ViewProviderPanelCut(Draft.ViewProviderDraft):
686
"a view provider for the panel cut object"
688
def __init__(self,vobj):
690
Draft.ViewProviderDraft.__init__(self,vobj)
691
self.setProperties(vobj)
693
def setProperties(self,vobj):
695
pl = vobj.PropertiesList
696
if not "Margin" in pl:
697
vobj.addProperty("App::PropertyLength","Margin","Arch",QT_TRANSLATE_NOOP("App::Property","A margin inside the boundary"))
698
if not "ShowMargin" in pl:
699
vobj.addProperty("App::PropertyBool","ShowMargin","Arch",QT_TRANSLATE_NOOP("App::Property","Turns the display of the margin on/off"))
701
def onDocumentRestored(self,vobj):
703
self.setProperties(vobj)
705
def attach(self,vobj):
707
Draft.ViewProviderDraft.attach(self,vobj)
708
from pivy import coin
709
self.coords = coin.SoCoordinate3()
710
self.lineset = coin.SoLineSet()
711
self.lineset.numVertices.setValue(-1)
712
lineStyle = coin.SoDrawStyle()
713
lineStyle.linePattern = 0x0f0f
714
self.color = coin.SoBaseColor()
715
self.switch = coin.SoSwitch()
716
sep = coin.SoSeparator()
717
self.switch.whichChild = -1
718
sep.addChild(self.color)
719
sep.addChild(lineStyle)
720
sep.addChild(self.coords)
721
sep.addChild(self.lineset)
722
self.switch.addChild(sep)
723
vobj.Annotation.addChild(self.switch)
724
self.onChanged(vobj,"ShowMargin")
725
self.onChanged(vobj,"LineColor")
727
def onChanged(self,vobj,prop):
729
if prop in ["Margin","ShowMargin"]:
730
if hasattr(vobj,"Margin") and hasattr(vobj,"ShowMargin"):
731
if (vobj.Margin.Value > 0) and vobj.Object.Shape and vobj.ShowMargin:
732
self.lineset.numVertices.setValue(-1)
733
if vobj.Object.Shape.Wires:
736
for w in vobj.Object.Shape.Wires:
737
if w.BoundBox.DiagonalLength > d:
738
d = w.BoundBox.DiagonalLength
741
ow = dw.makeOffset2D(vobj.Margin.Value)
743
for v in ow.OrderedVertexes:
744
v = vobj.Object.Placement.inverse().multVec(v.Point)
745
verts.append((v.x,v.y,v.z))
747
verts.append(verts[0])
748
self.coords.point.setValues(verts)
749
self.lineset.numVertices.setValue(len(verts))
750
self.switch.whichChild = 0
752
self.switch.whichChild = -1
753
elif prop == "LineColor":
754
if hasattr(vobj,"LineColor"):
756
self.color.rgb.setValue(c[0],c[1],c[2])
757
Draft.ViewProviderDraft.onChanged(self,vobj,prop)
759
def updateData(self,obj,prop):
761
if prop in ["Shape"]:
762
self.onChanged(obj.ViewObject,"Margin")
763
Draft.ViewProviderDraft.updateData(self,obj,prop)
765
def doubleClicked(self,vobj):
767
# See setEdit in ViewProviderDraft.
768
FreeCADGui.runCommand("Std_TransformManip")
771
class PanelSheet(Draft.DraftObject):
773
"A collection of Panel cuts under a sheet"
775
def __init__(self, obj):
777
Draft.DraftObject.__init__(self,obj)
779
self.setProperties(obj)
781
def setProperties(self,obj):
783
pl = obj.PropertiesList
784
if not "Group" in pl:
785
obj.addProperty("App::PropertyLinkList","Group","PanelSheet",QT_TRANSLATE_NOOP("App::Property","The linked Panel cuts"))
786
if not "TagText" in pl:
787
obj.addProperty("App::PropertyString","TagText","PanelSheet",QT_TRANSLATE_NOOP("App::Property","The tag text to display"))
788
if not "TagSize" in pl:
789
obj.addProperty("App::PropertyLength","TagSize","PanelSheet",QT_TRANSLATE_NOOP("App::Property","The size of the tag text"))
791
if not "TagPosition" in pl:
792
obj.addProperty("App::PropertyVector","TagPosition","PanelSheet",QT_TRANSLATE_NOOP("App::Property","The position of the tag text. Keep (0,0,0) for center position"))
793
if not "TagRotation" in pl:
794
obj.addProperty("App::PropertyAngle","TagRotation","PanelSheet",QT_TRANSLATE_NOOP("App::Property","The rotation of the tag text"))
795
if not "FontFile" in pl:
796
obj.addProperty("App::PropertyFile","FontFile","PanelSheet",QT_TRANSLATE_NOOP("App::Property","The font of the tag text"))
797
obj.FontFile = params.get_param("FontFile")
798
if not "Width" in pl:
799
obj.addProperty("App::PropertyLength","Width","PanelSheet",QT_TRANSLATE_NOOP("App::Property","The width of the sheet"))
800
obj.Width = params.get_param_arch("PanelLength")
801
if not "Height" in pl:
802
obj.addProperty("App::PropertyLength","Height","PanelSheet",QT_TRANSLATE_NOOP("App::Property","The height of the sheet"))
803
obj.Height = params.get_param_arch("PanelWidth")
804
if not "FillRatio" in pl:
805
obj.addProperty("App::PropertyPercent","FillRatio","PanelSheet",QT_TRANSLATE_NOOP("App::Property","The fill ratio of this sheet"))
806
obj.setEditorMode("FillRatio",2)
807
if not "MakeFace" in pl:
808
obj.addProperty("App::PropertyBool","MakeFace","PanelSheet",QT_TRANSLATE_NOOP("App::Property","If True, the object is rendered as a face, if possible."))
809
if not "GrainDirection" in pl:
810
obj.addProperty("App::PropertyAngle","GrainDirection","PanelSheet",QT_TRANSLATE_NOOP("App::Property","Specifies an angle for the wood grain (Clockwise, 0 is North)"))
811
if not "Scale" in pl:
812
obj.addProperty("App::PropertyFloat","Scale","PanelSheet", QT_TRANSLATE_NOOP("App::Property","Specifies the scale applied to each panel view."))
814
if not "Rotations" in pl:
815
obj.addProperty("App::PropertyFloatList","Rotations","PanelSheet", QT_TRANSLATE_NOOP("App::Property","A list of possible rotations for the nester"))
816
self.Type = "PanelSheet"
818
def onDocumentRestored(self, obj):
820
self.setProperties(obj)
822
def execute(self, obj):
826
self.sheetborder = None
828
if obj.Width.Value and obj.Height.Value:
829
l2 = obj.Width.Value/2
830
w2 = obj.Height.Value/2
831
v1 = Vector(-l2,-w2,0)
832
v2 = Vector(l2,-w2,0)
834
v4 = Vector(-l2,w2,0)
835
base = Part.makePolygon([v1,v2,v3,v4,v1])
836
if hasattr(obj,"MakeFace"):
838
base = Part.Face(base)
839
self.sheetborder = base
841
area = obj.Width.Value * obj.Height.Value
844
if hasattr(v,'Shape'):
845
wires.extend(v.Shape.Wires)
846
if Draft.getType(v) == "PanelCut":
848
subarea += v.Source.Area.Value
850
for w in v.Shape.Wires:
855
base = Part.Compound([base]+wires)
856
if obj.FontFile and obj.TagText and obj.TagSize.Value:
858
for char in Part.makeWireString(obj.TagText,obj.FontFile,obj.TagSize.Value,0):
860
textshape = Part.Compound(chars)
861
textshape.translate(obj.TagPosition)
862
textshape.rotate(textshape.BoundBox.Center,Vector(0,0,1),obj.TagRotation.Value)
863
self.sheettag = textshape
864
base = Part.Compound([base,textshape])
865
base.scale(obj.Scale, FreeCAD.Vector())
868
obj.FillRatio = int((subarea/area)*100)
870
def getOutlines(self,obj,transform=False):
872
"""getOutlines(obj,transform=False): returns a list of compounds whose wires define the
873
outlines of the panels in this sheet. If transform is True, the placement of
874
the sheet will be added to each wire"""
879
if hasattr(p,"Proxy"):
880
if hasattr(p.Proxy,"getWires"):
882
w = p.Proxy.getWires(p)
885
w.scale(obj.Scale, FreeCAD.Vector())
887
w.Placement = obj.Placement.multiply(w.Placement)
890
if hasattr(p,'Shape'):
891
for w in p.Shape.Wires:
892
w.scale(obj.Scale, FreeCAD.Vector())
894
w.Placement = obj.Placement.multiply(w.Placement)
898
def getHoles(self,obj,transform=False):
900
"""getHoles(obj,transform=False): returns a list of compound whose wires define the
901
holes contained in the panels in this sheet. If transform is True, the placement of
902
the sheet will be added to each wire"""
906
if hasattr(p,"Proxy"):
907
if hasattr(p.Proxy,"getWires"):
908
w = p.Proxy.getWires(p)
911
w.scale(obj.Scale, FreeCAD.Vector())
913
w.Placement = obj.Placement.multiply(w.Placement)
917
def getTags(self,obj,transform=False):
919
"""getTags(obj,transform=False): returns a list of compounds whose wires define the
920
tags (engravings) contained in the panels in this sheet and the sheet intself.
921
If transform is True, the placement of the sheet will be added to each wire.
922
Warning, the wires returned by this function may not be closed,
923
depending on the font"""
927
if hasattr(p,"Proxy"):
928
if hasattr(p.Proxy,"getWires"):
929
w = p.Proxy.getWires(p)
932
w.scale(obj.Scale, FreeCAD.Vector())
934
w.Placement = obj.Placement.multiply(w.Placement)
936
if self.sheettag is not None:
937
w = self.sheettag.copy()
938
w.scale(obj.Scale, FreeCAD.Vector())
940
w.Placement = obj.Placement.multiply(w.Placement)
946
class ViewProviderPanelSheet(Draft.ViewProviderDraft):
948
"a view provider for the panel sheet object"
950
def __init__(self,vobj):
952
Draft.ViewProviderDraft.__init__(self,vobj)
953
self.setProperties(vobj)
954
vobj.PatternSize = 0.0035
956
def setProperties(self,vobj):
958
pl = vobj.PropertiesList
959
if not "Margin" in pl:
960
vobj.addProperty("App::PropertyLength","Margin","PanelSheet",QT_TRANSLATE_NOOP("App::Property","A margin inside the boundary"))
961
if not "ShowMargin" in pl:
962
vobj.addProperty("App::PropertyBool","ShowMargin","PanelSheet",QT_TRANSLATE_NOOP("App::Property","Turns the display of the margin on/off"))
963
if not "ShowGrain" in pl:
964
vobj.addProperty("App::PropertyBool","ShowGrain","PanelSheet",QT_TRANSLATE_NOOP("App::Property","Turns the display of the wood grain texture on/off"))
966
def onDocumentRestored(self,vobj):
968
self.setProperties(vobj)
972
return ":/icons/Arch_Panel_Sheet_Tree.svg"
974
def setEdit(self, vobj, mode):
975
if mode == 1 or mode == 2:
978
taskd = SheetTaskPanel(vobj.Object)
980
FreeCADGui.Control.showDialog(taskd)
983
def unsetEdit(self, vobj, mode):
984
if mode == 1 or mode == 2:
987
FreeCADGui.Control.closeDialog()
990
def attach(self,vobj):
992
Draft.ViewProviderDraft.attach(self,vobj)
993
from pivy import coin
994
self.coords = coin.SoCoordinate3()
995
self.lineset = coin.SoLineSet()
996
self.lineset.numVertices.setValue(-1)
997
lineStyle = coin.SoDrawStyle()
998
lineStyle.linePattern = 0x0f0f
999
self.color = coin.SoBaseColor()
1000
self.switch = coin.SoSwitch()
1001
sep = coin.SoSeparator()
1002
self.switch.whichChild = -1
1003
sep.addChild(self.color)
1004
sep.addChild(lineStyle)
1005
sep.addChild(self.coords)
1006
sep.addChild(self.lineset)
1007
self.switch.addChild(sep)
1008
vobj.Annotation.addChild(self.switch)
1009
self.onChanged(vobj,"ShowMargin")
1010
self.onChanged(vobj,"LineColor")
1012
def onChanged(self,vobj,prop):
1014
if prop in ["Margin","ShowMargin"]:
1015
if hasattr(vobj,"Margin") and hasattr(vobj,"ShowMargin"):
1016
if (vobj.Margin.Value > 0) and (vobj.Margin.Value < vobj.Object.Width.Value/2) and (vobj.Margin.Value < vobj.Object.Height.Value/2):
1017
l2 = vobj.Object.Width.Value/2
1018
w2 = vobj.Object.Height.Value/2
1019
v = vobj.Margin.Value
1020
v1 = (-l2+v,-w2+v,0)
1024
self.coords.point.setValues([v1,v2,v3,v4,v1])
1025
self.lineset.numVertices.setValue(5)
1027
self.switch.whichChild = 0
1029
self.switch.whichChild = -1
1030
elif prop == "LineColor":
1031
if hasattr(vobj,"LineColor"):
1033
self.color.rgb.setValue(c[0],c[1],c[2])
1034
elif prop == "ShowGrain":
1035
if hasattr(vobj,"ShowGrain"):
1037
vobj.Pattern = "woodgrain"
1039
vobj.Pattern = "None"
1040
Draft.ViewProviderDraft.onChanged(self,vobj,prop)
1043
def updateData(self,obj,prop):
1045
if prop in ["Width","Height"]:
1046
self.onChanged(obj.ViewObject,"Margin")
1047
elif prop == "GrainDirection":
1048
if hasattr(self,"texcoords"):
1050
s = FreeCAD.Vector(self.texcoords.directionS.getValue().getValue()).Length
1051
vS = DraftVecUtils.rotate(FreeCAD.Vector(s,0,0),-math.radians(obj.GrainDirection.Value))
1052
vT = DraftVecUtils.rotate(FreeCAD.Vector(0,s,0),-math.radians(obj.GrainDirection.Value))
1053
self.texcoords.directionS.setValue(vS.x,vS.y,vS.z)
1054
self.texcoords.directionT.setValue(vT.x,vT.y,vT.z)
1055
Draft.ViewProviderDraft.updateData(self,obj,prop)
1058
class SheetTaskPanel(ArchComponent.ComponentTaskPanel):
1060
def __init__(self,obj):
1062
ArchComponent.ComponentTaskPanel.__init__(self)
1064
self.optwid = QtGui.QWidget()
1065
self.optwid.setWindowTitle(QtGui.QApplication.translate("Arch", "Tools", None))
1066
lay = QtGui.QVBoxLayout(self.optwid)
1067
self.editButton = QtGui.QPushButton(self.optwid)
1068
self.editButton.setIcon(QtGui.QIcon(":/icons/Draft_Edit.svg"))
1069
self.editButton.setText(QtGui.QApplication.translate("Arch", "Edit views positions", None))
1070
lay.addWidget(self.editButton)
1071
QtCore.QObject.connect(self.editButton, QtCore.SIGNAL("clicked()"), self.editNodes)
1072
self.form = [self.form,self.optwid]
1074
def editNodes(self):
1076
FreeCADGui.Control.closeDialog()
1077
FreeCADGui.runCommand("Draft_Edit")