<commit_before>#***************************************************************************
#*                                                                         *
#*   Copyright (c) 2011                                                    *
#*   <NAME> <<EMAIL>>                                 *
#*                                                                         *
#*   This program is free software; you can redistribute it and/or modify  *
#*   it under the terms of the GNU Lesser General Public License (LGPL)    *
#*   as published by the Free Software Foundation; either version 2 of     *
#*   the License, or (at your option) any later version.                   *
#*   for detail see the LICENCE text file.                                 *
#*                                                                         *
#*   This program is distributed in the hope that it will be useful,       *
#*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
#*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
#*   GNU Library General Public License for more details.                  *
#*                                                                         *
#*   You should have received a copy of the GNU Library General Public     *
#*   License along with this program; if not, write to the Free Software   *
#*   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
#*   USA                                                                   *
#*                                                                         *
#***************************************************************************

import FreeCAD,Draft,ArchComponent,DraftVecUtils,ArchCommands,math, Part, ArchNesting
from FreeCAD import Vector
if FreeCAD.GuiUp:
    import FreeCADGui
    from PySide import QtCore, QtGui
    from DraftTools import translate
    from PySide.QtCore import QT_TRANSLATE_NOOP
else:
    # \cond
    def translate(ctxt,txt):
        return txt
    def QT_TRANSLATE_NOOP(ctxt,txt):
        return txt
    # \endcond

## @package ArchPanel
#  \ingroup ARCH
#  \brief The Panel object and tools
#
#  This module provides tools to build Panel objects.
#  Panels consist of a closed shape that gets extruded to
#  produce a flat object.


__title__="FreeCAD Panel"
__author__ = "<NAME>"
__url__ = "http://www.freecadweb.org"

#           Description                 l    w    t

Presets = [None,
           ["Plywood 12mm, 1220 x 2440",1220,2440,12],
           ["Plywood 15mm, 1220 x 2440",1220,2440,15],
           ["Plywood 18mm, 1220 x 2440",1220,2440,18],
           ["Plywood 25mm, 1220 x 2440",1220,2440,25],
           ["MDF 3mm, 900 x 600",       900, 600, 3],
           ["MDF 6mm, 900 x 600",       900, 600, 6],
           ["OSB 18mm, 1220 x 2440",    1220,2440,18],
           ]



def makePanel(baseobj=None,length=0,width=0,thickness=0,placement=None,name="Panel"):

    '''makePanel([obj],[length],[width],[thickness],[placement]): creates a
    panel element based on the given profile object and the given
    extrusion thickness. If no base object is given, you can also specify
    length and width for a simple cubic object.'''

    if not FreeCAD.ActiveDocument:
        FreeCAD.Console.PrintError("No active document. Aborting\n")
        return
    obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Panel")
    obj.Label = translate("Arch",name)
    _Panel(obj)
    if FreeCAD.GuiUp:
        _ViewProviderPanel(obj.ViewObject)
    if baseobj:
        obj.Base = baseobj
        if FreeCAD.GuiUp:
            obj.Base.ViewObject.hide()
    if width:
        obj.Width = width
    if thickness:
        obj.Thickness = thickness
    if length:
        obj.Length = length
    return obj


def makePanelView(panel,page=None,name="PanelView"):

    """makePanelView(panel,[page]) : Creates a Drawing view of the given panel
    in the given or active Page object (a new page will be created if none exists)."""

    if not page:
        for o in FreeCAD.ActiveDocument.Objects:
            if o.isDerivedFrom("Drawing::FeaturePage"):
                page = o
                break
        if not page:
            page = FreeCAD.ActiveDocument.addObject("Drawing::FeaturePage",translate("Arch","Page"))
            page.Template = Draft.getParam("template",FreeCAD.getResourceDir()+'Mod/Drawing/Templates/A3_Landscape.svg')
    view = FreeCAD.ActiveDocument.addObject("Drawing::FeatureViewPython",name)
    page.addObject(view)
    PanelView(view)
    view.Source = panel
    view.Label = translate("Arch","View of")+" "+panel.Label
    return view


def makePanelCut(panel,name="PanelView"):

    """makePanelCut(panel) : Creates a 2D view of the given panel
    in the 3D space, positioned at the origin."""

    view = FreeCAD.ActiveDocument.addObject("Part::FeaturePython",name)
    PanelCut(view)
    view.Source = panel
    view.Label = translate("Arch","View of")+" "+panel.Label
    if FreeCAD.GuiUp:
        ViewProviderPanelCut(view.ViewObject)
    return view


def makePanelSheet(panels=[],name="PanelSheet"):

    """makePanelSheet([panels]) : Creates a sheet with the given panel cuts
    in the 3D space, positioned at the origin."""

    sheet = FreeCAD.ActiveDocument.addObject("Part::FeaturePython",name)
    PanelSheet(sheet)
    if panels:
        sheet.Group = panels
    if FreeCAD.GuiUp:
        ViewProviderPanelSheet(sheet.ViewObject)
    return sheet


