import FreeCAD import ArchComponent if FreeCAD.GuiUp: import FreeCADGui from PySide import QtCore from DraftTools import translate from PySide.QtCore import QT_TRANSLATE_NOOP import Part import os else: # \cond def translate(ctxt, txt): return txt def QT_TRANSLATE_NOOP(ctxt, txt): return txt # \endcond __title__ = "PVPlant Road" __author__ = "Javier Braña" __url__ = "http://www.sogos-solar.com" import PVPlantResources from PVPlantResources import DirIcons as DirIcons def makeRoad(base=None): obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Road") _Road(obj) _ViewProviderRoad(obj.ViewObject) obj.Base = base from Project.Area import PVPlantArea offset = PVPlantArea.makeOffsetArea(obj, 4000) PVPlantArea.makeProhibitedArea(offset) return obj class _Road(ArchComponent.Component): def __init__(self, obj): # Definición de Variables: ArchComponent.Component.__init__(self, obj) self.obj = obj self.setProperties(obj) self.Type = "Road" obj.Proxy = self self.route = False obj.IfcType = "Civil Element" ## puede ser: Cable Carrier Segment obj.setEditorMode("IfcType", 1) self.count = 0 def setProperties(self, obj): # Definicion de Propiedades: '''[ 'App::PropertyBool', 'App::PropertyBoolList', 'App::PropertyFloat', 'App::PropertyFloatList', 'App::PropertyFloatConstraint', 'App::PropertyPrecision', 'App::PropertyQuantity', 'App::PropertyQuantityConstraint', 'App::PropertyAngle', 'App::PropertyDistance', 'App::PropertyLength', 'App::PropertyArea', 'App::PropertyVolume', 'App::PropertyFrequency', 'App::PropertySpeed', 'App::PropertyAcceleration', 'App::PropertyForce', 'App::PropertyPressure', 'App::PropertyVacuumPermittivity', 'App::PropertyInteger', 'App::PropertyIntegerConstraint', 'App::PropertyPercent', 'App::PropertyEnumeration', 'App::PropertyIntegerList', 'App::PropertyIntegerSet', 'App::PropertyMap', 'App::PropertyString', 'App::PropertyPersistentObject', 'App::PropertyUUID', 'App::PropertyFont', 'App::PropertyStringList', 'p::PropertyLink', 'App::PropertyLinkChild', 'App::PropertyLinkGlobal', 'App::PropertyLinkHidden', 'App::PropertyLinkSub', 'App::PropertyLinkSubChild', 'App::PropertyLinkSubGlobal', 'App::PropertyLinkSubHidden', 'App::PropertyLinkList', 'App::PropertyLinkListChild', 'App::PropertyLinkListGlobal', 'App::PropertyLinkListHidden', 'App::PropertyLinkSubList', 'App::PropertyLinkSubListChild', 'App::PropertyLinkSubListGlobal', 'App::PropertyLinkSubListHidden', 'App::PropertyXLink', 'App::PropertyXLinkSub', 'App::PropertyXLinkSubList', 'App::PropertyXLinkList', 'App::PropertyMatrix', 'App::PropertyVector', 'App::PropertyVectorDistance', 'App::PropertyPosition', 'App::PropertyDirection', 'App::PropertyVectorList', 'App::PropertyPlacement', 'App::PropertyPlacementList', 'App::PropertyPlacementLink', 'App::PropertyColor', 'App::PropertyColorList', 'App::PropertyMaterial', 'App::PropertyMaterialList', 'App::PropertyPath', 'App::PropertyFile', 'App::PropertyFileIncluded', 'App::PropertyPythonObject', 'App::PropertyExpressionEngine', 'Part::PropertyPartShape', 'Part::PropertyGeometryList', 'Part::PropertyShapeHistory', 'Part::PropertyFilletEdges', 'Mesh::PropertyNormalList', 'Mesh::PropertyCurvatureList', 'Mesh::PropertyMeshKernel', 'Sketcher::PropertyConstraintList' ]''' obj.addProperty("App::PropertyPercent", "SurfaceSlope", "Road", QT_TRANSLATE_NOOP("App::Property", "Connection")).SurfaceSlope = 2 obj.addProperty("App::PropertyPercent", "SurfaceDrainSlope", "Road", QT_TRANSLATE_NOOP("App::Property", "Connection")).SurfaceDrainSlope = int(3 / 2 * 100) obj.addProperty("App::PropertyPercent", "SubbaseDrainSlope", "Road", QT_TRANSLATE_NOOP("App::Property", "Connection")).SubbaseDrainSlope = int(2 / 3 * 100) obj.addProperty("App::PropertyLength", "Width", "Road", QT_TRANSLATE_NOOP("App::Property", "Connection")).Width = 4000 obj.addProperty("App::PropertyLength", "Height", "Road", QT_TRANSLATE_NOOP("App::Property", "Connection")).Height = 250 obj.addProperty("App::PropertyLength", "Subbase", "Road", QT_TRANSLATE_NOOP("App::Property", "Connection")).Subbase = 400 def onDocumentRestored(self, obj): """Method run when the document is restored. Re-adds the Arch component, and object properties.""" ArchComponent.Component.onDocumentRestored(self, obj) self.obj = obj self.Type = "Road" obj.Proxy = self def execute(self, obj): import Part, math w = obj.Base.Shape profiles = [] SurfaceDrainSlope = obj.SurfaceDrainSlope / 100 SubbaseDrainSlope = obj.SubbaseDrainSlope / 100 vec_up_left = FreeCAD.Vector(-obj.Width.Value / 2, 0, obj.Height.Value) vec_up_center = FreeCAD.Vector(0, 0, obj.SurfaceSlope * obj.Width.Value / 200 + obj.Height.Value) vec_up_right = FreeCAD.Vector(obj.Width.Value / 2, 0, obj.Height.Value) vec_down_left = FreeCAD.Vector(-(obj.Width.Value / 2 + obj.Height.Value / SurfaceDrainSlope), 0, 0) vec_down_right = FreeCAD.Vector((obj.Width.Value / 2 + obj.Height.Value / SurfaceDrainSlope), 0, 0) vec_sand_left = FreeCAD.Vector(-(obj.Width.Value / 2 + obj.Height.Value * (1 / SurfaceDrainSlope + SubbaseDrainSlope)), 0, - obj.Subbase.Value) vec_sand_right = FreeCAD.Vector((obj.Width.Value / 2 + obj.Height.Value * (1 / SurfaceDrainSlope + SubbaseDrainSlope)), 0, - obj.Subbase.Value) edge1 = Part.makeLine(vec_down_left, vec_down_right) edge2 = Part.makeLine(vec_down_right, vec_up_right) edge3 = Part.makeLine(vec_up_right, vec_up_center) edge4 = Part.makeLine(vec_up_center, vec_up_left) edge5 = Part.makeLine(vec_up_left, vec_down_left) edge6 = Part.makeLine(vec_sand_left, vec_sand_right) edge7 = Part.makeLine(vec_sand_left, vec_down_left) edge8 = Part.makeLine(vec_sand_right, vec_down_right) p = Part.Wire([edge1, edge2, edge3, edge4, edge5]) profiles.append(p) p = Part.Wire([edge6, edge8, edge1, edge7]) profiles.append(p) shapes = self.makeSolids(obj, profiles, w, (vec_down_right + vec_down_left) / 2) angle = 30 height = FreeCAD.ActiveDocument.Site.Terrain.Shape.BoundBox.ZMax - obj.Height.Value offset = height / math.tan(math.radians(angle)) '''cutProfile = Part.makePolygon([vec_sand_left, vec_sand_right, vec_sand_right + FreeCAD.Vector(offset, 0, FreeCAD.ActiveDocument.Site.Terrain.Shape.BoundBox.ZMax), vec_sand_left + FreeCAD.Vector(-offset, 0, FreeCAD.ActiveDocument.Site.Terrain.Shape.BoundBox.ZMax), vec_sand_left]) height = obj.Height.Value - FreeCAD.ActiveDocument.Site.Terrain.Shape.BoundBox.ZMin offset = height / math.tan(math.radians(angle)) fillProfile = Part.makePolygon([vec_sand_left, vec_sand_right, vec_sand_right + FreeCAD.Vector(offset, 0, FreeCAD.ActiveDocument.Site.Terrain.Shape.BoundBox.ZMin), vec_sand_left + FreeCAD.Vector(-offset, 0, FreeCAD.ActiveDocument.Site.Terrain.Shape.BoundBox.ZMin), vec_sand_left]) cutshapes, fillshapes = self.makeSolids(obj, [cutProfile, fillProfile], w, (vec_up_right + vec_up_left) / 2) cuts = self.calculateCut(obj, cutshapes) fills = self.calculateFill(obj, fillshapes) if cuts: for cut in cuts: Part.show(cut, "RoadCut") if fills: for fill in fills: Part.show(fill, "RoadFill")''' obj.Shape = Part.makeCompound(shapes) def makeSolids(self, obj, profiles, w, origen): import Draft import DraftGeomUtils shapes = [] for p in profiles: if hasattr(p, "CenterOfMass"): c = p.CenterOfMass else: c = p.BoundBox.Center c = origen delta = w.Vertexes[0].Point - c p.translate(delta) if Draft.getType(obj.Base) == "BezCurve": v1 = obj.Base.Placement.multVec(obj.Base.Points[1]) - w.Vertexes[0].Point else: v1 = w.Vertexes[1].Point - w.Vertexes[0].Point v2 = DraftGeomUtils.getNormal(p) rot = FreeCAD.Rotation(v2, v1) #p.rotate(w.Vertexes[0].Point, rot.Axis, math.degrees(rot.Angle)) ang = rot.toEuler()[0] p.Placement.Rotation = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), ang) if p.Faces: for f in p.Faces: sh = w.makePipeShell([f.OuterWire], True, False, 2) for shw in f.Wires: if shw.hashCode() != f.OuterWire.hashCode(): sh2 = w.makePipeShell([shw], True, False, 2) sh = sh.cut(sh2) shapes.append(sh) elif p.Wires: for pw in p.Wires: sh = w.makePipeShell([pw], True, False, 2) shapes.append(sh) return shapes def calculateFill(self, obj, solid): import BOPTools.SplitAPI as splitter common = solid.common(FreeCAD.ActiveDocument.Site.Terrain.Shape) if common.Area > 0: sp = splitter.slice(solid, [common, ], "Split") common.Placement.Base.z += 1 solids = [] for sol in sp.Solids: common1 = sol.common(common) if common1.Area > 0: solids.append(sol) if len(solids) > 0: return solids return None def calculateCut(self, obj, solid): import BOPTools.SplitAPI as splitter common = solid.common(FreeCAD.ActiveDocument.Site.Terrain.Shape) if common.Area > 0: sp = splitter.slice(solid, [common, ], "Split") shells = [] commoncopy = common.copy() commoncopy.Placement.Base.z -= 1 for sol in sp.Solids: common1 = sol.common(commoncopy) if common1.Area > 0: shell = sol.Shells[0] shell = shell.cut(common) shells.append(shell) if len(shells) > 0: return shells return None def makeLoft(self, profile): return class _ViewProviderRoad(ArchComponent.ViewProviderComponent): def __init__(self, vobj): ArchComponent.ViewProviderComponent.__init__(self, vobj) def getIcon(self): return str(os.path.join(PVPlantResources.DirIcons, "road.svg")) class _RoadTaskPanel: def __init__(self, obj=None): if obj is None: self.new = True self.obj = makeRoad() else: self.new = False self.obj = obj self.form = FreeCADGui.PySideUic.loadUi(os.path.join(PVPlantResources.__dir__, "PVPlantRoad.ui")) def accept(self): FreeCADGui.Control.closeDialog() return True def reject(self): FreeCAD.ActiveDocument.removeObject(self.obj.Name) if self.new: FreeCADGui.Control.closeDialog() return True from PySide.QtCore import QT_TRANSLATE_NOOP import FreeCAD as App import FreeCADGui as Gui import DraftVecUtils import draftutils.utils as utils import draftutils.gui_utils as gui_utils import draftutils.todo as todo import draftguitools.gui_base_original as gui_base_original import draftguitools.gui_tool_utils as gui_tool_utils from draftutils.messages import _msg from draftutils.translate import translate class _CommandRoad(gui_base_original.Creator): """Gui command for the Line tool.""" def __init__(self): # super(_CommandRoad, self).__init__() gui_base_original.Creator.__init__(self) self.path = None def GetResources(self): """Set icon, menu and tooltip.""" return {'Pixmap': str(os.path.join(DirIcons, "road.svg")), 'MenuText': QtCore.QT_TRANSLATE_NOOP("PVPlantRoad", "Road"), 'Accel': "C, R", 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PVPlantRoad", "Creates a Road object from setup dialog.")} def Activated(self, name=translate("draft", "Line")): """Execute when the command is called.""" gui_base_original.Creator.Activated(self, name=translate("draft", "Line")) self.obj = None # stores the temp shape self.oldWP = None # stores the WP if we modify it sel = FreeCADGui.Selection.getSelection() done = False self.existing = [] if len(sel) > 0: print("Crear una carretera a lo largo de un trayecto") # TODO: chequear que el objeto seleccionado sea un "wire" import Draft if Draft.getType(sel[0]) == "Wire": self.path = sel[0] done = True if not done: self.ui.wireUi(name) self.ui.setTitle("Road") self.obj = self.doc.addObject("Part::Feature", self.featureName) gui_utils.format_object(self.obj) self.call = self.view.addEventCallback("SoEvent", self.action) _msg(translate("draft", "Pick first point")) def action(self, arg): """Handle the 3D scene events. This is installed as an EventCallback in the Inventor view. Parameters ---------- arg: dict Dictionary with strings that indicates the type of event received from the 3D view. """ if arg["Type"] == "SoKeyboardEvent" and arg["Key"] == "ESCAPE": self.finish() elif arg["Type"] == "SoLocation2Event": self.point, ctrlPoint, self.info = gui_tool_utils.getPoint(self, arg) gui_tool_utils.redraw3DView() elif (arg["Type"] == "SoMouseButtonEvent" and arg["State"] == "DOWN" and arg["Button"] == "BUTTON1"): if arg["Position"] == self.pos: return self.finish(False, cont=True) if (not self.node) and (not self.support): gui_tool_utils.getSupport(arg) self.point, ctrlPoint, self.info = gui_tool_utils.getPoint(self, arg) if self.point: self.point = FreeCAD.Vector(self.info["x"], self.info["y"], self.info["z"]) self.ui.redraw() self.pos = arg["Position"] self.node.append(self.point) self.drawSegment(self.point) if len(self.node) > 2: # The wire is closed if (self.point - self.node[0]).Length < utils.tolerance(): self.undolast() if len(self.node) > 2: self.finish(True, cont=True) else: self.finish(False, cont=True) def finish(self, closed=False, cont=False): """Terminate the operation and close the polyline if asked. Parameters ---------- closed: bool, optional Close the line if `True`. """ self.removeTemporaryObject() if self.oldWP: App.DraftWorkingPlane = self.oldWP if hasattr(Gui, "Snapper"): Gui.Snapper.setGrid() Gui.Snapper.restack() self.oldWP = None if len(self.node) > 1: if False: Gui.addModule("Draft") # The command to run is built as a series of text strings # to be committed through the `draftutils.todo.ToDo` class. if (len(self.node) == 2 and utils.getParam("UsePartPrimitives", False)): # Insert a Part::Primitive object p1 = self.node[0] p2 = self.node[-1] _cmd = 'FreeCAD.ActiveDocument.' _cmd += 'addObject("Part::Line", "Line")' _cmd_list = ['line = ' + _cmd, 'line.X1 = ' + str(p1.x), 'line.Y1 = ' + str(p1.y), 'line.Z1 = ' + str(p1.z), 'line.X2 = ' + str(p2.x), 'line.Y2 = ' + str(p2.y), 'line.Z2 = ' + str(p2.z), 'Draft.autogroup(line)', 'FreeCAD.ActiveDocument.recompute()'] self.commit(translate("draft", "Create Line"), _cmd_list) else: # Insert a Draft line rot, sup, pts, fil = self.getStrings() _base = DraftVecUtils.toString(self.node[0]) _cmd = 'Draft.makeWire' _cmd += '(' _cmd += 'points, ' _cmd += 'placement=pl, ' _cmd += 'closed=' + str(closed) + ', ' _cmd += 'face=' + fil + ', ' _cmd += 'support=' + sup _cmd += ')' _cmd_list = ['pl = FreeCAD.Placement()', 'pl.Rotation.Q = ' + rot, 'pl.Base = ' + _base, 'points = ' + pts, 'line = ' + _cmd, 'Draft.autogroup(line)', 'FreeCAD.ActiveDocument.recompute()'] self.commit(translate("draft", "Create Wire"), _cmd_list) else: import Draft self.path = Draft.makeWire(self.node, closed=False, face=False) # super(_CommandRoad, self).finish() gui_base_original.Creator.finish(self) if self.ui and self.ui.continueMode: self.Activated() self.makeRoad() def makeRoad(self): makeRoad(self.path) def removeTemporaryObject(self): """Remove temporary object created.""" if self.obj: try: old = self.obj.Name except ReferenceError: # object already deleted, for some reason pass else: todo.ToDo.delay(self.doc.removeObject, old) self.obj = None def undolast(self): """Undoes last line segment.""" import Part if len(self.node) > 1: self.node.pop() # last = self.node[-1] if self.obj.Shape.Edges: edges = self.obj.Shape.Edges if len(edges) > 1: newshape = Part.makePolygon(self.node) self.obj.Shape = newshape else: self.obj.ViewObject.hide() # DNC: report on removal # _msg(translate("draft", "Removing last point")) _msg(translate("draft", "Pick next point")) def drawSegment(self, point): """Draws new line segment.""" import Part if self.planetrack and self.node: self.planetrack.set(self.node[-1]) if len(self.node) == 1: _msg(translate("draft", "Pick next point")) elif len(self.node) == 2: last = self.node[len(self.node) - 2] newseg = Part.LineSegment(last, point).toShape() self.obj.Shape = newseg self.obj.ViewObject.Visibility = True _msg(translate("draft", "Pick next point")) else: currentshape = self.obj.Shape.copy() last = self.node[len(self.node) - 2] if not DraftVecUtils.equals(last, point): newseg = Part.LineSegment(last, point).toShape() newshape = currentshape.fuse(newseg) self.obj.Shape = newshape _msg(translate("draft", "Pick next point")) def wipe(self): """Remove all previous segments and starts from last point.""" if len(self.node) > 1: # self.obj.Shape.nullify() # For some reason this fails self.obj.ViewObject.Visibility = False self.node = [self.node[-1]] if self.planetrack: self.planetrack.set(self.node[0]) _msg(translate("draft", "Pick next point")) def orientWP(self): """Orient the working plane.""" import DraftGeomUtils if hasattr(App, "DraftWorkingPlane"): if len(self.node) > 1 and self.obj: n = DraftGeomUtils.getNormal(self.obj.Shape) if not n: n = App.DraftWorkingPlane.axis p = self.node[-1] v = self.node[-2].sub(self.node[-1]) v = v.negative() if not self.oldWP: self.oldWP = App.DraftWorkingPlane.copy() App.DraftWorkingPlane.alignToPointAndAxis(p, n, upvec=v) if hasattr(Gui, "Snapper"): Gui.Snapper.setGrid() Gui.Snapper.restack() if self.planetrack: self.planetrack.set(self.node[-1]) def numericInput(self, numx, numy, numz): """Validate the entry fields in the user interface. This function is called by the toolbar or taskpanel interface when valid x, y, and z have been entered in the input fields. """ self.point = App.Vector(numx, numy, numz) self.node.append(self.point) self.drawSegment(self.point) self.ui.setNextFocus() if FreeCAD.GuiUp: FreeCADGui.addCommand('PVPlantRoad', _CommandRoad())