Files
PVPlant/Civil/PVPlantTrench.py
2025-04-14 10:05:32 +06:00

1142 lines
42 KiB
Python

# /**********************************************************************
# * *
# * Copyright (c) 2021 Javier Braña <javier.branagutierrez@gmail.com> *
# * *
# * 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): #deepseek
"""Divide un wire en segmentos rectos basados en cambios de dirección (sin splitWiresByCurvature)"""
import Part
from math import degrees
segments = []
current_segment = []
angle_threshold = 1.0 # Grados para considerar cambio de dirección
def get_angle(v1, v2):
return degrees(v1.getAngle(v2))
edges = wire.Edges
for i in range(len(edges)):
if i == 0:
current_segment.append(edges[i])
continue
prev_edge = edges[i - 1]
curr_edge = edges[i]
# Vectores de dirección
v1 = prev_edge.tangentAt(prev_edge.FirstParameter)
v2 = curr_edge.tangentAt(curr_edge.FirstParameter)
angle = get_angle(v1, v2)
if angle > angle_threshold:
segments.append(Part.Wire(current_segment))
current_segment = [curr_edge]
else:
current_segment.append(curr_edge)
if current_segment:
segments.append(Part.Wire(current_segment))
return segments
def getsegments_old(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)
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())'''