class CommandPanel:

    "the Arch Panel command definition"

    def GetResources(self):

        return {'Pixmap'  : 'Arch_Panel',
                'MenuText': QT_TRANSLATE_NOOP("Arch_Panel","Panel"),
                'Accel': "P, A",
                'ToolTip': QT_TRANSLATE_NOOP("Arch_Panel","Creates a panel object from scratch or from a selected object (sketch, wire, face or solid)")}

    def IsActive(self):

        return not FreeCAD.ActiveDocument is None

    def Activated(self):

        p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch")
        self.Length = p.GetFloat("PanelLength",1000)
        self.Width = p.GetFloat("PanelWidth",1000)
        self.Thickness = p.GetFloat("PanelThickness",10)
        self.Profile = None
        self.continueCmd = False
        self.rotated = False
        sel = FreeCADGui.Selection.getSelection()
        if sel:
            if len(sel) == 1:
                if Draft.getType(sel[0]) == "Panel":
                    return
            FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Panel")))
            FreeCADGui.addModule("Arch")
            FreeCADGui.addModule("Draft")
            for obj in sel:
                FreeCADGui.doCommand("obj = Arch.makePanel(FreeCAD.ActiveDocument." + obj.Name + ",thickness=" + str(self.Thickness) + ")")
                FreeCADGui.doCommand("Draft.autogroup(obj)")
            FreeCAD.ActiveDocument.commitTransaction()
            FreeCAD.ActiveDocument.recompute()
            return

        # interactive mode
        if hasattr(FreeCAD,"DraftWorkingPlane"):
            FreeCAD.DraftWorkingPlane.setup()
        import DraftTrackers
        self.points = []
        self.tracker = DraftTrackers.boxTracker()
        self.tracker.width(self.Width)
        self.tracker.height(self.Thickness)
        self.tracker.length(self.Length)
        self.tracker.on()
        FreeCADGui.Snapper.getPoint(callback=self.getPoint,movecallback=self.update,extradlg=self.taskbox())

    def getPoint(self,point=None,obj=None):

        "this function is called by the snapper when it has a 3D point"

        self.tracker.finalize()
        if point == None:
            return
        FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create Panel"))
        FreeCADGui.addModule("Arch")
        if self.Profile:
            pr = Presets[self.Profile]
            FreeCADGui.doCommand('p = Arch.makeProfile('+str(pr[2])+','+str(pr[3])+','+str(pr[4])+','+str(pr[5])+')')
            FreeCADGui.doCommand('s = Arch.makePanel(p,thickness='+str(self.Thickness)+')')
            #FreeCADGui.doCommand('s.Placement.Rotation = FreeCAD.Rotation(-0.5,0.5,-0.5,0.5)')
        else:
            FreeCADGui.doCommand('s = Arch.makePanel(length='+str(self.Length)+',width='+str(self.Width)+',thickness='+str(self.Thickness)+')')
        FreeCADGui.doCommand('s.Placement.Base = '+DraftVecUtils.toString(point))
        if self.rotated:
            FreeCADGui.doCommand('s.Placement.Rotation = FreeCAD.Rotation(FreeCAD.Vector(1.00,0.00,0.00),90.00)')
        FreeCAD.ActiveDocument.commitTransaction()
        FreeCAD.ActiveDocument.recompute()
        if self.continueCmd:
            self.Activated()

    def taskbox(self):

        "sets up a taskbox widget"

        w = QtGui.QWidget()
        ui = FreeCADGui.UiLoader()
        w.setWindowTitle(translate("Arch","Panel options", utf8_decode=True))
        grid = QtGui.QGridLayout(w)

        # presets box
        labelp = QtGui.QLabel(translate("Arch","Preset", utf8_decode=True))
        valuep = QtGui.QComboBox()
        fpresets = [" "]
        for p in Presets[1:]:
            fpresets.append(translate("Arch",p[0]))
        valuep.addItems(fpresets)
        grid.addWidget(labelp,0,0,1,1)
        grid.addWidget(valuep,0,1,1,1)

        # length
        label1 = QtGui.QLabel(translate("Arch","Length", utf8_decode=True))
        self.vLength = ui.createWidget("Gui::InputField")
        self.vLength.setText(FreeCAD.Units.Quantity(self.Length,FreeCAD.Units.Length).UserString)
        grid.addWidget(label1,1,0,1,1)
        grid.addWidget(self.vLength,1,1,1,1)

        # width
        label2 = QtGui.QLabel(translate("Arch","Width", utf8_decode=True))
        self.vWidth = ui.createWidget("Gui::InputField")
        self.vWidth.setText(FreeCAD.Units.Quantity(self.Width,FreeCAD.Units.Length).UserString)
        grid.addWidget(label2,2,0,1,1)
        grid.addWidget(self.vWidth,2,1,1,1)

        # height
        label3 = QtGui.QLabel(translate("Arch","Thickness", utf8_decode=True))
        self.vHeight = ui.createWidget("Gui::InputField")
        self.vHeight.setText(FreeCAD.Units.Quantity(self.Thickness,FreeCAD.Units.Length).UserString)
        grid.addWidget(label3,3,0,1,1)
        grid.addWidget(self.vHeight,3,1,1,1)

        # horizontal button
        value5 = QtGui.QPushButton(translate("Arch","Rotate", utf8_decode=True))
        grid.addWidget(value5,4,0,1,2)

        # continue button
        label4 = QtGui.QLabel(translate("Arch","Con&tinue", utf8_decode=True))
        value4 = QtGui.QCheckBox()
        value4.setObjectName("ContinueCmd")
        value4.setLayoutDirection(QtCore.Qt.RightToLeft)
        label4.setBuddy(value4)
        if hasattr(FreeCADGui,"draftToolBar"):
            value4.setChecked(FreeCADGui.draftToolBar.continueMode)
            self.continueCmd = FreeCADGui.draftToolBar.continueMode
        grid.addWidget(label4,5,0,1,1)
        grid.addWidget(value4,5,1,1,1)

        QtCore.QObject.connect(valuep,QtCore.SIGNAL("currentIndexChanged(int)"),self.setPreset)
        QtCore.QObject.connect(self.vLength,QtCore.SIGNAL("valueChanged(double)"),self.setLength)
        QtCore.QObject.connect(self.vWidth,QtCore.SIGNAL("valueChanged(double)"),self.setWidth)
        QtCore.QObject.connect(self.vHeight,QtCore.SIGNAL("valueChanged(double)"),self.setThickness)
        QtCore.QObject.connect(value4,QtCore.SIGNAL("stateChanged(int)"),self.setContinue)
        QtCore.QObject.connect(value5,QtCore.SIGNAL("pressed()"),self.rotate)
        return w

    def update(self,point,info):

        "this function is called by the Snapper when the mouse is moved"

        if FreeCADGui.Control.activeDialog():
            self.tracker.pos(point)
            if self.rotated:
                self.tracker.width(self.Thickness)
                self.tracker.height(self.Width)
                self.tracker.length(self.Length)
            else:
                self.tracker.width(self.Width)
                self.tracker.height(self.Thickness)
                self.tracker.length(self.Length)

    def setWidth(self,d):

        self.Width = d
        FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").SetFloat("PanelWidth",d)

    def setThickness(self,d):

        self.Thickness = d
        FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").SetFloat("PanelThickness",d)

    def setLength(self,d):

        self.Length = d
        FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").SetFloat("PanelLength",d)

    def setContinue(self,i):

        self.continueCmd = bool(i)
        if hasattr(FreeCADGui,"draftToolBar"):
            FreeCADGui.draftToolBar.continueMode = bool(i)

    def setPreset(self,i):

        if i > 0:
            self.vLength.setText(FreeCAD.Units.Quantity(float(Presets[i][1]),FreeCAD.Units.Length).UserString)
            self.vWidth.setText(FreeCAD.Units.Quantity(float(Presets[i][2]),FreeCAD.Units.Length).UserString)
            self.vHeight.setText(FreeCAD.Units.Quantity(float(Presets[i][3]),FreeCAD.Units.Length).UserString)

    def rotate(self):

        self.rotated = not self.rotated


class CommandPanelCut:

    "the Arch Panel Cut command definition"

    def GetResources(self):

        return {'Pixmap'  : 'Arch_Panel_Cut',
                'MenuText': QT_TRANSLATE_NOOP("Arch_Panel_Cut","Panel Cut"),
                'Accel': "P, C",
                'ToolTip': QT_TRANSLATE_NOOP("Arch_Panel_Sheet","Creates 2D views of selected panels")}

    def IsActive(self):

        return not FreeCAD.ActiveDocument is None

    def Activated(self):

        if FreeCADGui.Selection.getSelection():
            FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Panel Cut")))
            FreeCADGui.addModule("Arch")
            for obj in FreeCADGui.Selection.getSelection():
                if Draft.getType(obj) == "Panel":
                    FreeCADGui.doCommand("Arch.makePanelCut(FreeCAD.ActiveDocument."+obj.Name+")")
            FreeCAD.ActiveDocument.commitTransaction()
            FreeCAD.ActiveDocument.recompute()


class CommandPanelSheet:

    "the Arch Panel Sheet command definition"

    def GetResources(self):

        return {'Pixmap'  : 'Arch_Panel_Sheet',
                'MenuText': QT_TRANSLATE_NOOP("Arch_Panel_Sheet","Panel Sheet"),
                'Accel': "P, S",
                'ToolTip': QT_TRANSLATE_NOOP("Arch_Panel_Sheet","Creates a 2D sheet which can contain panel cuts")}

    def IsActive(self):

        return not FreeCAD.ActiveDocument is None

    def Activated(self):

        FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Panel Sheet")))
        FreeCADGui.addModule("Arch")
        if FreeCADGui.Selection.getSelection():
            l = "["
            for obj in FreeCADGui.Selection.getSelection():
                l += "FreeCAD.ActiveDocument."+obj.Name+","
            l += "]"
            FreeCAD.ActiveDocument.commitTransaction()
            FreeCAD.ActiveDocument.recompute()
            FreeCADGui.doCommand("__objs__ = "+l)
            FreeCADGui.doCommand("Arch.makePanelSheet(__objs__)")
            FreeCADGui.doCommand("del __objs__")
        else:
            FreeCADGui.doCommand("Arch.makePanelSheet()")
        FreeCAD.ActiveDocument.commitTransaction()
        FreeCAD.ActiveDocument.recompute()


