Files
PVPlant/Utils/PVPlantTrace.py
2025-01-28 00:04:13 +01:00

464 lines
15 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
from copy import deepcopy
import FreeCAD
import Part
from pivy import coin
if FreeCAD.GuiUp:
import FreeCADGui
from PySide import QtCore
from PySide.QtCore import QT_TRANSLATE_NOOP
else:
# \cond
def translate(ctxt, txt):
return txt
def QT_TRANSLATE_NOOP(ctxt, txt):
return txt
# \endcond
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
from PVPlantResources import DirIcons as DirIcons
'''
import threading
import datetime
class ThreadClass(threading.Thread):
def __init__(self, land, Trace):
self.land = land
self.trace = Trace
def run(self):
tmp = self.land.makeParallelProjection(self.trace, FreeCAD.Vector(0, 0, 1))
pts = [ver.Point for ver in tmp.Vertexes]
return pts
for i in range(2):
t = ThreadClass()
t.start()
'''
def makeTrace(points = None, label = "Trace"):
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Trace")
obj.Label = label
Trace(obj)
ViewProviderTrace(obj.ViewObject)
if points:
obj.Points = points
return obj
class Trace:
def __init__(self, obj):
self.setCommonProperties(obj)
def setCommonProperties(self, obj):
pl = obj.PropertiesList
if not ("Points" in pl):
obj.addProperty("App::PropertyVectorList",
"Points",
"PlacementLine",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
)
if not ("ProjectionPoints" in pl):
obj.addProperty("App::PropertyVectorList",
"ProjectionPoints",
"PlacementLine",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
)
if not ("ShowTrend" in pl):
obj.addProperty("App::PropertyBool",
"ShowTrend",
"PlacementLine",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).ShowTrend = True
if not ("Edit" in pl):
obj.addProperty("App::PropertyBool",
"Edit",
"PlacementLine",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).Edit = False
self.Type = "TraceLine"
obj.Proxy = self
def onChanged(self, obj, prop):
''' Do something when a property has changed '''
if prop == "Edit":
if obj.Edit:
subobj = FreeCADGui.Selection.getSelectionEx()[0].SubObjects[0]
if subobj.__class__ == Part.Circle:
print ("Circle")
def execute(self, obj):
shape = None
base = None
if len(obj.Points) > 1:
base = Part.makePolygon(obj.Points)
import DraftGeomUtils
w = DraftGeomUtils.filletWire(base, 8000)
if w:
shape = w
if shape:
obj.Shape = shape
'''
if len(obj.ProjectionPoints) > 1 and obj.ShowTrend:
# TODO: check:
from scipy import stats
xx = list()
yy = list()
zz = list()
for pts in obj.ProjectionPoints:
xx.append(pts.x)
yy.append(pts.y)
zz.append(pts.z)
slope, intercept, r, p, std_err = stats.linregress(yy, zz)
def myfunc(x):
return slope * x + intercept
x = list()
x.append(yy[0])
x.append(yy[-1])
newzz = list(map(myfunc, x))
for p in range(len(obj.Points) - 1):
#found = [i for i in map(lambda n: float(n.replace(',', '.')), values) if 1.50 < i < 2.50]
r = []
#if obj.Points[p]
mi = obj.Points[p + 1]
ma = obj.Points[p]
for p3d in obj.ProjectionPoints:
if mi.x <= p3d.x <= ma.x and mi.y <= p3d.y <= ma.y:
r.append(p3d)
tmp = Part.makePolygon([FreeCAD.Vector(xx[0], yy[0], newzz[0]),
FreeCAD.Vector(xx[-1], yy[-1], newzz[-1])])
#obj.Shape = obj.Shape.fuse([tmp,])
'''
class ViewFunctions:
def get_stations(self, obj):
""" Retrieve the coordinates of the start and end points of the station
tick mark orthogonal to the alignment """
start = obj.Proxy.model.data['meta']['StartStation']
length = obj.Proxy.model.data['meta']['Length']
end = start + length/1000
stations = {}
for sta in range(int(start), int(end)):
if sta % 10 == 0:
tuple_coord, tuple_vec =\
obj.Proxy.model.get_orthogonal( sta, "Left", True)
coord = FreeCAD.Vector(tuple_coord)
vec = FreeCAD.Vector(tuple_vec)
left_vec = deepcopy(vec)
right_vec = deepcopy(vec)
left_side = coord.add(left_vec.multiply(1500))
right_side = coord.add(right_vec.negative().multiply(1500))
stations[sta] = [left_side, right_side]
return stations
class ViewProviderTrace(ViewFunctions):
def __init__(self, vobj):
''' Set view properties '''
self.Object = None
self.editor = None
self.select_state = True
self.active = False
vobj.Proxy = self
def getIcon(self):
''' Return object treeview icon '''
return str(os.path.join(DirIcons, "Trace.svg"))
def attach(self, vobj):
''' Create Object visuals in 3D view '''
self.Object = vobj.Object
# Line style.
line_style = coin.SoDrawStyle()
line_style.style = coin.SoDrawStyle.LINES
line_style.lineWidth = 2
# Line geometry keepers.
line_color = coin.SoBaseColor()
line_color.rgb = (1.0, 0.0, 0.0)
self.lines = coin.SoSeparator()
self.lines.addChild(line_style)
self.lines.addChild(line_color)
# Curve geometry keepers.
curve_color = coin.SoBaseColor()
curve_color.rgb = (0.0, 0.5, 0.0)
self.curves = coin.SoSeparator()
self.curves.addChild(line_style)
self.curves.addChild(curve_color)
# Spiral geometry keepers.
spiral_color = coin.SoBaseColor()
spiral_color.rgb = (0.0, 0.33, 1.0)
self.spirals = coin.SoSeparator()
self.spirals.addChild(line_style)
self.spirals.addChild(spiral_color)
# Labels root.
ticks = coin.SoSeparator()
self.tick_coords = coin.SoGeoCoordinate()
self.tick_lines = coin.SoLineSet()
ticks.addChild(self.tick_coords)
ticks.addChild(self.tick_lines)
self.labels = coin.SoSeparator()
# Alignment root.
lines_root = coin.SoSeparator()
lines_root.addChild(self.lines)
lines_root.addChild(self.curves)
lines_root.addChild(self.spirals)
lines_root.addChild(ticks)
lines_root.addChild(self.labels)
vobj.addDisplayMode(lines_root, "Wireframe")
def updateData(self, obj, prop):
'''
Update Object visuals when a data property changed.
'''
if prop == "Shape":
shape = obj.getPropertyByName(prop)
if not shape.SubShapes: return
# Set System.
#origin = geo_origin.get()
#geo_system = ["UTM", origin.UtmZone, "FLAT"]
copy_shape = shape.copy()
copy_shape.Placement.move(shape.Placement.Base)
#copy_shape.Placement.move(origin.Origin)
lines = copy_shape.SubShapes[0]
for wire in lines.Wires:
points = []
for vertex in wire.OrderedVertexes:
points.append(vertex.Point)
line = coin.SoType.fromName('SoFCSelection').createInstance()
line.style = 'EMISSIVE_DIFFUSE'
line_coords = coin.SoGeoCoordinate()
#line_coords.geoSystem.setValues(geo_system)
line_coords.point.values = points
line_set = coin.SoLineSet()
line.addChild(line_coords)
line.addChild(line_set)
self.lines.addChild(line)
del points
curves = copy_shape.SubShapes[1]
for wire in curves.Wires:
points = []
for vertex in wire.OrderedVertexes:
points.append(vertex.Point)
curve = coin.SoType.fromName('SoFCSelection').createInstance()
curve.style = 'EMISSIVE_DIFFUSE'
curve_coords = coin.SoGeoCoordinate()
#curve_coords.geoSystem.setValues(geo_system)
curve_coords.point.values = points
curve_set = coin.SoLineSet()
curve.addChild(curve_coords)
curve.addChild(curve_set)
self.curves.addChild(curve)
del points
spirals = copy_shape.SubShapes[2]
for wire in spirals.Wires:
points = []
for vertex in wire.OrderedVertexes:
points.append(vertex.Point)
spiral = coin.SoType.fromName('SoFCSelection').createInstance()
spiral.style = 'EMISSIVE_DIFFUSE'
spiral_coords = coin.SoGeoCoordinate()
#spiral_coords.geoSystem.setValues(geo_system)
spiral_coords.point.values = points
spiral_set = coin.SoLineSet()
spiral.addChild(spiral_coords)
spiral.addChild(spiral_set)
self.spirals.addChild(spiral)
del points
del copy_shape
def setEdit(self, vobj, mode=0):
"""Method called when the document requests the object to enter edit mode.
Edit mode is entered when a user double clicks on an object in the tree
view, or when they use the menu option [Edit -> Toggle Edit Mode].
Just display the standard Point Edit task panel.
Parameters
----------
mode: int or str
The edit mode the document has requested. Set to 0 when requested via
a double click or [Edit -> Toggle Edit Mode].
Returns
-------
bool
If edit mode was entered.
"""
if (mode == 0) and hasattr(self, "Object"):
import Utils.profile_editor as editor
if vobj.Selectable:
self.select_state = True
vobj.Selectable = False
pts = list()
for i in range(len(self.Object.Points)):
p = self.Object.Points[i]
#pts.append(editor.MarkerOnShape([p]))
self.editor = editor.Edit(self.Object) #(pts, self.Object)
'''for i in range(min(len(self.Object.Shape.Edges), len(self.editor.lines))):
self.editor.lines[i].updateLine()'''
self.active = True
return True
return False
def unsetEdit(self, vobj, mode=0):
"""
Disable edit
"""
import Utils.profile_editor as editor
if isinstance(self.editor, editor.Edit):# and check_pivy():
pts = list()
for p in self.editor.points:
if isinstance(p, editor.MarkerOnShape):
pt = p.points[0]
pts.append(FreeCAD.Vector(pt[0], pt[1], pt[2]))
self.Object.Points = pts
vobj.Selectable = self.select_state
self.editor.quit()
del self.editor
self.editor = None
self.active = False
self.Object.Document.recompute()
return True
def doubleClicked(self, vobj):
if not hasattr(self, 'active'):
self.active = False
if not self.active:
self.active = True
# self.setEdit(vobj)
vobj.Document.setEdit(vobj)
else:
vobj.Document.resetEdit()
self.active = False
return True
def setupContextMenu(self, obj, menu):
"""
Context menu construction
"""
pass
def edit(self):
"""
Edit callback
"""
pass
def __getstate__(self):
"""
Save variables to file.
"""
return None
def __setstate__(self, state):
"""
Get variables from file.
"""
return None
class CommandTrace:
def GetResources(self):
return {'Pixmap': str(os.path.join(DirIcons, "Trace.svg")),
'Accel': "T, R",
'MenuText': "Trace",
'ToolTip': "Trace"}
def Activated(self):
points = None
'''
sel = FreeCADGui.Selection.getSelection()
if sel:
points = sel[0].Points
'''
obj = makeTrace(points)
#obj.ViewObject.Document.setEdit(obj.ViewObject)
def IsActive(self):
if FreeCAD.ActiveDocument:
return True
else:
return False
if FreeCAD.GuiUp:
FreeCADGui.addCommand('Trace', CommandTrace())