464 lines
15 KiB
Python
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())
|