class _Panel(ArchComponent.Component):

    "The Panel object"

    def __init__(self,obj):

        ArchComponent.Component.__init__(self,obj)
        self.setProperties(obj)
        obj.IfcType = "Plate"

    def setProperties(self,obj):

        pl = obj.PropertiesList
        if not "Length" in pl:
            obj.addProperty("App::PropertyLength","Length","Panel",   QT_TRANSLATE_NOOP("App::Property","The length of this element, if not based on a profile"))
        if not "Width" in pl:
            obj.addProperty("App::PropertyLength","Width","Panel",    QT_TRANSLATE_NOOP("App::Property","The width of this element, if not based on a profile"))
        if not "Thickness" in pl:
            obj.addProperty("App::PropertyLength","Thickness","Panel",QT_TRANSLATE_NOOP("App::Property","The thickness or extrusion depth of this element"))
        if not "Sheets" in pl:
            obj.addProperty("App::PropertyInteger","Sheets","Panel",  QT_TRANSLATE_NOOP("App::Property","The number of sheets to use"))
            obj.Sheets = 1
        if not "Offset" in pl:
            obj.addProperty("App::PropertyDistance","Offset","Panel",   QT_TRANSLATE_NOOP("App::Property","The offset between this panel and its baseline"))
        if not "WaveLength" in pl:
            obj.addProperty("App::PropertyLength","WaveLength","Panel", QT_TRANSLATE_NOOP("App::Property","The length of waves for corrugated elements"))
        if not "WaveHeight" in pl:
            obj.addProperty("App::PropertyLength","WaveHeight","Panel", QT_TRANSLATE_NOOP("App::Property","The height of waves for corrugated elements"))
        if not "WaveOffset" in pl:
            obj.addProperty("App::PropertyDistance","WaveOffset","Panel", QT_TRANSLATE_NOOP("App::Property","The horizontal offset of waves for corrugated elements"))
        if not "WaveDirection" in pl:
            obj.addProperty("App::PropertyAngle","WaveDirection","Panel", QT_TRANSLATE_NOOP("App::Property","The direction of waves for corrugated elements"))
        if not "WaveType" in pl:
            obj.addProperty("App::PropertyEnumeration","WaveType","Panel", QT_TRANSLATE_NOOP("App::Property","The type of waves for corrugated elements"))
            obj.WaveType = ["Curved","Trapezoidal","Spikes"]
        if not "WaveBottom" in pl:
            obj.addProperty("App::PropertyBool","WaveBottom","Panel", QT_TRANSLATE_NOOP("App::Property","If the wave also affects the bottom side or not"))
        if not "Area" in pl:
            obj.addProperty("App::PropertyArea","Area","Panel",       QT_TRANSLATE_NOOP("App::Property","The area of this panel"))
        if not "FaceMaker" in pl:
            obj.addProperty("App::PropertyEnumeration","FaceMaker","Panel",QT_TRANSLATE_NOOP("App::Property","The facemaker type to use to build the profile of this object"))
            obj.FaceMaker = ["None","Simple","Cheese","Bullseye"]
        if not "Normal" in pl:
            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)"))
        self.Type = "Panel"
        obj.setEditorMode("VerticalArea",2)
        obj.setEditorMode("HorizontalArea",2)

    def onDocumentRestored(self,obj):

        ArchComponent.Component.onDocumentRestored(self,obj)
        self.setProperties(obj)

    def execute(self,obj):

        "creates the panel shape"

        if self.clone(obj):
            return

        import Part #, DraftGeomUtils

        layers = []
        length = 0
        width = 0
        thickness = 0

        # base tests
        if obj.Base:
            if obj.Base.isDerivedFrom("Part::Feature"):
                if obj.Base.Shape.isNull():
                    return
            elif obj.Base.isDerivedFrom("Mesh::Feature"):
                if not obj.Base.Mesh.isSolid():
                    return
        else:
            if obj.Length.Value:
                length = obj.Length.Value
            else:
                return
            if obj.Width.Value:
                width = obj.Width.Value
            else:
                return
        if obj.Thickness.Value:
            thickness = obj.Thickness.Value
        else:
            if not obj.Base:
                return
            elif obj.Base.isDerivedFrom("Part::Feature"):
                if not obj.Base.Shape.Solids:
                    return
        if hasattr(obj,"Material"):
            if obj.Material:
                if hasattr(obj.Material,"Materials"):
                    varwidth = 0
                    restwidth = thickness - sum(obj.Material.Thicknesses)
                    if restwidth > 0:
                        varwidth = [t for t in obj.Material.Thicknesses if t == 0]
                        if varwidth:
                            varwidth = restwidth/len(varwidth)
                    for t in obj.Material.Thicknesses:
                        if t:
                            layers.append(t)
                        elif varwidth:
                            layers.append(varwidth)
        # creating base shape
        pl = obj.Placement
        base = None
        normal = None
        if hasattr(obj,"Normal"):
            if obj.Normal.Length > 0:
                normal = Vector(obj.Normal)
                normal.normalize()
                normal.multiply(thickness)
        baseprofile = None
        if obj.Base:
            base = obj.Base.Shape.copy()
            if not base.Solids:
               # p = FreeCAD.Placement(obj.Base.Placement)
                if base.Faces:
                    baseprofile = base
                    if not normal:
                        normal = baseprofile.Faces[0].normalAt(0,0).multiply(thickness)
                    if layers:
                        layeroffset = 0
                        shps = []
                        for l in layers:
                            n = Vector(normal).normalize().multiply(l)
                            b = base.extrude(n)
                            if layeroffset:
                                o = Vector(normal).normalize().multiply(layeroffset)
                                b.translate(o)
                            shps.append(b)
                            layeroffset += l
                        base = Part.makeCompound(shps)
                    else:
                        base = base.extrude(normal)
                elif base.Wires:
                    fm = False
                    if hasattr(obj,"FaceMaker"):
                        if obj.FaceMaker != "None":
                            try:
                                baseprofile = Part.makeFace(base.Wires,"Part::FaceMaker"+str(obj.FaceMaker))
                                fm = True
                            except:
                                FreeCAD.Console.PrintError(translate("Arch","Facemaker returned an error")+"\n")
                                return
                    if not fm:
                        closed = True
                        for w in base.Wires:
                            if not w.isClosed():
                                closed = False
                        if closed:
                            baseprofile = ArchCommands.makeFace(base.Wires)
                    if not normal:
                        normal = baseprofile.normalAt(0,0).multiply(thickness)
                    if layers:
                        layeroffset = 0
                        shps = []
                        for l in layers:
                            n = Vector(normal).normalize().multiply(l)
                            b = baseprofile.extrude(n)
                            if layeroffset:
                                o = Vector(normal).normalize().multiply(layeroffset)
                                b.translate(o)
                            shps.append(b)
                            layeroffset += l
                        base = Part.makeCompound(shps)
                    else:
                        base = baseprofile.extrude(normal)
                elif obj.Base.isDerivedFrom("Mesh::Feature"):
                    if obj.Base.Mesh.isSolid():
                        if obj.Base.Mesh.countComponents() == 1:
                            sh = ArchCommands.getShapeFromMesh(obj.Base.Mesh)
                            if sh.isClosed() and sh.isValid() and sh.Solids:
                                base = sh
        else:
            if layers:
                shps = []
                layeroffset = 0
                for l in layers:
                    if normal:
                        n = Vector(normal).normalize().multiply(l)
                    else:
                        n = Vector(0,0,1).multiply(l)
                    l2 = length/2 or 0.5
                    w2 = width/2 or 0.5
                    v1 = Vector(-l2,-w2,layeroffset)
                    v2 = Vector(l2,-w2,layeroffset)
                    v3 = Vector(l2,w2,layeroffset)
                    v4 = Vector(-l2,w2,layeroffset)
                    base = Part.makePolygon([v1,v2,v3,v4,v1])
                    baseprofile = Part.Face(base)
                    base = baseprofile.extrude(n)
                    shps.append(base)
                    layeroffset += l
                base = Part.makeCompound(shps)
            else:
                if not normal:
                    normal = Vector(0,0,1).multiply(thickness)
                l2 = length/2 or 0.5
                w2 = width/2 or 0.5
                v1 = Vector(-l2,-w2,0)
                v2 = Vector(l2,-w2,0)
                v3 = Vector(l2,w2,0)
                v4 = Vector(-l2,w2,0)
                base = Part.makePolygon([v1,v2,v3,v4,v1])
                baseprofile = Part.Face(base)
                base = baseprofile.extrude(normal)

        if hasattr(obj,"Area"):
            if baseprofile:
                obj.Area = baseprofile.Area

        if hasattr(obj,"WaveLength"):
            if baseprofile and obj.WaveLength.Value and obj.WaveHeight.Value:
                # corrugated element
                bb = baseprofile.BoundBox
                bb.enlarge(bb.DiagonalLength)
                downsegment = None
                if hasattr(obj,"WaveBottom"):
                    if not obj.WaveBottom:
                        if obj.WaveType == "Curved":
                            if obj.Thickness.Value > obj.WaveHeight.Value:
                                downsegment = obj.Thickness.Value
                            else:
                                downsegment = obj.WaveHeight.Value + obj.Thickness.Value
                        else:
                            downsegment = obj.Thickness.Value
                p1 = Vector(0,0,0)
                p5 = Vector(obj.WaveLength.Value*2,0,0)
                if obj.WaveType == "Curved":
                    p2 = Vector(obj.WaveLength.Value/2,0,obj.WaveHeight.Value)
                    p3 = Vector(obj.WaveLength.Value,0,0)
                    e1 = Part.Arc(p1,p2,p3).toShape()
                    p4 = Vector(obj.WaveLength.Value*1.5,0,-obj.WaveHeight.Value)
                    e2 = Part.Arc(p3,p4,p5).toShape()
                    upsegment = Part.Wire([e1,e2])
                    if not downsegment:
                        if obj.Thickness.Value < e1.Curve.Radius:
                            c3 = e1.Curve.copy()
                            c3.Radius = e1.Curve.Radius-obj.Thickness.Value
                            e3 = Part.Arc(c3,e1.FirstParameter,e1.LastParameter).toShape()
                            c4 = e2.Curve.copy()
                            c4.Radius = e2.Curve.Radius+obj.Thickness.Value
                            e4 = Part.Arc(c4,e2.FirstParameter,e2.LastParameter).toShape()
                            downsegment = Part.Wire([e3,e4])
                        else:
                            r = e2.Curve.Radius+obj.Thickness.Value
                            z = math.sqrt(r^2 - obj.WaveLength.Value^2)
                            p6 = e2.Curve.Center.add(Vector(-obj.WaveLength,0,-z))
                            p7 = e2.Curve.Center.add(Vector(0,0,-r))
                            p8 = e2.Curve.Center.add(Vector(obj.WaveLength,0,-z))
                            downsegment = Part.Arc(p6,p7,p8).toShape()

                elif obj.WaveType == "Trapezoidal":
                    p2 = Vector(obj.WaveLength.Value/4,0,obj.WaveHeight.Value)
                    p3 = Vector(obj.WaveLength.Value,0,obj.WaveHeight.Value)
                    p4 = Vector(obj.WaveLength.Value*1.25,0,0)
                    upsegment = Part.makePolygon([p1,p2,p3,p4,p5])
                    if not downsegment:
                        a = ((p1.sub(p2)).getAngle(p3.sub(p2)))/2
                        tx = obj.Thickness.Value*math.tan(a)
                        d1 = Vector(tx,0,-obj.Thickness.Value)
                        d2 = Vector(-tx,0,-obj.Thickness.Value)
                        p6 = p1.add(d1)
                        if tx >= p3.sub(p2).Length/2:
                            d3 = p2.sub(p1)
                            d3.normalize()
                            d3.multiply((0.625*obj.WaveLength.Value)/d3.x)
                            d4 = Vector(d3.x,0,-d3.z)
                            p7 = p6.add(d3)
                            p8 = p7.add(d4)
                            p9 = p5.add(d1)
                            downsegment = Part.makePolygon([p6,p7,p8,p9])
                        elif tx <= 0.625*obj.WaveLength.Value:
                            p7 = p2.add(d1)
                            p8 = p3.add(d2)
                            p9 = p4.add(d2)
                            p10 = p5.add(d1)
                            downsegment = Part.makePolygon([p6,p7,p8,p9,p10])
                        else:
                            downsegment = obj.Thickness.Value

                else: # spike
                    p2 = Vector(obj.WaveHeight.Value,0,obj.WaveHeight.Value)
                    p3 = Vector(obj.WaveHeight.Value*2,0,0)
                    upsegment = Part.makePolygon([p1,p2,p3,p5])
                    if not downsegment:
                        downsegment = obj.Thickness.Value

                upsegment.translate(Vector(bb.getPoint(0).x,bb.getPoint(0).y,bb.Center.z))
                if isinstance(downsegment,Part.Shape):
                    downsegment.translate(Vector(bb.getPoint(0).x,bb.getPoint(0).y,bb.Center.z))
                if hasattr(obj,"WaveOffset"):
                    if obj.WaveOffset.Value:
                        upsegment.translate(Vector(obj.WaveOffset.Value,0,0))
                        if isinstance(downsegment,Part.Shape):
                            downsegment.translate(Vector(obj.WaveOffset.Value,0,0))

                upedges = []
                downedges = []
                for i in range(int(bb.XLength/(obj.WaveLength.Value*2))):
                    w1 = upsegment.copy()
                    w1.translate(Vector(obj.WaveLength.Value*2*i,0,0))
                    upedges.extend(w1.Edges)
                    if isinstance(downsegment,Part.Shape):
                        w2 = downsegment.copy()
                        w2.translate(Vector(obj.WaveLength.Value*2*i,0,0))
                        downedges.extend(w2.Edges)
                upwire = Part.Wire(upedges)
                FreeCAD.upwire = upwire # REMOVE
                if isinstance(downsegment,Part.Shape):
                    downwire = Part.Wire(downedges)
                    FreeCAD.downwire = downwire # REMOVE
                    e1 = Part.LineSegment(upwire.Vertexes[0].Point,downwire.Vertexes[0].Point).toShape()
                    e2 = Part.LineSegment(upwire.Vertexes[-1].Point,downwire.Vertexes[-1].Point).toShape()
                    basewire = Part.Wire(upwire.Edges+[e1,e2]+downwire.Edges)
                else:
                    z = obj.Thickness.Value
                    if obj.WaveType == "Curved":
                        z += obj.WaveHeight.Value
                    p1 = upwire.Vertexes[0].Point
                    p2 = p1.add(Vector(0,0,-z))
                    p3 = Vector(upwire.Vertexes[-1].Point.x,upwire.Vertexes[-1].Point.y,p2.z)
                    p4 = upwire.Vertexes[-1].Point
                    w = Part.makePolygon([p1,p2,p3,p4])
                    basewire = Part.Wire(upwire.Edges+w.Edges)

                #
                FreeCAD.basewire = basewire
                if not basewire.isClosed():
                    print("Error closing base wire - check FreeCAD.basewire")
                    return
                #

                baseface = Part.Face(basewire)
                base = baseface.extrude(Vector(0,bb.YLength,0))
                rot = FreeCAD.Rotation(FreeCAD.Vector(0,0,1),normal)
                base.rotate(bb.Center,rot.Axis,math.degrees(rot.Angle))
                if obj.WaveDirection.Value:
                    base.rotate(bb.Center,normal,obj.WaveDirection.Value)
                n1 = normal.negative().normalize().multiply(obj.WaveHeight.Value*2)
                self.vol = baseprofile.copy()
                self.vol.translate(n1)
                self.vol = self.vol.extrude(n1.negative().multiply(2))
                base = self.vol.common(base)
                base = base.removeSplitter()
                if not base:
                    FreeCAD.Console.PrintError(translate("Arch","Error computing shape of")+" "+obj.Label+"\n")
                    return False

        if base and (obj.Sheets > 1) and normal and thickness:
            bases = [base]
            for i in range(1,obj.Sheets):
                n = FreeCAD.Vector(normal).normalize().multiply(i*thickness)
                b = base.copy()
                b.translate(n)
                bases.append(b)
            base = Part.makeCompound(bases)

        if base and normal and hasattr(obj,"Offset"):
            if obj.Offset.Value:
                v = DraftVecUtils.scaleTo(normal,obj.Offset.Value)
                base.translate(v)

        # process subshapes
        base = self.processSubShapes(obj,base,pl)

        # applying
        if base:
            if not base.isNull():
                if base.isValid() and base.Solids:
                    if len(base.Solids) == 1:
                        if base.Volume < 0:
                            base.reverse()
                        if base.Volume < 0:
                            FreeCAD.Console.PrintError(translate("Arch","Couldn't compute a shape"))
                            return
                        base = base.removeSplitter()
                    obj.Shape = base
                    if not pl.isNull():
                        obj.Placement = pl


