# /********************************************************************** # * * # * Copyright (c) 2021 Javier Braña * # * * # * This program is free software; you can redistribute it and/or modify* # * it under the terms of the GNU Lesser General Public License (LGPL) * # * as published by the Free Software Foundation; either version 2 of * # * the License, or (at your option) any later version. * # * for detail see the LICENCE text file. * # * * # * This program is distributed in the hope that it will be useful, * # * but WITHOUT ANY WARRANTY; without even the implied warranty of * # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * # * GNU Library General Public License for more details. * # * * # * You should have received a copy of the GNU Library General Public * # * License along with this program; if not, write to the Free Software * # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307* # * USA * # * * # *********************************************************************** import os import ArchComponent import FreeCAD import Part if FreeCAD.GuiUp: import FreeCADGui from PySide.QtCore import QT_TRANSLATE_NOOP from pivy import coin import draftguitools.gui_trackers as DraftTrackers else: # \cond def translate(ctxt, txt): return txt def QT_TRANSLATE_NOOP(ctxt, txt): return txt # \endcond __title__ = "PVPlant Trench" __author__ = "Javier Braña" __url__ = "http://www.sogos-solar.com" import PVPlantResources from PVPlantResources import DirIcons as DirIcons TrenchFillTypes = ["Arena", "Tierra de excavación", "Cemento", "Graba"] # sand color: ece2c6 / 236 226 198 # tierra color: 4e3b31 / 78 59 49 MateriralColor = [(0.9255, 0.8863, 0.7765), (0.3059, 0.2314, 0.1922), (0.4902, 0.5176, 0.4431)] def makeTrenchNode(point=None, trench=None): obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "TrenchNode") TrenchNode(obj) ViewProviderTrenchNode(obj.ViewObject) if point: obj.Placement.Base = point if trench: tl = [trench] obj.TrenchList = tl return obj class TrenchNode: def __init__(self, obj): ''' Initialize the class. ''' self.Type = None self.obj = None self.setProperties(obj) def setProperties(self, obj): pl = obj.PropertiesList if not ("TrenchList" in pl): obj.addProperty("App::PropertyLinkList", "TrenchList", "TrenchNode", "Connection") self.obj = obj self.Type = "TrenchNode" obj.Proxy = self def onDocumentRestored(self, obj): """ Method run when the document is restored. """ self.setProperties(obj) def execute(self, obj): pl = obj.Placement obj.Shape = Part.makeSphere(1500) obj.Placement = pl class ViewProviderTrenchNode: def __init__(self, vobj): ''' Set view properties. ''' vobj.Proxy = self def getIcon(self): ''' Return object treeview icon. ''' return str(os.path.join(DirIcons, "node.jpg")) def makeTrench(base=None): obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Trench") Trench(obj) ViewProviderTrench(obj.ViewObject) obj.Base = base try: folder = FreeCAD.ActiveDocument.Trenches except: folder = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", 'Trenches') folder.Label = "Trenches" folder.addObject(obj) return obj class Trench(ArchComponent.Component): def __init__(self, obj): # Definición de Variables: ArchComponent.Component.__init__(self, obj) self.numLayersSegment = dict() self.setProperties(obj) self.number_old_segments = int(obj.Segments) self.obj = obj self.route = False def setProperties(self, obj): # Definicion de Propiedades: pl = obj.PropertiesList if not ("OffsetStart" in pl): obj.addProperty("App::PropertyLength", "OffsetStart", "Trench", "Offset al comienzo") if not ("OffsetEnd" in pl): obj.addProperty("App::PropertyLength", "OffsetEnd", "Trench", "Offset al final") if not ("Width" in pl): obj.addProperty("App::PropertyLength", "Width", "Trench", QT_TRANSLATE_NOOP("App::Property", "Connection")).Width = 600 if not ("Trapezoid" in pl): obj.addProperty("App::PropertyBool", "Trapezoid", "Trench", "Connection").Trapezoid = False if not ("Segments" in pl): obj.addProperty("App::PropertyIntegerConstraint", "Segments", "Segments", "Segments").Segments = (1, 1, 100, 1) # Cables: ------------------------ if not ("Cables" in pl): obj.addProperty("App::PropertyIntegerConstraint", "Cables", "Cables", "Número de cables").Cables = (1, 1, 20, 1) if not ("DistanceToExcavationBotton" in pl): obj.addProperty("App::PropertyLength", "DistanceToExcavationBotton", "Cables", "Separación del cable al fondo de excavación").DistanceToExcavationBotton = 100 # Outputs: ------------------------ if not ("Length" in pl): obj.addProperty("App::PropertyLength", "Length", "Outputs", "Length") obj.setEditorMode("Length", 1) if not ("Volume" in pl): obj.addProperty("App::PropertyVolume", "Volume", "Outputs", "Volume") obj.setEditorMode("Volume", 1) if not ("Type" in pl): obj.addProperty("App::PropertyString", "Type", "Base", "Type").Type = "Trench" obj.setEditorMode("Type", 1) self.Type = obj.Type obj.Proxy = self obj.IfcType = "Civil Element" ## puede ser: Cable Carrier Segment #obj.setEditorMode("IfcType", 1) def onDocumentRestored(self, obj): ArchComponent.Component.onDocumentRestored(self, obj) self.setProperties(obj) def onBeforeChange(self, obj, prop): if prop == "Segments": self.number_old_segments = int(obj.Segments) def createSegment(self, obj, ind): pl = obj.PropertiesList name = f"Start{ind}" if not (name in pl): obj.addProperty("App::PropertyLength", name, f"Segment{ind}", "Number of layers") if ind == 1: obj.setEditorMode(name, 1) val = 0 else: val = obj.getPropertyByName(f"Start{ind - 1}").Value + 100 setattr(obj, name, val) name = f"LayersSeg{ind}" if not (name in pl): obj.addProperty("App::PropertyIntegerConstraint", name, f"Segment{ind}", "Number of layers") setattr(obj, name, (2, 2, 6, 1)) name = f"Deep{ind}" if not (name in pl): obj.addProperty("App::PropertyLength", name, f"Segment{ind}", "Number of layers") setattr(obj, name, 900) def removeSegment(self, obj, ind): lay = getattr(obj, f"LayersSeg{ind}") # TODO: cambiar a S(xx)Layers for i in range(1, lay + 1): obj.removeProperty(f"Layer{i}HeigthSeg{ind}") obj.removeProperty(f"Layer{i}MaterialSeg{ind}") obj.removeProperty(f"LayersSeg{ind}") obj.removeProperty(f"Start{ind}") obj.removeProperty(f"Deep{ind}") def createLayer(self, obj, seg_num, ind): pl = obj.PropertiesList name = f"Layer{ind + 1}HeigthSeg{seg_num}" # TODO: cambiar a S(xx)Layer(yy)Heigth if not (name in pl): obj.addProperty("App::PropertyLength", name, f"Segment{seg_num}", "Number of layers") setattr(obj, name, 450) name = f"Layer{ind + 1}MaterialSeg{seg_num}" # TODO: cambiar a S(xx)Layer(yy)Material if not (name in pl): obj.addProperty("App::PropertyEnumeration", name, f"Segment{seg_num}", "Number of layers") setattr(obj, name, TrenchFillTypes) setattr(obj, name, TrenchFillTypes[ind]) def removeLayer(self, obj, seg_num, ind): obj.removeProperty(f"Layer{ind}HeigthSeg{seg_num}") obj.removeProperty(f"Layer{ind}MaterialSeg{seg_num}") def onChanged(self, obj, prop): if prop == "Segments": if self.number_old_segments == int(obj.Segments): pass elif self.number_old_segments < int(obj.Segments): for i in range(self.number_old_segments + 1, int(obj.Segments) + 1): self.createSegment(obj, i) else: for i in range(self.number_old_segments, int(obj.Segments), -1): self.removeSegment(obj, i) if prop.startswith("LayersSeg"): seg_num = int(prop[len("LayersSeg"):]) if not (prop in self.numLayersSegment): for i in range(obj.getPropertyByName(prop)): self.createLayer(obj, seg_num, i) elif self.numLayersSegment[prop] < obj.getPropertyByName(prop): for i in range(self.numLayersSegment[prop], obj.getPropertyByName(prop)): self.createLayer(obj, seg_num, i) elif self.numLayersSegment[prop] > obj.getPropertyByName(prop): for i in range(self.numLayersSegment[prop], obj.getPropertyByName(prop), -1): self.removeLayer(obj, seg_num, i) self.numLayersSegment[prop] = int(obj.getPropertyByName(prop)) def calculateOffset(self, obj, dist): offset = obj.makeOffset2D(dist, 2, False, True, True) pts_o = [] for offseti, offsetv in enumerate(offset.Vertexes): lns = list() for point in pts: tmp = FreeCAD.Vector(point) tmp.z = 0 lns.append(offsetv.Point.sub(tmp).Length) tmp = FreeCAD.Vector(offset.Vertexes[offseti].Point) tmp.z = pts[lns.index(min(lns))].z pts_o.append(tmp) if dist < 0: pts_o.insert(0, pts_o.pop(1)) pts_o.reverse() return pts_o #Part.Wire(Part.makePolygon(points)) def execute(self, obj): # obj.Shape: compound # |- Segments: compound # |-- segment x: compound # |---- Layer x # |- Path for cables: compound # |-- Path 1 # |-- Path x import Part import MeshPart as mp def getVector(edge): p1 = edge.Vertexes[0].Point p1.z = 0 p2 = edge.Vertexes[1].Point p2.z = 0 return p2.sub(p1) def getsegments(wire): import math segments = [] segment = [wire.Edges[0]] for i in range(1, len(wire.Edges)): vec1 = getVector(wire.Edges[i - 1]) vec2 = getVector(wire.Edges[i]) angle = math.degrees(vec1.getAngle(vec2)) if angle <= 1: segment.append(wire.Edges[i]) else: segments.append(Part.Wire(segment)) segment = [wire.Edges[i]] segments.append(Part.Wire(segment)) return segments w = self.calculatePathWire(obj) if w is None: return obj.Base.Visibility = False segmetPaths = self.getSegmentPaths(obj, w) land = FreeCAD.ActiveDocument.Site.Terrain.Mesh d = obj.Width.Value / 2 sh = Part.makeCompound([]) for i in range(1, obj.Segments + 1): w = segmetPaths[i - 1] tmp = mp.projectShapeOnMesh(w, land, FreeCAD.Vector(0, 0, 1)) pts = [] for p in tmp: if len(p) > 0: pts.extend(p) pts_plane = list() for pt in pts: tmp = FreeCAD.Vector(pt) tmp.z = 0 pts_plane.append(tmp) path_plane = Part.makePolygon(pts_plane) '''o1 = path_plane.makeOffset2D(d, 2, False, True, True) o2 = path_plane.makeOffset2D(-d, 2, False, True, True) points = calculateOffset(o1) points.insert(0, points.pop(1)) points.reverse() points2 = calculateOffset(o2)''' points = self.calculateOffset(path_plane, d) points2 = self.calculateOffset(path_plane, -d) w1 = Part.Wire(Part.makePolygon(points)) w2 = Part.Wire(Part.makePolygon(points2)) segments1 = getsegments(w1) segments2 = getsegments(w2) lines = [] for sidx in range(len(segments1)): reverse = False if len(segments1[sidx].Vertexes) >= len(segments2[sidx].Vertexes): pl1 = segments1[sidx] pl2 = segments2[sidx] else: pl1 = segments2[sidx] pl2 = segments1[sidx] reverse = True lines.append(Part.LineSegment(pl1.Vertexes[0].Point, pl2.Vertexes[0].Point).toShape()) for i1 in range(1, len(pl1.Vertexes) - 1): p1 = pl1.Vertexes[i1].Point tmp = [] for i2 in range(1, len(pl2.Vertexes)): p2 = pl2.Vertexes[i2].Point tmp.append([p2, p1.sub(p2).Length]) p2 = min(tmp, key=lambda x: x[1])[0] if not reverse: lines.append(Part.LineSegment(p1, p2).toShape()) else: lines.append(Part.LineSegment(p2, p1).toShape()) lines.append(Part.LineSegment(segments1[-1].Vertexes[-1].Point, segments2[-1].Vertexes[-1].Point).toShape()) loft = Part.makeLoft(lines, False, True, False) lay = Part.makeCompound([]) if not obj.Trapezoid: zz = -obj.getPropertyByName(f"Deep{i}").Value for j in range(1, obj.getPropertyByName(f"LayersSeg{i}") + 1): h = obj.getPropertyByName(f"Layer{j}HeigthSeg{i}").Value tmp = loft.extrude(FreeCAD.Vector(0,0, h)) tmp.Placement.Base.z = zz lay.add(tmp) zz += h else: ''' to be defined...''' sh.add(lay) gap = 250 if obj.Cables % 2 == 0: xx = -gap/2 - gap * (obj.Cables / 2 - 1) # 25 se cambiará a una variable que indicará la separación entre cables else: xx = - gap * int(obj.Cables / 2) paths = Part.makeCompound([]) '''for i in range(obj.Cables): o1 = path_plane.makeOffset2D(xx, 2, False, True, True) points = calculateOffset(o1) for point in points: point.z = point.z - obj.Height.Value + obj.DistanceToExcavationBotton.Value if points[2].sub(points[0]).Length < points[2].sub(points[1]).Length: points.insert(0, points.pop(1)) if xx < 0: points.reverse() p = Part.makePolygon(points) paths.add(p) xx += gap''' obj.Shape = Part.makeCompound([sh, paths]) obj.Length = path_plane.Length obj.Volume = sh.Volume colors = [] for i in range(1, obj.Segments + 1): for j in range(1, obj.getPropertyByName(f"LayersSeg{i}") + 1): material = obj.getPropertyByName(f"Layer{j}MaterialSeg{i}") #material = obj.getPropertyByName("Layer{0}Material".format(i + 1)) color = MateriralColor[TrenchFillTypes.index(material)] colors.extend([color] * len(obj.Shape.SubShapes[0].SubShapes[i - 1].SubShapes[j - 1].Faces)) obj.ViewObject.DiffuseColor = colors def calculatePathWire(self, obj): if obj.Base: wire = None if hasattr(obj.Base.Shape, 'Wires') and obj.Base.Shape.Wires: wire = obj.Base.Shape.Wires[0] elif obj.Base.Shape.Edges: wire = Part.Wire(obj.Base.Shape.Edges) if obj.OffsetStart.Value: d = obj.OffsetStart.Value for i, e in enumerate(wire.Edges): if e.Length < d: d -= e.Length else: v = e.Vertexes[-1].Point.sub(e.Vertexes[0].Point).normalize() v.multiply(d) p = e.Vertexes[0].Point.add(v) pts = [ver.Point for ver in wire.Vertexes] pts = [p] + pts[i + 1:] wire = Part.makePolygon(pts) break if obj.OffsetEnd.Value: d = wire.Length - obj.OffsetEnd.Value for i, e in enumerate(wire.Edges): if e.Length < d: d -= e.Length else: v = e.Vertexes[-1].Point.sub(e.Vertexes[0].Point).normalize() v.multiply(d) p = e.Vertexes[0].Point.add(v) pts = [ver.Point for ver in wire.Vertexes] pts = pts[:i + 1] + [p] wire = Part.makePolygon(pts) break return wire return None def getSegmentPaths(self, obj, wire): segmetPaths = [] for i in range(1, obj.Segments + 1): s = obj.getPropertyByName(f"Start{i}").Value if (i + 1) <= obj.Segments: ln = obj.getPropertyByName(f"Start{i + 1}").Value else: ln = wire.Length f = ln - s segmetPaths.append(self.calcualteSementPath(wire, s, f)) return segmetPaths def calcualteSementPath(self, w, start, length): for i, e in enumerate(w.Edges): if e.Length < start: start -= e.Length else: v = e.Vertexes[-1].Point.sub(e.Vertexes[0].Point).normalize() v.multiply(start) p = e.Vertexes[0].Point.add(v) pts = [ver.Point for ver in w.Vertexes] pts = [p] + pts[i + 1:] w = Part.makePolygon(pts) break for i, e in enumerate(w.Edges): if e.Length < length: length -= e.Length else: v = e.Vertexes[-1].Point.sub(e.Vertexes[0].Point).normalize() v.multiply(length) p = e.Vertexes[0].Point.add(v) pts = [ver.Point for ver in w.Vertexes] pts = pts[:i + 1] + [p] w = Part.makePolygon(pts) break return w class ViewProviderTrench(ArchComponent.ViewProviderComponent): def __init__(self, vobj): ArchComponent.ViewProviderComponent.__init__(self, vobj) def getIcon(self): return str(os.path.join(PVPlantResources.DirIcons, "trench.svg")) import DraftVecUtils import draftutils.utils as utils class TrenchTaskPanel: def __init__(self, obj=None): self.new = False self.obj = obj if obj is None: import Draft self.new = True self.obj = Part.Shape() self.form = FreeCADGui.PySideUic.loadUi(os.path.join(PVPlantResources.__dir__, "PVPlantTrench.ui")) self.form.buttonAddLayer.clicked.connect(self.addLayer) self.form.buttonDeleteLayer.clicked.connect(self.removeLayer) self.form.buttonUp.clicked.connect(self.moveUp) self.form.buttonDown.clicked.connect(self.moveDown) self.trenchnode = None self.point = None self.points = [] self.linesegments = [] self.path = None self.pos = None self.support = None self.info = None self.tracker = DraftTrackers.wireTracker( Part.Wire(Part.makePolygon([FreeCAD.Vector(), FreeCAD.Vector(1, 1, 1)]))) self.view = FreeCADGui.ActiveDocument.ActiveView self.call = self.view.addEventCallback("SoEvent", self.action) def action(self, arg): if arg["Type"] == "SoKeyboardEvent": if arg["Key"] == "ESCAPE": self.finish() elif arg["Type"] == "SoLocation2Event": pos = arg['Position'] point = FreeCADGui.ActiveDocument.ActiveView.getPoint(pos) if len(self.points) > 0: pts = self.points.copy() pts.append(point) self.tracker.updateFromPointlist(pts) elif (arg["Type"] == "SoMouseButtonEvent" and arg["State"] == "DOWN" and arg["Button"] == "BUTTON1"): pos = arg['Position'] listObjects = FreeCADGui.ActiveDocument.ActiveView.getObjectsInfo((int(pos[0]), int(pos[1]))) if len(listObjects) == 1: if listObjects[0]["Object"].startswith("Mesh002"): print("Press on Mesh002. Point: ", pos) self.setPoint(pos) elif len(listObjects) > 1: find = False for object in listObjects: if object["Object"].startswith("TrenchNode"): self.trenchnode = FreeCAD.ActiveDocument.getObject(object["Object"]) self.setPoint(self.trenchnode.Placement.Base) find = True break elif object["Object"].startswith("Trench"): tmp = SplitTrench(FreeCAD.ActiveDocument.getObject(object["Object"]), FreeCADGui.ActiveDocument.ActiveView.getPoint(pos)) print(tmp) '''if tmp: self.setPoint(tmp[0]) self.trenchnode = tmp[2]''' find = True break if not find: if listObjects[0]["Object"].startswith("Mesh002"): self.setPoint(pos) else: self.setPoint(FreeCAD.ActiveDocument.getObject(listObjects[0]["Object"].Name).Placement.Base) def setPoint(self, position): if isinstance(position, FreeCAD.Vector): point = position else: point = FreeCADGui.ActiveDocument.ActiveView.getPoint(position) self.points.append(point) if len(self.points) == 1: self.tracker.on() self.drawSegment() def finish(self, close=False, some=False): """ Terminate the operation. """ if len(self.points) > 1: import Draft self.path = Draft.makeWire(self.points) FreeCAD.ActiveDocument.recompute() makeTrench(self.path) def removeTemporaryObject(self): """ Remove temporary object created. """ self.tracker.finalize() sg = FreeCADGui.ActiveDocument.ActiveView.getSceneGraph() for no in self.linesegments: sg.removeChild(no) def undolast(self): """Undoes last line segment.""" if len(self.points) > 1: self.points.pop() sg = FreeCADGui.ActiveDocument.ActiveView.getSceneGraph() no = self.linesegments.pop() sg.removeChild(no) def drawSegment(self): """Draws new line segment.""" if len(self.points) > 1: self.tracker.updateFromPointlist(self.points) """p1 = self.points[-2] p2 = self.points[-1] print(" linesegment from p1(", p1, ") to p2 (", p2, ")") sg = FreeCADGui.ActiveDocument.ActiveView.getSceneGraph() co = coin.SoCoordinate3() pts = [[p1.x, p1.y, p1.z], [p2.x, p2.y, p2.z]] '''for i in range(len(pts)): p = pts[i] co.point.set1Value(i, [p.x, p.y, p.z])''' co.point.setValues(0, len(pts), pts) ma = coin.SoBaseColor() ma.rgb = (0, 0, 1) st = coin.SoDrawStyle() st.style = coin.SoDrawStyle.LINES st.lineWidth = 3 li = coin.SoLineSet() li.numVertices.setValue(2) no = coin.SoSeparator() no.addChild(co) no.addChild(ma) no.addChild(st) no.addChild(li) sg.addChild(no) self.linesegments.append(no)""" def wipe(self): """Remove all previous segments and starts from last point.""" if len(self.points) > 1: self.obj.ViewObject.Visibility = False self.points = [self.points[-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 = FreeCAD.Vector(numx, numy, numz) self.points.append(self.point) self.drawSegment() self.ui.setNextFocus() def addLayer(self): num = self.form.listLayers.count() + 1 self.form.listLayers.addItem("Layer" + str(num)) # TODO: add property to obj layer = "Layer" + str(num) self.obj.addProperty("App::PropertyIntegerList", "Name", layer, layer + " Name" ) setattr(self.obj, "Name", layer) self.obj.addProperty("App::PropertyIntegerList", "Description", layer, layer + " description" ) self.obj.addProperty("App::PropertyIntegerList", "Height", layer, layer + " Height" ) setattr(self.obj, "Heigth", 100) def removeLayer(self): # TODO: remove property to obj currentRow = self.form.listLayers.currentRow() currentItem = self.form.listLayers.takeItem(currentRow) del (currentItem) def moveUp(self): currentRow = self.form.listLayers.currentRow() currentItem = self.form.listLayers.takeItem(currentRow) self.form.listLayers.insertItem(currentRow - 1, currentItem) def moveDown(self): currentRow = self.form.listLayers.currentRow() currentItem = self.form.listLayers.takeItem(currentRow) self.form.listLayers.insertItem(currentRow + 1, currentItem) def accept(self): FreeCAD.ActiveDocument.openTransaction("Create Trench") self.finish() FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() self.closeForm() return True def reject(self): self.closeForm() return False def closeForm(self): print(" .. Closing .. ") self.removeTemporaryObject() self.view.removeEventCallback("SoEvent", self.call) FreeCADGui.Control.closeDialog() class semiAutomaticTrench: def __init__(self): import draftguitools.gui_trackers as DraftTrackers self.objects = [] self.state = 0 self.direction = None self.distance = 1500 self.point = None self.tracker = DraftTrackers.wireTracker( Part.Wire(Part.makePolygon([FreeCAD.Vector(), FreeCAD.Vector(1, 1, 1)]))) # event callbacks self._keyPressedCB = None self._mouseMovedCB = None self._mousePressedCB = None self.view = FreeCADGui.activeDocument().activeView() self.render_manager = FreeCADGui.ActiveDocument.ActiveView.getViewer().getSoRenderManager() # Callbacks self.unregister_editing_callbacks() self.register_editing_callbacks() # ------------------------------------------------------------------------- # SCENE EVENTS CALLBACKS # ------------------------------------------------------------------------- def register_editing_callbacks(self): """ Register editing callbacks (former action function) """ print("Registering callbacks") if self._keyPressedCB is None: self._keyPressedCB = self.view.addEventCallbackPivy(coin.SoKeyboardEvent.getClassTypeId(), self.keyPressed) if self._mousePressedCB is None: self._mousePressedCB = self.view.addEventCallbackPivy(coin.SoMouseButtonEvent.getClassTypeId(), self.mousePressed) if self._mouseMovedCB is None: self._mouseMovedCB = self.view.addEventCallbackPivy(coin.SoLocation2Event.getClassTypeId(), self.mouseMoved) def unregister_editing_callbacks(self): """ Remove callbacks used during editing if they exist """ print("Unregistering callbacks") if self._keyPressedCB: self.view.removeEventCallbackPivy(coin.SoKeyboardEvent.getClassTypeId(), self._keyPressedCB) self._keyPressedCB = None if self._mousePressedCB: self.view.removeEventCallbackPivy(coin.SoMouseButtonEvent.getClassTypeId(), self._mousePressedCB) self._mousePressedCB = None if self._mouseMovedCB: self.view.removeEventCallbackPivy(coin.SoLocation2Event.getClassTypeId(), self._mouseMovedCB) self._mouseMovedCB = None # ------------------------------------------------------------------------- # SCENE EVENT HANDLERS # ------------------------------------------------------------------------- def keyPressed(self, event_callback): event = event_callback.getEvent() print(event.getKey(), " - ", event.getState()) if event.getState() == event.UP: if event.getKey() == 65307: # ESC self.quit() elif event.getKey() == 65293: # ENTER print("ENTER") self.state += 1 if self.state == 2: '''print("----------------------------------------------------------------") print(" -- objects: ") print(self.objects) print(" -- distance:") print(self.distance) print("----------------------------------------------------------------")''' self.calculateTrenches() self.quit() '''elif event.getKey() == ord("q"): # or event.getKey() == ord(65307): if self.obj: self.obj.ViewObject.Proxy.doubleClicked(self.obj.ViewObject) else: self.quit() elif event.getKey() == ord("s"): sel = FreeCADGui.Selection.getSelectionEx() tup = None if len(sel) == 1: tup = (sel[0].Object, sel[0].SubElementNames) for i in range(len(self.selected_objects)): if isinstance(self.selected_objects[i], MarkerOnShape): self.selected_objects[i].sublink = tup # FreeCAD.Console.PrintMessage("Snapped to {}\n".format(str(self.selected_objects[i].sublink))) self.selected_objects[i].drag_start() self.selected_objects[i].drag((0, 0, 0.)) self.selected_objects[i].drag_release() elif (event.getKey() == 65535) or (event.getKey() == 65288): # Suppr or Backspace # FreeCAD.Console.PrintMessage("Some objects have been deleted\n") pts = list() for o in self.dynamic_objects: if isinstance(o, MarkerOnShape): pts.append(o) self.points = pts self.setupInteractionSeparator()''' def mousePressed(self, event_callback): """ Mouse button event handler, calls: startEditing, endEditing, addPoint, delPoint """ event = event_callback.getEvent() pos = event.getPosition().getValue() listObjects = FreeCADGui.ActiveDocument.ActiveView.getObjectsInfo((int(pos[0]), int(pos[1]))) if event.getButton() == event.BUTTON1: # left click if event.getState() == coin.SoMouseButtonEvent.DOWN: if self.state == 0: ''' Select objects ''' obj = FreeCAD.ActiveDocument.getObject(listObjects[0]['Object']) if obj in self.objects: self.objects.remove(obj) else: self.objects.append(obj) numobjs = len(self.objects) if numobjs == 0: self.tracker.finalize() elif numobjs == 1: self.tracker.updateFromPointlist([obj.Placement.Base for obj in self.objects]) self.tracker.on() else: self.tracker.updateFromPointlist([obj.Placement.Base for obj in self.objects]) elif self.state == 1: ''' Select distance and direction ''' self.direction = FreeCAD.Vector(listObjects[0]['x'], listObjects[0]['y'], listObjects[0]['z']) # self.point = None elif event.getState() == coin.SoMouseButtonEvent.UP: if listObjects and self.point: if self.state == 0: FreeCADGui.Selection.clearSelection() for obj in listObjects: FreeCADGui.Selection.addSelection(obj) """if (event.getState() == coin.SoMouseButtonEvent.DOWN) and ( event.getButton() == event.BUTTON1): # left click print("Mouse button down and mouse button 1") if not event.wasAltDown(): if self.editing is None: ''' do something''' else: self.endEditing(self.obj, self.editing) elif event.wasAltDown(): # left click with ctrl down self.display_tracker_menu(event) elif (event.getState() == coin.SoMouseButtonEvent.DOWN) and ( event.getButton() == event.BUTTON2): # right click self.display_tracker_menu(event)""" def mouseMoved(self, event_callback): """ Execute as callback for mouse movement. Update tracker position and update preview ghost. """ if self.state == 1: event = event_callback.getEvent() pos = event.getPosition().getValue() listObjects = FreeCADGui.ActiveDocument.ActiveView.getObjectsInfo((int(pos[0]), int(pos[1]))) print(listObjects) point = FreeCAD.Vector(listObjects[0]['x'], listObjects[0]['y'], listObjects[0]['z']) pts = [obj.Placement.Base for obj in self.objects] offset = point.sub(pts[0]) self.tracker.updateFromPointlist([point.add(offset) for point in pts]) def calculateTrenches(self): if len(self.objects) > 1: import Draft pts = [obj.Placement.Base for obj in self.objects] vec = self.direction.sub(pts[0]) pts1 = [point.add(vec) for point in pts] for i in range(len(pts1) - 1): makeTrench(Draft.makeLine(pts1[i], pts1[i + 1])) return pts = [obj.Placement.Base for obj in FreeCADGui.Selection.getSelection()] from sklearn.cluster import OPTICS, cluster_optics_dbscan import numpy as np clust = OPTICS(min_samples=5, xi=0.05, min_cluster_size=0.05) X = np.array(pts) # Run the fit clust.fit(X) labels_050 = cluster_optics_dbscan( reachability=clust.reachability_, core_distances=clust.core_distances_, ordering=clust.ordering_, eps=0.5, ) labels_200 = cluster_optics_dbscan( reachability=clust.reachability_, core_distances=clust.core_distances_, ordering=clust.ordering_, eps=2, ) space = np.arange(len(X)) reachability = clust.reachability_[clust.ordering_] labels = clust.labels_[clust.ordering_] print("\n") print(" Space: ", space) print(" Reachability", reachability) print(" Labels", labels) return from scipy import stats xx = list() yy = list() zz = list() for point in pts: xx.append(point.x) yy.append(point.y) zz.append(point.z) slope, intercept, r, p, std_err = stats.linregress(xx, yy) def myfunc(val): return slope * val + intercept newzz = list(map(myfunc, [xx[0], xx[-1]])) points3D = list() points3D.append(FreeCAD.Vector(xx[0], yy[0], newzz[0])) points3D.append(FreeCAD.Vector(xx[-1], yy[-1], newzz[1])) def quit(self): print("Quit") self.tracker.finalize() self.unregister_editing_callbacks() class CommandTrench: # V1: """Gui command for the Line tool.""" def GetResources(self): """Set icon, menu and tooltip.""" return {'Pixmap': str(os.path.join(DirIcons, "trench.svg")), 'MenuText': "Trench", 'Accel': "C, T", 'ToolTip': "Creates a Trench object from setup dialog."} def IsActive(self): active = not (FreeCAD.ActiveDocument is None) terrain = not (FreeCAD.ActiveDocument.getObject("Terrain") is None) active = active and terrain if terrain: active = active and not (FreeCAD.ActiveDocument.getObject("Terrain").Mesh is None) return active def Activated(self): """Execute when the command is called.""" sel = FreeCADGui.Selection.getSelection() done = False if len(sel) > 0: import Draft for obj in sel: if Draft.getType(obj) == "Wire": FreeCAD.ActiveDocument.openTransaction("Create Trench") makeTrench(obj) FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() done = True break if not done: taskd = TrenchTaskPanel() if taskd: FreeCADGui.Control.showDialog(taskd) else: print(" No ha sido posible crear el formulario") class CommandSemiAutomaticTrench: # V1: """Gui command for the Line tool.""" def GetResources(self): """Set icon, menu and tooltip.""" return {'Pixmap': str(os.path.join(DirIcons, "trench.svg")), 'MenuText': "Semi-Automatic Trench Generator", 'Accel': "T, S", 'ToolTip': "Creates a Trench object from setup dialog."} def IsActive(self): active = not (FreeCAD.ActiveDocument is None) terrain = not (FreeCAD.ActiveDocument.getObject("Terrain") is None) active = active and terrain if terrain: active = active and not (FreeCAD.ActiveDocument.getObject("Terrain").Mesh is None) return active def Activated(self): """Execute when the command is called.""" semi = semiAutomaticTrench() if FreeCAD.GuiUp: class CommandTrenchGroup: def GetCommands(self): return tuple(['PVPlantTrench', 'PVPlantSemiAutomaticTrench', ]) def GetResources(self): return {'MenuText': 'Rack Types', 'ToolTip': 'Rack Types' } def IsActive(self): active = not (FreeCAD.ActiveDocument is None) terrain = not (FreeCAD.ActiveDocument.getObject("Terrain") is None) active = active and terrain if terrain: active = active and not (FreeCAD.ActiveDocument.getObject("Terrain").Mesh is None) return active FreeCADGui.addCommand('PVPlantTrench', CommandTrench()) FreeCADGui.addCommand('PVPlantSemiAutomaticTrench', CommandSemiAutomaticTrench()) FreeCADGui.addCommand('Trenches', CommandTrenchGroup())