class _ViewProviderPanel(ArchComponent.ViewProviderComponent):

    "A View Provider for the Panel object"

    def __init__(self,vobj):

        ArchComponent.ViewProviderComponent.__init__(self,vobj)
        vobj.ShapeColor = ArchCommands.getDefaultColor("Panel")

    def getIcon(self):

        #import Arch_rc
        if hasattr(self,"Object"):
            if hasattr(self.Object,"CloneOf"):
                if self.Object.CloneOf:
                    return ":/icons/Arch_Panel_Clone.svg"
        return ":/icons/Arch_Panel_Tree.svg"

    def updateData(self,obj,prop):

        if prop in ["Placement","Shape"]:
            if hasattr(obj,"Material"):
                if obj.Material:
                    if hasattr(obj.Material,"Materials"):
                        if len(obj.Material.Materials) == len(obj.Shape.Solids):
                            cols = []
                            for i,mat in enumerate(obj.Material.Materials):
                                c = obj.ViewObject.ShapeColor
                                c = (c[0],c[1],c[2],obj.ViewObject.Transparency/100.0)
                                if 'DiffuseColor' in mat.Material:
                                    if "(" in mat.Material['DiffuseColor']:
                                        c = tuple([float(f) for f in mat.Material['DiffuseColor'].strip("()").split(",")])
                                if 'Transparency' in mat.Material:
                                    c = (c[0],c[1],c[2],float(mat.Material['Transparency']))
                                cols.extend([c for j in range(len(obj.Shape.Solids[i].Faces))])
                            if obj.ViewObject.DiffuseColor != cols:
                                obj.ViewObject.DiffuseColor = cols
        ArchComponent.ViewProviderComponent.updateData(self,obj,prop)<commit_msg>Arch: panel layers with negative thickness are not drawn<commit_after>#***************************************************************************
#*                                                                         *
#*   Copyright (c) 2011                                                    *
#*   <NAME> <<EMAIL>>                                 *
#*                                                                         *
#*   This program is free software; you can redistribute it and/or modify  *
#*   it under the terms of the GNU Lesser General Public License (LGPL)    *
#*   as published by the Free Software Foundation; either version 2 of     *
#*   the License, or (at your option) any later version.                   *
#*   for detail see the LICENCE text file.                                 *
#*                                                                         *
#*   This program is distributed in the hope that it will be useful,       *
#*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
#*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
#*   GNU Library General Public License for more details.                  *
#*                                                                         *
#*   You should have received a copy of the GNU Library General Public     *
#*   License along with this program; if not, write to the Free Software   *
#*   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
#*   USA                                                                   *
#*                                                                         *
#***************************************************************************

import FreeCAD,Draft,ArchComponent,DraftVecUtils,ArchCommands,math, Part, ArchNesting
from FreeCAD import Vector
if FreeCAD.GuiUp:
    import FreeCADGui
    from PySide import QtCore, QtGui
    from DraftTools import translate
    from PySide.QtCore import QT_TRANSLATE_NOOP
else:
    # \cond
    def translate(ctxt,txt):
        return txt
    def QT_TRANSLATE_NOOP(ctxt,txt):
        return txt
    # \endcond

## @package ArchPanel
#  \ingroup ARCH
#  \brief The Panel object and tools
#
#  This module provides tools to build Panel objects.
#  Panels consist of a closed shape that gets extruded to
#  produce a flat object.


__title__="FreeCAD Panel"
__author__ = "<NAME>"
__url__ = "http://www.freecadweb.org"

#           Description                 l    w    t

Presets = [None,
           ["Plywood 12mm, 1220 x 2440",1220,2440,12],
           ["Plywood 15mm, 1220 x 2440",1220,2440,15],
           ["Plywood 18mm, 1220 x 2440",1220,2440,18],
           ["Plywood 25mm, 1220 x 2440",1220,2440,25],
           ["MDF 3mm, 900 x 600",       900, 600, 3],
           ["MDF 6mm, 900 x 600",       900, 600, 6],
           ["OSB 18mm, 1220 x 2440",    1220,2440,18],
           ]



def makePanel(baseobj=None,length=0,width=0,thickness=0,placement=None,name="Panel"):

    '''makePanel([obj],[length],[width],[thickness],[placement]): creates a
    panel element based on the given profile object and the given
    extrusion thickness. If no base object is given, you can also specify
    length and width for a simple cubic object.'''

    if not FreeCAD.ActiveDocument:
        FreeCAD.Console.PrintError("No active document. Aborting\n")
        return
    obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Panel")
    obj.Label = translate("Arch",name)
    _Panel(obj)
    if FreeCAD.GuiUp:
        _ViewProviderPanel(obj.ViewObject)
    if baseobj:
        obj.Base = baseobj
        if FreeCAD.GuiUp:
            obj.Base.ViewObject.hide()
    if width:
        obj.Width = width
    if thickness:
        obj.Thickness = thickness
    if length:
        obj.Length = length
    return obj


def makePanelView(panel,page=None,name="PanelView"):

    """makePanelView(panel,[page]) : Creates a Drawing view of the given panel
    in the given or active Page object (a new page will be created if none exists)."""

    if not page:
        for o in FreeCAD.ActiveDocument.Objects:
            if o.isDerivedFrom("Drawing::FeaturePage"):
                page = o
                break
        if not page:
            page = FreeCAD.ActiveDocument.addObject("Drawing::FeaturePage",translate("Arch","Page"))
            page.Template = Draft.getParam("template",FreeCAD.getResourceDir()+'Mod/Drawing/Templates/A3_Landscape.svg')
    view = FreeCAD.ActiveDocument.addObject("Drawing::FeatureViewPython",name)
    page.addObject(view)
    PanelView(view)
    view.Source = panel
    view.Label = translate("Arch","View of")+" "+panel.Label
    return view


def makePanelCut(panel,name="PanelView"):

    """makePanelCut(panel) : Creates a 2D view of the given panel
    in the 3D space, positioned at the origin."""

    view = FreeCAD.ActiveDocument.addObject("Part::FeaturePython",name)
    PanelCut(view)
    view.Source = panel
    view.Label = translate("Arch","View of")+" "+panel.Label
    if FreeCAD.GuiUp:
        ViewProviderPanelCut(view.ViewObject)
    return view


def makePanelSheet(panels=[],name="PanelSheet"):

    """makePanelSheet([panels]) : Creates a sheet with the given panel cuts
    in the 3D space, positioned at the origin."""

    sheet = FreeCAD.ActiveDocument.addObject("Part::FeaturePython",name)
    PanelSheet(sheet)
    if panels:
        sheet.Group = panels
    if FreeCAD.GuiUp:
        ViewProviderPanelSheet(sheet.ViewObject)
    return sheet


class CommandPanel:

    "the Arch Panel command definition"

    def GetResources(self):

        return {'Pixmap'  : 'Arch_Panel',
                'MenuText': QT_TRANSLATE_NOOP("Arch_Panel","Panel"),
                'Accel': "P, A",
                'ToolTip': QT_TRANSLATE_NOOP("Arch_Panel","Creates a panel object from scratch or from a selected object (sketch, wire, face or solid)")}

    def IsActive(self):

        return not FreeCAD.ActiveDocument is None

    def Activated(self):

        p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch")
        self.Length = p.GetFloat("PanelLength",1000)
        self.Width = p.GetFloat("PanelWidth",1000)
        self.Thickness = p.GetFloat("PanelThickness",10)
        self.Profile = None
        self.continueCmd = False
        self.rotated = False
        sel = FreeCADGui.Selection.getSelection()
        if sel:
            if len(sel) == 1:
                if Draft.getType(sel[0]) == "Panel":
                    return
            FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Panel")))
            FreeCADGui.addModule("Arch")
            FreeCADGui.addModule("Draft")
            for obj in sel:
                FreeCADGui.doCommand("obj = Arch.makePanel(FreeCAD.ActiveDocument." + obj.Name + ",thickness=" + str(self.Thickness) + ")")
                FreeCADGui.doCommand("Draft.autogroup(obj)")
            FreeCAD.ActiveDocument.commitTransaction()
            FreeCAD.ActiveDocument.recompute()
            return

        # interactive mode
        if hasattr(FreeCAD,"DraftWorkingPlane"):
            FreeCAD.DraftWorkingPlane.setup()
        import DraftTrackers
        self.points = []
        self.tracker = DraftTrackers.boxTracker()
        self.tracker.width(self.Width)
        self.tracker.height(self.Thickness)
        self.tracker.length(self.Length)
        self.tracker.on()
        FreeCADGui.Snapper.getPoint(callback=self.getPoint,movecallback=self.update,extradlg=self.taskbox())

    def getPoint(self,point=None,obj=None):

        "this function is called by the snapper when it has a 3D point"

        self.tracker.finalize()
        if point == None:
            return
        FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create Panel"))
        FreeCADGui.addModule("Arch")
        if self.Profile:
            pr = Presets[self.Profile]
            FreeCADGui.doCommand('p = Arch.makeProfile('+str(pr[2])+','+str(pr[3])+','+str(pr[4])+','+str(pr[5])+')')
            FreeCADGui.doCommand('s = Arch.makePanel(p,thickness='+str(self.Thickness)+')')
            #FreeCADGui.doCommand('s.Placement.Rotation = FreeCAD.Rotation(-0.5,0.5,-0.5,0.5)')
        else:
            FreeCADGui.doCommand('s = Arch.makePanel(length='+str(self.Length)+',width='+str(self.Width)+',thickness='+str(self.Thickness)+')')
        FreeCADGui.doCommand('s.Placement.Base = '+DraftVecUtils.toString(point))
        if self.rotated:
            FreeCADGui.doCommand('s.Placement.Rotation = FreeCAD.Rotation(FreeCAD.Vector(1.00,0.00,0.00),90.00)')
        FreeCAD.ActiveDocument.commitTransaction()
        FreeCAD.ActiveDocument.recompute()
        if self.continueCmd:
            self.Activated()

    def taskbox(self):

        "sets up a taskbox widget"

        w = QtGui.QWidget()
        ui = FreeCADGui.UiLoader()
        w.setWindowTitle(translate("Arch","Panel options", utf8_decode=True))
        grid = QtGui.QGridLayout(w)

        # presets box
        labelp = QtGui.QLabel(translate("Arch","Preset", utf8_decode=True))
        valuep = QtGui.QComboBox()
        fpresets = [" "]
        for p in Presets[1:]:
            fpresets.append(translate("Arch",p[0]))
        valuep.addItems(fpresets)
        grid.addWidget(labelp,0,0,1,1)
        grid.addWidget(valuep,0,1,1,1)

        # length
        label1 = QtGui.QLabel(translate("Arch","Length", utf8_decode=True))
        self.vLength = ui.createWidget("Gui::InputField")
        self.vLength.setText(FreeCAD.Units.Quantity(self.Length,FreeCAD.Units.Length).UserString)
        grid.addWidget(label1,1,0,1,1)
        grid.addWidget(self.vLength,1,1,1,1)

        # width
        label2 = QtGui.QLabel(translate("Arch","Width", utf8_decode=True))
        self.vWidth = ui.createWidget("Gui::InputField")
        self.vWidth.setText(FreeCAD.Units.Quantity(self.Width,FreeCAD.Units.Length).UserString)
        grid.addWidget(label2,2,0,1,1)
        grid.addWidget(self.vWidth,2,1,1,1)

        # height
        label3 = QtGui.QLabel(translate("Arch","Thickness", utf8_decode=True))
        self.vHeight = ui.createWidget("Gui::InputField")
        self.vHeight.setText(FreeCAD.Units.Quantity(self.Thickness,FreeCAD.Units.Length).UserString)
        grid.addWidget(label3,3,0,1,1)
        grid.addWidget(self.vHeight,3,1,1,1)

        # horizontal button
        value5 = QtGui.QPushButton(translate("Arch","Rotate", utf8_decode=True))
        grid.addWidget(value5,4,0,1,2)

        # continue button
        label4 = QtGui.QLabel(translate("Arch","Con&tinue", utf8_decode=True))
        value4 = QtGui.QCheckBox()
        value4.setObjectName("ContinueCmd")
        value4.setLayoutDirection(QtCore.Qt.RightToLeft)
        label4.setBuddy(value4)
        if hasattr(FreeCADGui,"draftToolBar"):
            value4.setChecked(FreeCADGui.draftToolBar.continueMode)
            self.continueCmd = FreeCADGui.draftToolBar.continueMode
        grid.addWidget(label4,5,0,1,1)
        grid.addWidget(value4,5,1,1,1)

        QtCore.QObject.connect(valuep,QtCore.SIGNAL("currentIndexChanged(int)"),self.setPreset)
        QtCore.QObject.connect(self.vLength,QtCore.SIGNAL("valueChanged(double)"),self.setLength)
        QtCore.QObject.connect(self.vWidth,QtCore.SIGNAL("valueChanged(double)"),self.setWidth)
        QtCore.QObject.connect(self.vHeight,QtCore.SIGNAL("valueChanged(double)"),self.setThickness)
        QtCore.QObject.connect(value4,QtCore.SIGNAL("stateChanged(int)"),self.setContinue)
        QtCore.QObject.connect(value5,QtCore.SIGNAL("pressed()"),self.rotate)
        return w

    def update(self,point,info):

        "this function is called by the Snapper when the mouse is moved"

        if FreeCADGui.Control.activeDialog():
            self.tracker.pos(point)
            if self.rotated:
                self.tracker.width(self.Thickness)
                self.tracker.height(self.Width)
                self.tracker.length(self.Length)
            else:
                self.tracker.width(self.Width)
                self.tracker.height(self.Thickness)
                self.tracker.length(self.Length)

    def setWidth(self,d):

        self.Width = d
        FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").SetFloat("PanelWidth",d)

    def setThickness(self,d):

        self.Thickness = d
        FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").SetFloat("PanelThickness",d)

    def setLength(self,d):

        self.Length = d
        FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").SetFloat("PanelLength",d)

    def setContinue(self,i):

        self.continueCmd = bool(i)
        if hasattr(FreeCADGui,"draftToolBar"):
            FreeCADGui.draftToolBar.continueMode = bool(i)

    def setPreset(self,i):

        if i > 0:
            self.vLength.setText(FreeCAD.Units.Quantity(float(Presets[i][1]),FreeCAD.Units.Length).UserString)
            self.vWidth.setText(FreeCAD.Units.Quantity(float(Presets[i][2]),FreeCAD.Units.Length).UserString)
            self.vHeight.setText(FreeCAD.Units.Quantity(float(Presets[i][3]),FreeCAD.Units.Length).UserString)

    def rotate(self):

        self.rotated = not self.rotated


class CommandPanelCut:

    "the Arch Panel Cut command definition"

    def GetResources(self):

        return {'Pixmap'  : 'Arch_Panel_Cut',
                'MenuText': QT_TRANSLATE_NOOP("Arch_Panel_Cut","Panel Cut"),
                'Accel': "P, C",
                'ToolTip': QT_TRANSLATE_NOOP("Arch_Panel_Sheet","Creates 2D views of selected panels")}

    def IsActive(self):

        return not FreeCAD.ActiveDocument is None

    def Activated(self):

        if FreeCADGui.Selection.getSelection():
            FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Panel Cut")))
            FreeCADGui.addModule("Arch")
            for obj in FreeCADGui.Selection.getSelection():
                if Draft.getType(obj) == "Panel":
                    FreeCADGui.doCommand("Arch.makePanelCut(FreeCAD.ActiveDocument."+obj.Name+")")
            FreeCAD.ActiveDocument.commitTransaction()
            FreeCAD.ActiveDocument.recompute()


class CommandPanelSheet:

    "the Arch Panel Sheet command definition"

    def GetResources(self):

        return {'Pixmap'  : 'Arch_Panel_Sheet',
                'MenuText': QT_TRANSLATE_NOOP("Arch_Panel_Sheet","Panel Sheet"),
                'Accel': "P, S",
                'ToolTip': QT_TRANSLATE_NOOP("Arch_Panel_Sheet","Creates a 2D sheet which can contain panel cuts")}

    def IsActive(self):

        return not FreeCAD.ActiveDocument is None

    def Activated(self):

        FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Create Panel Sheet")))
        FreeCADGui.addModule("Arch")
        if FreeCADGui.Selection.getSelection():
            l = "["
            for obj in FreeCADGui.Selection.getSelection():
                l += "FreeCAD.ActiveDocument."+obj.Name+","
            l += "]"
            FreeCAD.ActiveDocument.commitTransaction()
            FreeCAD.ActiveDocument.recompute()
            FreeCADGui.doCommand("__objs__ = "+l)
            FreeCADGui.doCommand("Arch.makePanelSheet(__objs__)")
            FreeCADGui.doCommand("del __objs__")
        else:
            FreeCADGui.doCommand("Arch.makePanelSheet()")
        FreeCAD.ActiveDocument.commitTransaction()
        FreeCAD.ActiveDocument.recompute()


class _Panel(ArchComponent.Component):

    "The Panel object"

    def __init__(self,obj):

        ArchComponent.Component.__init__(self,obj)
        self.setProperties(obj)
        obj.IfcType = "Plate"

    def setProperties(self,obj):

        pl = obj.PropertiesList
        if not "Length" in pl:
            obj.addProperty("App::PropertyLength","Length","Panel",   QT_TRANSLATE_NOOP("App::Property","The length of this element, if not based on a profile"))
        if not "Width" in pl:
            obj.addProperty("App::PropertyLength","Width","Panel",    QT_TRANSLATE_NOOP("App::Property","The width of this element, if not based on a profile"))
        if not "Thickness" in pl:
            obj.addProperty("App::PropertyLength","Thickness","Panel",QT_TRANSLATE_NOOP("App::Property","The thickness or extrusion depth of this element"))
        if not "Sheets" in pl:
            obj.addProperty("App::PropertyInteger","Sheets","Panel",  QT_TRANSLATE_NOOP("App::Property","The number of sheets to use"))
            obj.Sheets = 1
        if not "Offset" in pl:
            obj.addProperty("App::PropertyDistance","Offset","Panel",   QT_TRANSLATE_NOOP("App::Property","The offset between this panel and its baseline"))
        if not "WaveLength" in pl:
            obj.addProperty("App::PropertyLength","WaveLength","Panel", QT_TRANSLATE_NOOP("App::Property","The length of waves for corrugated elements"))
        if not "WaveHeight" in pl:
            obj.addProperty("App::PropertyLength","WaveHeight","Panel", QT_TRANSLATE_NOOP("App::Property","The height of waves for corrugated elements"))
        if not "WaveOffset" in pl:
            obj.addProperty("App::PropertyDistance","WaveOffset","Panel", QT_TRANSLATE_NOOP("App::Property","The horizontal offset of waves for corrugated elements"))
        if not "WaveDirection" in pl:
            obj.addProperty("App::PropertyAngle","WaveDirection","Panel", QT_TRANSLATE_NOOP("App::Property","The direction of waves for corrugated elements"))
        if not "WaveType" in pl:
            obj.addProperty("App::PropertyEnumeration","WaveType","Panel", QT_TRANSLATE_NOOP("App::Property","The type of waves for corrugated elements"))
            obj.WaveType = ["Curved","Trapezoidal","Spikes"]
        if not "WaveBottom" in pl:
            obj.addProperty("App::PropertyBool","WaveBottom","Panel", QT_TRANSLATE_NOOP("App::Property","If the wave also affects the bottom side or not"))
        if not "Area" in pl:
            obj.addProperty("App::PropertyArea","Area","Panel",       QT_TRANSLATE_NOOP("App::Property","The area of this panel"))
        if not "FaceMaker" in pl:
            obj.addProperty("App::PropertyEnumeration","FaceMaker","Panel",QT_TRANSLATE_NOOP("App::Property","The facemaker type to use to build the profile of this object"))
            obj.FaceMaker = ["None","Simple","Cheese","Bullseye"]
        if not "Normal" in pl:
            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)"))
        self.Type = "Panel"
        obj.setEditorMode("VerticalArea",2)
        obj.setEditorMode("HorizontalArea",2)

    def onDocumentRestored(self,obj):

        ArchComponent.Component.onDocumentRestored(self,obj)
        self.setProperties(obj)

    def execute(self,obj):

        "creates the panel shape"

        if self.clone(obj):
            return

        import Part #, DraftGeomUtils

        layers = []
        length = 0
        width = 0
        thickness = 0

        # base tests
        if obj.Base:
            if obj.Base.isDerivedFrom("Part::Feature"):
                if obj.Base.Shape.isNull():
                    return
            elif obj.Base.isDerivedFrom("Mesh::Feature"):
                if not obj.Base.Mesh.isSolid():
                    return
        else:
            if obj.Length.Value:
                length = obj.Length.Value
            else:
                return
            if obj.Width.Value:
                width = obj.Width.Value
            else:
                return
        if obj.Thickness.Value:
            thickness = obj.Thickness.Value
        else:
            if not obj.Base:
                return
            elif obj.Base.isDerivedFrom("Part::Feature"):
                if not obj.Base.Shape.Solids:
                    return
        if hasattr(obj,"Material"):
            if obj.Material:
                if hasattr(obj.Material,"Materials"):
                    varwidth = 0
                    thicknesses = [t for t in obj.Material.Thicknesses if t >= 0]
                    restwidth = thickness - sum(thicknesses)
                    if restwidth > 0:
                        varwidth = [t for t in thicknesses if t == 0]
                        if varwidth:
                            varwidth = restwidth/len(varwidth)
                    for t in obj.Material.Thicknesses:
                        if t:
                            layers.append(t)
                        elif varwidth:
                            layers.append(varwidth)
        # creating base shape
        pl = obj.Placement
        base = None
        normal = None
        if hasattr(obj,"Normal"):
            if obj.Normal.Length > 0:
                normal = Vector(obj.Normal)
                normal.normalize()
                normal.multiply(thickness)
        baseprofile = None
        if obj.Base:
            base = obj.Base.Shape.copy()
            if not base.Solids:
               # p = FreeCAD.Placement(obj.Base.Placement)
                if base.Faces:
                    baseprofile = base
                    if not normal:
                        normal = baseprofile.Faces[0].normalAt(0,0).multiply(thickness)
                    if layers:
                        layeroffset = 0
                        shps = []
                        for l in layers:
                            if l >= 0:
                                n = Vector(normal).normalize().multiply(abs(l))
                                b = base.extrude(n)
                                if layeroffset:
                                    o = Vector(normal).normalize().multiply(layeroffset)
                                    b.translate(o)
                                shps.append(b)
                            layeroffset += abs(l)
                        base = Part.makeCompound(shps)
                    else:
                        base = base.extrude(normal)
                elif base.Wires:
                    fm = False
                    if hasattr(obj,"FaceMaker"):
                        if obj.FaceMaker != "None":
                            try:
                                baseprofile = Part.makeFace(base.Wires,"Part::FaceMaker"+str(obj.FaceMaker))
                                fm = True
                            except:
                                FreeCAD.Console.PrintError(translate("Arch","Facemaker returned an error")+"\n")
                                return
                    if not fm:
                        closed = True
                        for w in base.Wires:
                            if not w.isClosed():
                                closed = False
                        if closed:
                            baseprofile = ArchCommands.makeFace(base.Wires)
                    if not normal:
                        normal = baseprofile.normalAt(0,0).multiply(thickness)
                    if layers:
                        layeroffset = 0
                        shps = []
                        for l in layers:
                            if l >= 0:
                                n = Vector(normal).normalize().multiply(abs(l))
                                b = baseprofile.extrude(n)
                                if layeroffset:
                                    o = Vector(normal).normalize().multiply(layeroffset)
                                    b.translate(o)
                                shps.append(b)
                            layeroffset += abs(l)
                        base = Part.makeCompound(shps)
                    else:
                        base = baseprofile.extrude(normal)
                elif obj.Base.isDerivedFrom("Mesh::Feature"):
                    if obj.Base.Mesh.isSolid():
                        if obj.Base.Mesh.countComponents() == 1:
                            sh = ArchCommands.getShapeFromMesh(obj.Base.Mesh)
                            if sh.isClosed() and sh.isValid() and sh.Solids:
                                base = sh
        else:
            if layers:
                shps = []
                layeroffset = 0
                for l in layers:
                    if l >= 0:
                        if normal:
                            n = Vector(normal).normalize().multiply(l)
                        else:
                            n = Vector(0,0,1).multiply(abs(l))
                        l2 = length/2 or 0.5
                        w2 = width/2 or 0.5
                        v1 = Vector(-l2,-w2,layeroffset)
                        v2 = Vector(l2,-w2,layeroffset)
                        v3 = Vector(l2,w2,layeroffset)
                        v4 = Vector(-l2,w2,layeroffset)
                        base = Part.makePolygon([v1,v2,v3,v4,v1])
                        baseprofile = Part.Face(base)
                        base = baseprofile.extrude(n)
                        shps.append(base)
                    layeroffset += abs(l)
                base = Part.makeCompound(shps)
            else:
                if not normal:
                    normal = Vector(0,0,1).multiply(thickness)
                l2 = length/2 or 0.5
                w2 = width/2 or 0.5
                v1 = Vector(-l2,-w2,0)
                v2 = Vector(l2,-w2,0)
                v3 = Vector(l2,w2,0)
                v4 = Vector(-l2,w2,0)
                base = Part.makePolygon([v1,v2,v3,v4,v1])
                baseprofile = Part.Face(base)
                base = baseprofile.extrude(normal)

        if hasattr(obj,"Area"):
            if baseprofile:
                obj.Area = baseprofile.Area

        if hasattr(obj,"WaveLength"):
            if baseprofile and obj.WaveLength.Value and obj.WaveHeight.Value:
                # corrugated element
                bb = baseprofile.BoundBox
                bb.enlarge(bb.DiagonalLength)
                downsegment = None
                if hasattr(obj,"WaveBottom"):
                    if not obj.WaveBottom:
                        if obj.WaveType == "Curved":
                            if obj.Thickness.Value > obj.WaveHeight.Value:
                                downsegment = obj.Thickness.Value
                            else:
                                downsegment = obj.WaveHeight.Value + obj.Thickness.Value
                        else:
                            downsegment = obj.Thickness.Value
                p1 = Vector(0,0,0)
                p5 = Vector(obj.WaveLength.Value*2,0,0)
                if obj.WaveType == "Curved":
                    p2 = Vector(obj.WaveLength.Value/2,0,obj.WaveHeight.Value)
                    p3 = Vector(obj.WaveLength.Value,0,0)
                    e1 = Part.Arc(p1,p2,p3).toShape()
                    p4 = Vector(obj.WaveLength.Value*1.5,0,-obj.WaveHeight.Value)
                    e2 = Part.Arc(p3,p4,p5).toShape()
                    upsegment = Part.Wire([e1,e2])
                    if not downsegment:
                        if obj.Thickness.Value < e1.Curve.Radius:
                            c3 = e1.Curve.copy()
                            c3.Radius = e1.Curve.Radius-obj.Thickness.Value
                            e3 = Part.Arc(c3,e1.FirstParameter,e1.LastParameter).toShape()
                            c4 = e2.Curve.copy()
                            c4.Radius = e2.Curve.Radius+obj.Thickness.Value
                            e4 = Part.Arc(c4,e2.FirstParameter,e2.LastParameter).toShape()
                            downsegment = Part.Wire([e3,e4])
                        else:
                            r = e2.Curve.Radius+obj.Thickness.Value
                            z = math.sqrt(r^2 - obj.WaveLength.Value^2)
                            p6 = e2.Curve.Center.add(Vector(-obj.WaveLength,0,-z))
                            p7 = e2.Curve.Center.add(Vector(0,0,-r))
                            p8 = e2.Curve.Center.add(Vector(obj.WaveLength,0,-z))
                            downsegment = Part.Arc(p6,p7,p8).toShape()

                elif obj.WaveType == "Trapezoidal":
                    p2 = Vector(obj.WaveLength.Value/4,0,obj.WaveHeight.Value)
                    p3 = Vector(obj.WaveLength.Value,0,obj.WaveHeight.Value)
                    p4 = Vector(obj.WaveLength.Value*1.25,0,0)
                    upsegment = Part.makePolygon([p1,p2,p3,p4,p5])
                    if not downsegment:
                        a = ((p1.sub(p2)).getAngle(p3.sub(p2)))/2
                        tx = obj.Thickness.Value*math.tan(a)
                        d1 = Vector(tx,0,-obj.Thickness.Value)
                        d2 = Vector(-tx,0,-obj.Thickness.Value)
                        p6 = p1.add(d1)
                        if tx >= p3.sub(p2).Length/2:
                            d3 = p2.sub(p1)
                            d3.normalize()
                            d3.multiply((0.625*obj.WaveLength.Value)/d3.x)
                            d4 = Vector(d3.x,0,-d3.z)
                            p7 = p6.add(d3)
                            p8 = p7.add(d4)
                            p9 = p5.add(d1)
                            downsegment = Part.makePolygon([p6,p7,p8,p9])
                        elif tx <= 0.625*obj.WaveLength.Value:
                            p7 = p2.add(d1)
                            p8 = p3.add(d2)
                            p9 = p4.add(d2)
                            p10 = p5.add(d1)
                            downsegment = Part.makePolygon([p6,p7,p8,p9,p10])
                        else:
                            downsegment = obj.Thickness.Value

                else: # spike
                    p2 = Vector(obj.WaveHeight.Value,0,obj.WaveHeight.Value)
                    p3 = Vector(obj.WaveHeight.Value*2,0,0)
                    upsegment = Part.makePolygon([p1,p2,p3,p5])
                    if not downsegment:
                        downsegment = obj.Thickness.Value

                upsegment.translate(Vector(bb.getPoint(0).x,bb.getPoint(0).y,bb.Center.z))
                if isinstance(downsegment,Part.Shape):
                    downsegment.translate(Vector(bb.getPoint(0).x,bb.getPoint(0).y,bb.Center.z))
                if hasattr(obj,"WaveOffset"):
                    if obj.WaveOffset.Value:
                        upsegment.translate(Vector(obj.WaveOffset.Value,0,0))
                        if isinstance(downsegment,Part.Shape):
                            downsegment.translate(Vector(obj.WaveOffset.Value,0,0))

                upedges = []
                downedges = []
                for i in range(int(bb.XLength/(obj.WaveLength.Value*2))):
                    w1 = upsegment.copy()
                    w1.translate(Vector(obj.WaveLength.Value*2*i,0,0))
                    upedges.extend(w1.Edges)
                    if isinstance(downsegment,Part.Shape):
                        w2 = downsegment.copy()
                        w2.translate(Vector(obj.WaveLength.Value*2*i,0,0))
                        downedges.extend(w2.Edges)
                upwire = Part.Wire(upedges)
                FreeCAD.upwire = upwire # REMOVE
                if isinstance(downsegment,Part.Shape):
                    downwire = Part.Wire(downedges)
                    FreeCAD.downwire = downwire # REMOVE
                    e1 = Part.LineSegment(upwire.Vertexes[0].Point,downwire.Vertexes[0].Point).toShape()
                    e2 = Part.LineSegment(upwire.Vertexes[-1].Point,downwire.Vertexes[-1].Point).toShape()
                    basewire = Part.Wire(upwire.Edges+[e1,e2]+downwire.Edges)
                else:
                    z = obj.Thickness.Value
                    if obj.WaveType == "Curved":
                        z += obj.WaveHeight.Value
                    p1 = upwire.Vertexes[0].Point
                    p2 = p1.add(Vector(0,0,-z))
                    p3 = Vector(upwire.Vertexes[-1].Point.x,upwire.Vertexes[-1].Point.y,p2.z)
                    p4 = upwire.Vertexes[-1].Point
                    w = Part.makePolygon([p1,p2,p3,p4])
                    basewire = Part.Wire(upwire.Edges+w.Edges)

                FreeCAD.basewire = basewire
                if not basewire.isClosed():
                    print("Error closing base wire - check FreeCAD.basewire")
                    return

                baseface = Part.Face(basewire)
                base = baseface.extrude(Vector(0,bb.YLength,0))
                rot = FreeCAD.Rotation(FreeCAD.Vector(0,0,1),normal)
                base.rotate(bb.Center,rot.Axis,math.degrees(rot.Angle))
                if obj.WaveDirection.Value:
                    base.rotate(bb.Center,normal,obj.WaveDirection.Value)
                n1 = normal.negative().normalize().multiply(obj.WaveHeight.Value*2)
                self.vol = baseprofile.copy()
                self.vol.translate(n1)
                self.vol = self.vol.extrude(n1.negative().multiply(2))
                base = self.vol.common(base)
                base = base.removeSplitter()
                if not base:
                    FreeCAD.Console.PrintError(translate("Arch","Error computing shape of")+" "+obj.Label+"\n")
                    return False

        if base and (obj.Sheets > 1) and normal and thickness:
            bases = [base]
            for i in range(1,obj.Sheets):
                n = FreeCAD.Vector(normal).normalize().multiply(i*thickness)
                b = base.copy()
                b.translate(n)
                bases.append(b)
            base = Part.makeCompound(bases)

        if base and normal and hasattr(obj,"Offset"):
            if obj.Offset.Value:
                v = DraftVecUtils.scaleTo(normal,obj.Offset.Value)
                base.translate(v)

        # process subshapes
        base = self.processSubShapes(obj,base,pl)

        # applying
        if base:
            if not base.isNull():
                if base.isValid() and base.Solids:
                    if len(base.Solids) == 1:
                        if base.Volume < 0:
                            base.reverse()
                        if base.Volume < 0:
                            FreeCAD.Console.PrintError(translate("Arch","Couldn't compute a shape"))
                            return
                        base = base.removeSplitter()
                    obj.Shape = base
                    if not pl.isNull():
                        obj.Placement = pl


class _ViewProviderPanel(ArchComponent.ViewProviderComponent):

    "A View Provider for the Panel object"

    def __init__(self,vobj):

        ArchComponent.ViewProviderComponent.__init__(self,vobj)
        vobj.ShapeColor = ArchCommands.getDefaultColor("Panel")

    def getIcon(self):

        #import Arch_rc
        if hasattr(self,"Object"):
            if hasattr(self.Object,"CloneOf"):
                if self.Object.CloneOf:
                    return ":/icons/Arch_Panel_Clone.svg"
        return ":/icons/Arch_Panel_Tree.svg"

    def updateData(self,obj,prop):

        if prop in ["Placement","Shape","Material"]:
            if hasattr(obj,"Material"):
                if obj.Material:
                    if hasattr(obj.Material,"Materials"):
                        activematerials = [obj.Material.Materials[i] for i in range(len(obj.Material.Materials)) if obj.Material.Thicknesses[i] >= 0]
                        if len(activematerials) == len(obj.Shape.Solids):
                            cols = []
                            for i,mat in enumerate(activematerials):
                                c = obj.ViewObject.ShapeColor
                                c = (c[0],c[1],c[2],obj.ViewObject.Transparency/100.0)
                                if 'DiffuseColor' in mat.Material:
                                    if "(" in mat.Material['DiffuseColor']:
                                        c = tuple([float(f) for f in mat.Material['DiffuseColor'].strip("()").split(",")])
                                if 'Transparency' in mat.Material:
                                    c = (c[0],c[1],c[2],float(mat.Material['Transparency']))
                                cols.extend([c for j in range(len(obj.Shape.Solids[i].Faces))])
                            if obj.ViewObject.DiffuseColor != cols:
                                obj.ViewObject.DiffuseColor = cols
        ArchComponent.ViewProviderComponent.updateData(self,obj,prop)
{'fraction_non_alphanumeric': 0.11485859187717377, 'fraction_numerical': 0.01603225851150803, 'mean_word_length': 1.6771525021330267, 'pattern_counts': {'":': 18, '<': 19, '<?xml version=': 0, '>': 28, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 72, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5493012', 'n_tokens_mistral': 21200, 'n_tokens_neox': 19689, 'n_words': 3716}