primera subida
This commit is contained in:
403
Utils/PVPlantFillets.py
Normal file
403
Utils/PVPlantFillets.py
Normal file
@@ -0,0 +1,403 @@
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2009, 2010 Yorik van Havre <yorik@uncreated.net> *
|
||||
# * Copyright (c) 2009, 2010 Ken Cline <cline@frii.com> *
|
||||
# * *
|
||||
# * This file is part of the FreeCAD CAx development system. *
|
||||
# * *
|
||||
# * 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. *
|
||||
# * *
|
||||
# * FreeCAD 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 FreeCAD; if not, write to the Free Software *
|
||||
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
# * USA *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
"""Provides various functions to work with fillets."""
|
||||
## @package fillets
|
||||
# \ingroup draftgeoutils
|
||||
# \brief Provides various functions to work with fillets.
|
||||
|
||||
import math
|
||||
import lazy_loader.lazy_loader as lz
|
||||
|
||||
import FreeCAD as App
|
||||
|
||||
from draftgeoutils.general import precision
|
||||
from draftgeoutils.arcs import arcFrom2Pts
|
||||
from draftgeoutils.wires import isReallyClosed
|
||||
|
||||
# Delay import of module until first use because it is heavy
|
||||
Part = lz.LazyLoader("Part", globals(), "Part")
|
||||
|
||||
## \addtogroup draftgeoutils
|
||||
# @{
|
||||
|
||||
|
||||
def fillet(lEdges, r, chamfer=False, limit=0):
|
||||
"""Return a list of sorted edges describing a round corner.
|
||||
|
||||
Author: Jacques-Antoine Gaudin
|
||||
"""
|
||||
|
||||
def getCurveType(edge, existingCurveType=None):
|
||||
"""Build or complete a dictionary containing edges.
|
||||
|
||||
The dictionary contains edges with keys 'Arc' and 'Line'.
|
||||
"""
|
||||
if not existingCurveType:
|
||||
existingCurveType = {'Line': [],
|
||||
'Arc': []}
|
||||
if issubclass(type(edge.Curve), Part.LineSegment):
|
||||
existingCurveType['Line'] += [edge]
|
||||
elif issubclass(type(edge.Curve), Part.Line):
|
||||
existingCurveType['Line'] += [edge]
|
||||
elif issubclass(type(edge.Curve), Part.Circle):
|
||||
existingCurveType['Arc'] += [edge]
|
||||
else:
|
||||
raise ValueError("Edge's curve must be either Line or Arc")
|
||||
return existingCurveType
|
||||
|
||||
rndEdges = lEdges[0:2]
|
||||
rndEdges = Part.__sortEdges__(rndEdges)
|
||||
|
||||
if len(rndEdges) < 2:
|
||||
return rndEdges
|
||||
|
||||
if r <= 0:
|
||||
print("DraftGeomUtils.fillet: Error: radius is negative.")
|
||||
return rndEdges
|
||||
|
||||
curveType = getCurveType(rndEdges[0])
|
||||
curveType = getCurveType(rndEdges[1], curveType)
|
||||
|
||||
lVertexes = rndEdges[0].Vertexes + [rndEdges[1].Vertexes[-1]]
|
||||
|
||||
if len(curveType['Line']) == 2:
|
||||
# Deals with 2-line-edges lists
|
||||
U1 = lVertexes[0].Point.sub(lVertexes[1].Point)
|
||||
U1.normalize()
|
||||
|
||||
U2 = lVertexes[2].Point.sub(lVertexes[1].Point)
|
||||
U2.normalize()
|
||||
|
||||
alpha = U1.getAngle(U2)
|
||||
|
||||
if chamfer:
|
||||
# correcting r value so the size of the chamfer = r
|
||||
beta = math.pi - alpha/2
|
||||
r = (r/2)/math.cos(beta)
|
||||
|
||||
# Edges have same direction
|
||||
if (round(alpha, precision()) == 0
|
||||
or round(alpha - math.pi, precision()) == 0):
|
||||
print("DraftGeomUtils.fillet: Warning: "
|
||||
"edges have same direction. Did nothing")
|
||||
return rndEdges
|
||||
|
||||
dToCenter = r / math.sin(alpha/2.0)
|
||||
dToTangent = (dToCenter**2-r**2)**(0.5)
|
||||
dirVect = App.Vector(U1)
|
||||
dirVect.scale(dToTangent, dToTangent, dToTangent)
|
||||
arcPt1 = lVertexes[1].Point.add(dirVect)
|
||||
|
||||
dirVect = U2.add(U1)
|
||||
dirVect.normalize()
|
||||
dirVect.scale(dToCenter - r, dToCenter - r, dToCenter - r)
|
||||
arcPt2 = lVertexes[1].Point.add(dirVect)
|
||||
|
||||
dirVect = App.Vector(U2)
|
||||
dirVect.scale(dToTangent, dToTangent, dToTangent)
|
||||
arcPt3 = lVertexes[1].Point.add(dirVect)
|
||||
|
||||
if (dToTangent > lEdges[0].Length) or (dToTangent > lEdges[1].Length):
|
||||
print("DraftGeomUtils.fillet: Error: radius value ", r,
|
||||
" is too high")
|
||||
return rndEdges
|
||||
|
||||
if chamfer:
|
||||
newedge = Part.Edge(Part.LineSegment(arcPt1, arcPt3))
|
||||
else:
|
||||
newedge = Part.Edge(Part.Arc(arcPt1, arcPt2, arcPt3))
|
||||
|
||||
if newedge.Length <= 100:
|
||||
return rndEdges
|
||||
|
||||
rndEdges[1] = newedge
|
||||
|
||||
if lVertexes[0].Point == arcPt1:
|
||||
# fillet consumes entire first edge
|
||||
rndEdges.pop(0)
|
||||
else:
|
||||
rndEdges[0] = Part.Edge(Part.LineSegment(lVertexes[0].Point, arcPt1))
|
||||
|
||||
if lVertexes[2].Point != arcPt3:
|
||||
# fillet does not consume entire second edge
|
||||
rndEdges += [Part.Edge(Part.LineSegment(arcPt3, lVertexes[2].Point))]
|
||||
|
||||
return rndEdges
|
||||
|
||||
elif len(curveType['Arc']) == 1:
|
||||
# Deals with lists containing an arc and a line
|
||||
if lEdges[0] in curveType['Arc']:
|
||||
lineEnd = lVertexes[2]
|
||||
arcEnd = lVertexes[0]
|
||||
arcFirst = True
|
||||
else:
|
||||
lineEnd = lVertexes[0]
|
||||
arcEnd = lVertexes[2]
|
||||
arcFirst = False
|
||||
arcCenter = curveType['Arc'][0].Curve.Center
|
||||
arcRadius = curveType['Arc'][0].Curve.Radius
|
||||
arcAxis = curveType['Arc'][0].Curve.Axis
|
||||
arcLength = curveType['Arc'][0].Length
|
||||
|
||||
U1 = lineEnd.Point.sub(lVertexes[1].Point)
|
||||
U1.normalize()
|
||||
toCenter = arcCenter.sub(lVertexes[1].Point)
|
||||
if arcFirst: # make sure the tangent points towards the arc
|
||||
T = arcAxis.cross(toCenter)
|
||||
else:
|
||||
T = toCenter.cross(arcAxis)
|
||||
|
||||
projCenter = toCenter.dot(U1)
|
||||
if round(abs(projCenter), precision()) > 0:
|
||||
normToLine = U1.cross(T).cross(U1)
|
||||
else:
|
||||
normToLine = App.Vector(toCenter)
|
||||
normToLine.normalize()
|
||||
|
||||
dCenterToLine = toCenter.dot(normToLine) - r
|
||||
|
||||
if round(projCenter, precision()) > 0:
|
||||
newRadius = arcRadius - r
|
||||
elif (round(projCenter, precision()) < 0
|
||||
or (round(projCenter, precision()) == 0 and U1.dot(T) > 0)):
|
||||
newRadius = arcRadius + r
|
||||
else:
|
||||
print("DraftGeomUtils.fillet: Warning: "
|
||||
"edges are already tangent. Did nothing")
|
||||
return rndEdges
|
||||
|
||||
toNewCent = newRadius**2 - dCenterToLine**2
|
||||
if toNewCent > 0:
|
||||
toNewCent = abs(abs(projCenter) - toNewCent**(0.5))
|
||||
else:
|
||||
print("DraftGeomUtils.fillet: Error: radius value ", r,
|
||||
" is too high")
|
||||
return rndEdges
|
||||
|
||||
U1.scale(toNewCent, toNewCent, toNewCent)
|
||||
normToLine.scale(r, r, r)
|
||||
newCent = lVertexes[1].Point.add(U1).add(normToLine)
|
||||
|
||||
arcPt1 = lVertexes[1].Point.add(U1)
|
||||
arcPt2 = lVertexes[1].Point.sub(newCent)
|
||||
arcPt2.normalize()
|
||||
arcPt2.scale(r, r, r)
|
||||
arcPt2 = arcPt2.add(newCent)
|
||||
|
||||
if newRadius == arcRadius - r:
|
||||
arcPt3 = newCent.sub(arcCenter)
|
||||
else:
|
||||
arcPt3 = arcCenter.sub(newCent)
|
||||
arcPt3.normalize()
|
||||
arcPt3.scale(r, r, r)
|
||||
arcPt3 = arcPt3.add(newCent)
|
||||
arcPt = [arcPt1, arcPt2, arcPt3]
|
||||
|
||||
# Warning: In the following I used a trick for calling
|
||||
# the right element in arcPt or V:
|
||||
# arcFirst is a boolean so - not arcFirst is -0 or -1
|
||||
# list[-1] is the last element of a list and list[0] the first
|
||||
# this way I don't have to proceed tests to know the position
|
||||
# of the arc
|
||||
myTrick = not arcFirst
|
||||
|
||||
V = [arcPt3]
|
||||
V += [arcEnd.Point]
|
||||
|
||||
toCenter.scale(-1, -1, -1)
|
||||
|
||||
delLength = arcRadius * V[0].sub(arcCenter).getAngle(toCenter)
|
||||
if delLength > arcLength or toNewCent > curveType['Line'][0].Length:
|
||||
print("DraftGeomUtils.fillet: Error: radius value ", r,
|
||||
" is too high")
|
||||
return rndEdges
|
||||
|
||||
arcAsEdge = arcFrom2Pts(V[-arcFirst], V[-myTrick], arcCenter, arcAxis)
|
||||
|
||||
V = [lineEnd.Point, arcPt1]
|
||||
lineAsEdge = Part.Edge(Part.LineSegment(V[-arcFirst], V[myTrick]))
|
||||
|
||||
rndEdges[not arcFirst] = arcAsEdge
|
||||
rndEdges[arcFirst] = lineAsEdge
|
||||
if chamfer:
|
||||
rndEdges[1:1] = [Part.Edge(Part.LineSegment(arcPt[- arcFirst],
|
||||
arcPt[- myTrick]))]
|
||||
else:
|
||||
rndEdges[1:1] = [Part.Edge(Part.Arc(arcPt[- arcFirst],
|
||||
arcPt[1],
|
||||
arcPt[- myTrick]))]
|
||||
|
||||
return rndEdges
|
||||
|
||||
elif len(curveType['Arc']) == 2:
|
||||
# Deals with lists of 2 arc-edges
|
||||
(arcCenter, arcRadius,
|
||||
arcAxis, arcLength,
|
||||
toCenter, T, newRadius) = [], [], [], [], [], [], []
|
||||
|
||||
for i in range(2):
|
||||
arcCenter += [curveType['Arc'][i].Curve.Center]
|
||||
arcRadius += [curveType['Arc'][i].Curve.Radius]
|
||||
arcAxis += [curveType['Arc'][i].Curve.Axis]
|
||||
arcLength += [curveType['Arc'][i].Length]
|
||||
toCenter += [arcCenter[i].sub(lVertexes[1].Point)]
|
||||
|
||||
T += [arcAxis[0].cross(toCenter[0])]
|
||||
T += [toCenter[1].cross(arcAxis[1])]
|
||||
CentToCent = toCenter[1].sub(toCenter[0])
|
||||
dCentToCent = CentToCent.Length
|
||||
|
||||
sameDirection = (arcAxis[0].dot(arcAxis[1]) > 0)
|
||||
TcrossT = T[0].cross(T[1])
|
||||
|
||||
if sameDirection:
|
||||
if round(TcrossT.dot(arcAxis[0]), precision()) > 0:
|
||||
newRadius += [arcRadius[0] + r]
|
||||
newRadius += [arcRadius[1] + r]
|
||||
elif round(TcrossT.dot(arcAxis[0]), precision()) < 0:
|
||||
newRadius += [arcRadius[0] - r]
|
||||
newRadius += [arcRadius[1] - r]
|
||||
elif T[0].dot(T[1]) > 0:
|
||||
newRadius += [arcRadius[0] + r]
|
||||
newRadius += [arcRadius[1] + r]
|
||||
else:
|
||||
print("DraftGeomUtils.fillet: Warning: "
|
||||
"edges are already tangent. Did nothing")
|
||||
return rndEdges
|
||||
|
||||
elif not sameDirection:
|
||||
if round(TcrossT.dot(arcAxis[0]), precision()) > 0:
|
||||
newRadius += [arcRadius[0] + r]
|
||||
newRadius += [arcRadius[1] - r]
|
||||
elif round(TcrossT.dot(arcAxis[0]), precision()) < 0:
|
||||
newRadius += [arcRadius[0] - r]
|
||||
newRadius += [arcRadius[1] + r]
|
||||
elif T[0].dot(T[1]) > 0:
|
||||
if arcRadius[0] > arcRadius[1]:
|
||||
newRadius += [arcRadius[0] - r]
|
||||
newRadius += [arcRadius[1] + r]
|
||||
elif arcRadius[1] > arcRadius[0]:
|
||||
newRadius += [arcRadius[0] + r]
|
||||
newRadius += [arcRadius[1] - r]
|
||||
else:
|
||||
print("DraftGeomUtils.fillet: Warning: "
|
||||
"arcs are coincident. Did nothing")
|
||||
return rndEdges
|
||||
else:
|
||||
print("DraftGeomUtils.fillet: Warning: "
|
||||
"edges are already tangent. Did nothing")
|
||||
return rndEdges
|
||||
|
||||
if (newRadius[0] + newRadius[1] < dCentToCent
|
||||
or newRadius[0] - newRadius[1] > dCentToCent
|
||||
or newRadius[1] - newRadius[0] > dCentToCent):
|
||||
print("DraftGeomUtils.fillet: Error: radius value ", r,
|
||||
" is too high")
|
||||
return rndEdges
|
||||
|
||||
x = ((dCentToCent**2 + newRadius[0]**2 - newRadius[1]**2)
|
||||
/ (2*dCentToCent))
|
||||
y = (newRadius[0]**2 - x**2)**(0.5)
|
||||
|
||||
CentToCent.normalize()
|
||||
toCenter[0].normalize()
|
||||
toCenter[1].normalize()
|
||||
if abs(toCenter[0].dot(toCenter[1])) != 1:
|
||||
normVect = CentToCent.cross(CentToCent.cross(toCenter[0]))
|
||||
else:
|
||||
normVect = T[0]
|
||||
|
||||
normVect.normalize()
|
||||
CentToCent.scale(x, x, x)
|
||||
normVect.scale(y, y, y)
|
||||
newCent = arcCenter[0].add(CentToCent.add(normVect))
|
||||
CentToNewCent = [newCent.sub(arcCenter[0]),
|
||||
newCent.sub(arcCenter[1])]
|
||||
|
||||
for i in range(2):
|
||||
CentToNewCent[i].normalize()
|
||||
if newRadius[i] == arcRadius[i] + r:
|
||||
CentToNewCent[i].scale(-r, -r, -r)
|
||||
else:
|
||||
CentToNewCent[i].scale(r, r, r)
|
||||
|
||||
toThirdPt = lVertexes[1].Point.sub(newCent)
|
||||
toThirdPt.normalize()
|
||||
toThirdPt.scale(r, r, r)
|
||||
arcPt1 = newCent.add(CentToNewCent[0])
|
||||
arcPt2 = newCent.add(toThirdPt)
|
||||
arcPt3 = newCent.add(CentToNewCent[1])
|
||||
arcPt = [arcPt1, arcPt2, arcPt3]
|
||||
|
||||
arcAsEdge = []
|
||||
for i in range(2):
|
||||
toCenter[i].scale(-1, -1, -1)
|
||||
delLength = (arcRadius[i]
|
||||
* arcPt[-i].sub(arcCenter[i]).getAngle(toCenter[i]))
|
||||
if delLength > arcLength[i]:
|
||||
print("DraftGeomUtils.fillet: Error: radius value ", r,
|
||||
" is too high")
|
||||
return rndEdges
|
||||
V = [arcPt[-i], lVertexes[-i].Point]
|
||||
arcAsEdge += [arcFrom2Pts(V[i-1], V[-i],
|
||||
arcCenter[i], arcAxis[i])]
|
||||
|
||||
rndEdges[0] = arcAsEdge[0]
|
||||
rndEdges[1] = arcAsEdge[1]
|
||||
if chamfer:
|
||||
rndEdges[1:1] = [Part.Edge(Part.LineSegment(arcPt[0], arcPt[2]))]
|
||||
else:
|
||||
rndEdges[1:1] = [Part.Edge(Part.Arc(arcPt[0],
|
||||
arcPt[1],
|
||||
arcPt[2]))]
|
||||
|
||||
return rndEdges
|
||||
|
||||
|
||||
def filletWire(aWire, r, chamfer=False):
|
||||
"""Fillet each angle of a wire with r as radius.
|
||||
|
||||
If chamfer is true, a `chamfer` is made instead, and `r` is the
|
||||
size of the chamfer.
|
||||
"""
|
||||
edges = aWire.Edges
|
||||
edges = Part.__sortEdges__(edges)
|
||||
filEdges = [edges[0]]
|
||||
|
||||
for i in range(len(edges) - 1):
|
||||
result = fillet([filEdges[-1], edges[i+1]], r, chamfer)
|
||||
if len(result) > 2:
|
||||
filEdges[-1:] = result[0:3]
|
||||
else:
|
||||
filEdges[-1:] = result[0:2]
|
||||
|
||||
if isReallyClosed(aWire):
|
||||
result = fillet([filEdges[-1], filEdges[0]], r, chamfer)
|
||||
if len(result) > 2:
|
||||
filEdges[-1:] = result[0:2]
|
||||
filEdges[0] = result[2]
|
||||
|
||||
return Part.Wire(filEdges)
|
||||
|
||||
## @}
|
||||
464
Utils/PVPlantTrace.py
Normal file
464
Utils/PVPlantTrace.py
Normal file
@@ -0,0 +1,464 @@
|
||||
# /**********************************************************************
|
||||
# * *
|
||||
# * 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())
|
||||
321
Utils/PVPlantUtils.py
Normal file
321
Utils/PVPlantUtils.py
Normal file
@@ -0,0 +1,321 @@
|
||||
import math
|
||||
|
||||
import FreeCAD
|
||||
import Part
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
from PySide import QtCore
|
||||
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
|
||||
|
||||
|
||||
|
||||
def VectorToPoint(vector):
|
||||
from shapely.geometry import Point
|
||||
if True:
|
||||
return Point(vector.x, vector.y)
|
||||
else:
|
||||
return Point(vector.x, vector.y, vector.z)
|
||||
|
||||
# buscar el camino más corto:
|
||||
from collections import defaultdict
|
||||
class Graph():
|
||||
def __init__(self):
|
||||
"""
|
||||
self.edges is a dict of all possible next nodes
|
||||
e.g. {'X': ['A', 'B', 'C', 'E'], ...}
|
||||
self.weights has all the weights between two nodes,
|
||||
with the two nodes as a tuple as the key
|
||||
e.g. {('X', 'A'): 7, ('X', 'B'): 2, ...}
|
||||
"""
|
||||
self.edges = defaultdict(list)
|
||||
self.weights = {}
|
||||
|
||||
def add_edge(self, from_node, to_node, weight):
|
||||
# Note: assumes edges are bi-directional
|
||||
self.edges[from_node].append(to_node)
|
||||
self.edges[to_node].append(from_node)
|
||||
self.weights[(from_node, to_node)] = weight
|
||||
self.weights[(to_node, from_node)] = weight
|
||||
|
||||
|
||||
|
||||
def dijsktra(graph, initial, end):
|
||||
# shortest paths is a dict of nodes
|
||||
# whose value is a tuple of (previous node, weight)
|
||||
shortest_paths = {initial: (None, 0)}
|
||||
current_node = initial
|
||||
visited = set()
|
||||
|
||||
while current_node != end:
|
||||
visited.add(current_node)
|
||||
destinations = graph.edges[current_node]
|
||||
weight_to_current_node = shortest_paths[current_node][1]
|
||||
|
||||
for next_node in destinations:
|
||||
weight = graph.weights[(current_node, next_node)] + weight_to_current_node
|
||||
if next_node not in shortest_paths:
|
||||
shortest_paths[next_node] = (current_node, weight)
|
||||
else:
|
||||
current_shortest_weight = shortest_paths[next_node][1]
|
||||
if current_shortest_weight > weight:
|
||||
shortest_paths[next_node] = (current_node, weight)
|
||||
|
||||
next_destinations = {node: shortest_paths[node] for node in shortest_paths if node not in visited}
|
||||
if not next_destinations:
|
||||
return "Route Not Possible"
|
||||
# next node is the destination with the lowest weight
|
||||
current_node = min(next_destinations, key=lambda k: next_destinations[k][1])
|
||||
|
||||
# Work back through destinations in shortest path
|
||||
path = []
|
||||
while current_node is not None:
|
||||
path.append(current_node)
|
||||
next_node = shortest_paths[current_node][0]
|
||||
current_node = next_node
|
||||
# Reverse path
|
||||
path = path[::-1]
|
||||
return path
|
||||
|
||||
|
||||
'''
|
||||
Flatten a wire to 2 axis: X - Y
|
||||
'''
|
||||
def FlattenWire(wire):
|
||||
pts = []
|
||||
xx = 0
|
||||
for i, ver in enumerate(wire.Vertexes):
|
||||
if i == 0:
|
||||
pts.append(FreeCAD.Vector(xx, ver.Point.z, 0))
|
||||
else:
|
||||
xx += (ver.Point - wire.Vertexes[i - 1].Point).Length
|
||||
pts.append(FreeCAD.Vector(xx, ver.Point.z, 0))
|
||||
return Part.makePolygon(pts)
|
||||
|
||||
def flattenWire(wire):
|
||||
pts = []
|
||||
for i, ver in enumerate(wire.Vertexes):
|
||||
point = FreeCAD.Vector(ver.Point)
|
||||
point.z = 0
|
||||
pts.append(point)
|
||||
return Part.makePolygon(pts)
|
||||
|
||||
def simplifyWire(wire):
|
||||
if not wire.isDerivedFrom("Part::Part2DObject"):
|
||||
wire = flattenWire(wire)
|
||||
pts = [ver.Point for ver in wire.Vertexes]
|
||||
for i in range(len(pts)-2):
|
||||
'''
|
||||
'''
|
||||
|
||||
'''for i in range(len(wire.Edges) - 1):
|
||||
ed1 = wire.Edges[i]
|
||||
ed2 = wire.Edges[i + 1]
|
||||
vec1 = ed1.Vertexes[1].Point.sub(ed1.Vertexes[0].Point)
|
||||
vec2 = ed2.Vertexes[1].Point.sub(ed2.Vertexes[0].Point)
|
||||
angle = vec1.getAngle(vec2)
|
||||
if angle == 0:'''
|
||||
|
||||
|
||||
|
||||
def getPointsFromVertexes(Vertexes):
|
||||
return [ver.Point for ver in Vertexes]
|
||||
|
||||
def makeProfileFromTerrain(path):
|
||||
import PVPlantSite
|
||||
terrain = PVPlantSite.get().Terrain
|
||||
if terrain:
|
||||
return terrain.Shape.makeParallelProjection(path.Shape.Wires[0], FreeCAD.Vector(0, 0, 1))
|
||||
|
||||
def angleToPercent(deg):
|
||||
''' ángulo en porcentaje = tan(ángulo en grados) * 100% '''
|
||||
|
||||
return math.tan(math.radians(deg)) * 100
|
||||
|
||||
def PercentToAngle(percent):
|
||||
''' ángulo en grados = arctan(ángulo en porcentaje / 100%) '''
|
||||
|
||||
return math.degrees(math.atan(percent / 100))
|
||||
|
||||
def getEWAngle(frame1, frame2):
|
||||
vec = frame2.Placement.Base.sub(frame1.Placement.Base)
|
||||
rad = vec.getAngle(FreeCAD.Vector(1, 0, 0))
|
||||
deg = math.degrees(rad)
|
||||
if deg > 90:
|
||||
deg = 180 - deg
|
||||
return deg
|
||||
|
||||
def simplifyWire(wire):
|
||||
import math
|
||||
|
||||
closed = False
|
||||
points = [ver.Point for ver in wire.Vertexes]
|
||||
if points[0] == points[-1]:
|
||||
points.pop(-1)
|
||||
closed = True
|
||||
|
||||
if len(points) < 3:
|
||||
return wire
|
||||
|
||||
'''cnt = 0
|
||||
while cnt < (len(points) - 2):
|
||||
p1 = cnt + 1
|
||||
if p1 == len(points)-2:
|
||||
p1 = 0
|
||||
elif p1 == len(points)-1:
|
||||
p1 = 1
|
||||
|
||||
p2 = p1 + 1
|
||||
vec1 = points[p1].sub(points[cnt])
|
||||
vec2 = points[p2].sub(points[p1])
|
||||
angle = vec2.getAngle(vec1)
|
||||
# Edges have same direction
|
||||
if (round(angle, 2) == 0) or (round(angle - math.pi, 2) == 0):
|
||||
points.pop(cnt + 1)
|
||||
else:
|
||||
cnt += 1'''
|
||||
|
||||
i = 0
|
||||
while i < len(points) - 2:
|
||||
p1 = points[(i + 1) % len(points)]
|
||||
p2 = points[(i + 2) % len(points)]
|
||||
vec1 = p1 - points[i]
|
||||
vec2 = p2 - p1
|
||||
angle = vec2.getAngle(vec1)
|
||||
# Agregar tolerancia para el ángulo
|
||||
if round(angle, 2) == 0 or round(angle - math.pi, 2) == 0:
|
||||
points.pop(i + 1)
|
||||
else:
|
||||
i += 1
|
||||
|
||||
if closed:
|
||||
points.append(FreeCAD.Vector(points[0]))
|
||||
|
||||
pol = Part.makePolygon(points)
|
||||
return pol
|
||||
|
||||
def getProjected(shape, direction=FreeCAD.Vector(0, 0, 1)): # Based on Draft / shape2dview
|
||||
""" returns projected edges from a shape and a direction """
|
||||
|
||||
import Part, TechDraw
|
||||
edges = []
|
||||
groups = TechDraw.projectEx(shape, direction)
|
||||
for g in groups[0:5]:
|
||||
if g:
|
||||
if len(g.Edges) > 0:
|
||||
edges.extend(g.Edges)
|
||||
if len(edges) > 1:
|
||||
ow = TechDraw.findOuterWire(edges)
|
||||
else:
|
||||
ow = Part.Wire(edges[0])
|
||||
return ow
|
||||
|
||||
|
||||
def findObjects(classtype):
|
||||
objects = FreeCAD.ActiveDocument.Objects
|
||||
objlist = list()
|
||||
for object in objects:
|
||||
if hasattr(object, "Proxy"):
|
||||
if object.Proxy.Type == classtype:
|
||||
objlist.append(object)
|
||||
return objlist
|
||||
|
||||
def getClosePoints(sh1, angle):
|
||||
'''
|
||||
sh1= reference
|
||||
tag angle = height / offset
|
||||
- dato conocido: angle
|
||||
- si conocemos offset (= 1 mm) => height = offset * tag angle
|
||||
'''
|
||||
|
||||
import Draft, math, Part
|
||||
import MeshPart as mp
|
||||
|
||||
pts = []
|
||||
for ver in sh1.Vertexes:
|
||||
point = FreeCAD.Vector(ver.Point)
|
||||
point.z = 0
|
||||
pts.append([point, ver])
|
||||
minsh1 = min(pts, key=lambda x: x[1].Z)[1]
|
||||
|
||||
projected = getProjected(max(sh1.Wires, key=lambda x: x.Length))
|
||||
sh2 = projected.makeOffset2D(1, 0, False, False, True)
|
||||
height = math.tan(math.radians(angle))
|
||||
sh2.Placement.Base.z = - height
|
||||
sh2pts = []
|
||||
for edge in sh2.Edges:
|
||||
count = 0
|
||||
if issubclass(type(edge.Curve), Part.Circle):
|
||||
v1 = edge.Vertexes[0].Point.sub(edge.Curve.Center)
|
||||
v2 = edge.Curve.Center.sub(edge.Vertexes[0].Point)
|
||||
count = int(v1.getAngle(v2) / math.radians(5))
|
||||
|
||||
if count > 0:
|
||||
sh2pts.extend(edge.discretize(Number=count))
|
||||
else:
|
||||
sh2pts.append(edge.Vertexes[0].Point)
|
||||
|
||||
def getline(real, pro):
|
||||
land = FreeCAD.ActiveDocument.Mesh002.Mesh
|
||||
vec = FreeCAD.Vector(real.Point)
|
||||
vec.z = 0
|
||||
vec = vec.sub(pro)
|
||||
vec.normalize()
|
||||
vec.Length = 10000
|
||||
vec = real.Point.sub(vec)
|
||||
line = Part.LineSegment(real.Point, vec)
|
||||
plane = Part.Plane(minsh1, FreeCAD.Vector(0, 0, 1))
|
||||
|
||||
tmp = mp.projectPointsOnMesh([real.Point,], land, vec)
|
||||
if len(tmp) > 0:
|
||||
return tmp[0]
|
||||
|
||||
#Draft.makeLine(real.Point, vec)
|
||||
#return vec
|
||||
|
||||
lines = []
|
||||
if len(sh1.Vertexes) >= len(sh2pts):
|
||||
for ver in sh1.Vertexes:
|
||||
point = FreeCAD.Vector(ver.Point)
|
||||
point.z = 0
|
||||
mn = min(sh2.Vertexes, key=lambda x: x.Point.sub(point).Length)
|
||||
getpoints(ver, mn)
|
||||
else:
|
||||
from datetime import datetime
|
||||
starttime = datetime.now()
|
||||
for point in sh2pts:
|
||||
mn = min(pts, key=lambda x: x[0].sub(point).Length)
|
||||
lines.append(getline(mn[1], point))
|
||||
total_time = datetime.now() - starttime
|
||||
print(" -- Tiempo tardado en ajustar al terreno:", total_time)
|
||||
|
||||
ret = [ver.Point for ver in sh1.Vertexes]
|
||||
ret.extend(lines)
|
||||
Draft.makeWire(lines, closed=True)
|
||||
import Mesh
|
||||
from MeshTools.Triangulation import Triangulate
|
||||
m = Triangulate(ret, MaxlengthLE=4000)
|
||||
Mesh.show(m)
|
||||
|
||||
|
||||
import FreeCAD as App
|
||||
|
||||
def offset_wire(wire, distance):
|
||||
# Crear un tubo alrededor de la wire original
|
||||
tube = Part.makeTube(wire, distance)
|
||||
# Offsetear el tubo
|
||||
offset_wire = tube.makeOffsetShape(distance)
|
||||
return offset_wire
|
||||
348
Utils/gordon_profile_FP - copia.py
Normal file
348
Utils/gordon_profile_FP - copia.py
Normal file
@@ -0,0 +1,348 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
__title__ = "Freehand BSpline"
|
||||
__author__ = "Christophe Grellier (Chris_G)"
|
||||
__license__ = "LGPL 2.1"
|
||||
__doc__ = "Creates an freehand BSpline curve"
|
||||
__usage__ = """*** Interpolation curve control keys :
|
||||
|
||||
a - Select all / Deselect
|
||||
i - Insert point in selected segments
|
||||
t - Set / unset tangent (view direction)
|
||||
p - Align selected objects
|
||||
s - Snap points on shape / Unsnap
|
||||
l - Set/unset a linear interpolation
|
||||
x,y,z - Axis constraints during grab
|
||||
q - Apply changes and quit editing"""
|
||||
|
||||
import os
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import Part
|
||||
|
||||
from . import ICONPATH
|
||||
from . import _utils
|
||||
from . import profile_editor
|
||||
|
||||
TOOL_ICON = os.path.join(ICONPATH, 'editableSpline.svg')
|
||||
# debug = _utils.debug
|
||||
debug = _utils.doNothing
|
||||
|
||||
|
||||
def check_pivy():
|
||||
try:
|
||||
profile_editor.MarkerOnShape([FreeCAD.Vector()])
|
||||
return True
|
||||
except Exception as exc:
|
||||
FreeCAD.Console.PrintWarning(str(exc) + "\nPivy interaction library failure\n")
|
||||
return False
|
||||
|
||||
|
||||
def midpoint(e):
|
||||
p = e.FirstParameter + 0.5 * (e.LastParameter - e.FirstParameter)
|
||||
return e.valueAt(p)
|
||||
|
||||
|
||||
class GordonProfileFP:
|
||||
"""Creates an editable interpolation curve"""
|
||||
def __init__(self, obj, s, d, t):
|
||||
"""Add the properties"""
|
||||
obj.addProperty("App::PropertyLinkSubList", "Support", "Profile", "Constraint shapes").Support = s
|
||||
obj.addProperty("App::PropertyFloatConstraint", "Parametrization", "Profile", "Parametrization factor")
|
||||
obj.addProperty("App::PropertyFloat", "Tolerance", "Profile", "Tolerance").Tolerance = 1e-5
|
||||
obj.addProperty("App::PropertyBool", "Periodic", "Profile", "Periodic curve").Periodic = False
|
||||
obj.addProperty("App::PropertyVectorList", "Data", "Profile", "Data list").Data = d
|
||||
obj.addProperty("App::PropertyVectorList", "Tangents", "Profile", "Tangents list")
|
||||
obj.addProperty("App::PropertyBoolList", "Flags", "Profile", "Tangent flags")
|
||||
obj.addProperty("App::PropertyIntegerList", "DataType", "Profile", "Types of interpolated points").DataType = t
|
||||
obj.addProperty("App::PropertyBoolList", "LinearSegments", "Profile", "Linear segment flags")
|
||||
obj.Parametrization = (1.0, 0.0, 1.0, 0.05)
|
||||
obj.Proxy = self
|
||||
|
||||
def get_shapes(self, fp):
|
||||
if hasattr(fp, 'Support'):
|
||||
sl = list()
|
||||
for ob, names in fp.Support:
|
||||
for name in names:
|
||||
if "Vertex" in name:
|
||||
n = eval(name.lstrip("Vertex"))
|
||||
if len(ob.Shape.Vertexes) >= n:
|
||||
sl.append(ob.Shape.Vertexes[n - 1])
|
||||
elif ("Point" in name):
|
||||
sl.append(Part.Vertex(ob.Shape.Point))
|
||||
elif ("Edge" in name):
|
||||
n = eval(name.lstrip("Edge"))
|
||||
if len(ob.Shape.Edges) >= n:
|
||||
sl.append(ob.Shape.Edges[n - 1])
|
||||
elif ("Face" in name):
|
||||
n = eval(name.lstrip("Face"))
|
||||
if len(ob.Shape.Faces) >= n:
|
||||
sl.append(ob.Shape.Faces[n - 1])
|
||||
return sl
|
||||
|
||||
def get_points(self, fp, stretch=True):
|
||||
touched = False
|
||||
shapes = self.get_shapes(fp)
|
||||
if not len(fp.Data) == len(fp.DataType):
|
||||
FreeCAD.Console.PrintError("Gordon Profile : Data and DataType mismatch\n")
|
||||
return(None)
|
||||
pts = list()
|
||||
shape_idx = 0
|
||||
for i in range(len(fp.Data)):
|
||||
if fp.DataType[i] == 0: # Free point
|
||||
pts.append(fp.Data[i])
|
||||
elif (fp.DataType[i] == 1):
|
||||
if (shape_idx < len(shapes)): # project on shape
|
||||
d, p, i = Part.Vertex(fp.Data[i]).distToShape(shapes[shape_idx])
|
||||
if d > fp.Tolerance:
|
||||
touched = True
|
||||
pts.append(p[0][1]) # shapes[shape_idx].valueAt(fp.Data[i].x))
|
||||
shape_idx += 1
|
||||
else:
|
||||
pts.append(fp.Data[i])
|
||||
if stretch and touched:
|
||||
params = [0]
|
||||
knots = [0]
|
||||
moves = [pts[0] - fp.Data[0]]
|
||||
lsum = 0
|
||||
mults = [2]
|
||||
for i in range(1, len(pts)):
|
||||
lsum += fp.Data[i - 1].distanceToPoint(fp.Data[i])
|
||||
params.append(lsum)
|
||||
if fp.DataType[i] == 1:
|
||||
knots.append(lsum)
|
||||
moves.append(pts[i] - fp.Data[i])
|
||||
mults.insert(1, 1)
|
||||
mults[-1] = 2
|
||||
if len(moves) < 2:
|
||||
return(pts)
|
||||
# FreeCAD.Console.PrintMessage("%s\n%s\n%s\n"%(moves,mults,knots))
|
||||
curve = Part.BSplineCurve()
|
||||
curve.buildFromPolesMultsKnots(moves, mults, knots, False, 1)
|
||||
for i in range(1, len(pts)):
|
||||
if fp.DataType[i] == 0:
|
||||
# FreeCAD.Console.PrintMessage("Stretch %s #%d: %s to %s\n"%(fp.Label,i,pts[i],curve.value(params[i])))
|
||||
pts[i] += curve.value(params[i])
|
||||
if touched:
|
||||
return pts
|
||||
else:
|
||||
return False
|
||||
|
||||
def execute(self, obj):
|
||||
try:
|
||||
o = FreeCADGui.ActiveDocument.getInEdit().Object
|
||||
if o == obj:
|
||||
return
|
||||
except:
|
||||
FreeCAD.Console.PrintWarning("execute is disabled during editing\n")
|
||||
pts = self.get_points(obj)
|
||||
if pts:
|
||||
if len(pts) < 2:
|
||||
FreeCAD.Console.PrintError("{} : Not enough points\n".format(obj.Label))
|
||||
return False
|
||||
else:
|
||||
obj.Data = pts
|
||||
else:
|
||||
pts = obj.Data
|
||||
|
||||
tans = [FreeCAD.Vector()] * len(pts)
|
||||
flags = [False] * len(pts)
|
||||
for i in range(len(obj.Tangents)):
|
||||
tans[i] = obj.Tangents[i]
|
||||
for i in range(len(obj.Flags)):
|
||||
flags[i] = obj.Flags[i]
|
||||
# if not (len(obj.LinearSegments) == len(pts)-1):
|
||||
# FreeCAD.Console.PrintError("%s : Points and LinearSegments mismatch\n"%obj.Label)
|
||||
if len(obj.LinearSegments) > 0:
|
||||
for i, b in enumerate(obj.LinearSegments):
|
||||
if b:
|
||||
tans[i] = pts[i + 1] - pts[i]
|
||||
tans[i + 1] = tans[i]
|
||||
flags[i] = True
|
||||
flags[i + 1] = True
|
||||
params = profile_editor.parameterization(pts, obj.Parametrization, obj.Periodic)
|
||||
|
||||
curve = Part.BSplineCurve()
|
||||
if len(pts) == 2:
|
||||
curve.buildFromPoles(pts)
|
||||
elif obj.Periodic and pts[0].distanceToPoint(pts[-1]) < 1e-7:
|
||||
curve.interpolate(Points=pts[:-1], Parameters=params, PeriodicFlag=obj.Periodic, Tolerance=obj.Tolerance, Tangents=tans[:-1], TangentFlags=flags[:-1])
|
||||
else:
|
||||
curve.interpolate(Points=pts, Parameters=params, PeriodicFlag=obj.Periodic, Tolerance=obj.Tolerance, Tangents=tans, TangentFlags=flags)
|
||||
obj.Shape = curve.toShape()
|
||||
|
||||
def onChanged(self, fp, prop):
|
||||
if prop in ("Support", "Data", "DataType", "Periodic"):
|
||||
# FreeCAD.Console.PrintMessage("%s : %s changed\n"%(fp.Label,prop))
|
||||
if (len(fp.Data) == len(fp.DataType)) and (sum(fp.DataType) == len(fp.Support)):
|
||||
new_pts = self.get_points(fp, True)
|
||||
if new_pts:
|
||||
fp.Data = new_pts
|
||||
if prop == "Parametrization":
|
||||
self.execute(fp)
|
||||
|
||||
def onDocumentRestored(self, fp):
|
||||
fp.setEditorMode("Data", 2)
|
||||
fp.setEditorMode("DataType", 2)
|
||||
|
||||
|
||||
class GordonProfileVP:
|
||||
def __init__(self, vobj):
|
||||
vobj.Proxy = self
|
||||
self.select_state = True
|
||||
self.active = False
|
||||
|
||||
def getIcon(self):
|
||||
return TOOL_ICON
|
||||
|
||||
def attach(self, vobj):
|
||||
self.Object = vobj.Object
|
||||
self.active = False
|
||||
self.select_state = vobj.Selectable
|
||||
self.ip = None
|
||||
|
||||
def setEdit(self, vobj, mode=0):
|
||||
if mode == 0 and check_pivy():
|
||||
if vobj.Selectable:
|
||||
self.select_state = True
|
||||
vobj.Selectable = False
|
||||
pts = list()
|
||||
sl = list()
|
||||
for ob, names in self.Object.Support:
|
||||
for name in names:
|
||||
sl.append((ob, (name,)))
|
||||
shape_idx = 0
|
||||
for i in range(len(self.Object.Data)):
|
||||
p = self.Object.Data[i]
|
||||
t = self.Object.DataType[i]
|
||||
if t == 0:
|
||||
pts.append(profile_editor.MarkerOnShape([p]))
|
||||
elif t == 1:
|
||||
pts.append(profile_editor.MarkerOnShape([p], sl[shape_idx]))
|
||||
shape_idx += 1
|
||||
for i in range(len(pts)): # p,t,f in zip(pts, self.Object.Tangents, self.Object.Flags):
|
||||
if i < min(len(self.Object.Flags), len(self.Object.Tangents)):
|
||||
if self.Object.Flags[i]:
|
||||
pts[i].tangent = self.Object.Tangents[i]
|
||||
self.ip = profile_editor.InterpoCurveEditor(pts, self.Object)
|
||||
self.ip.periodic = self.Object.Periodic
|
||||
self.ip.param_factor = self.Object.Parametrization
|
||||
for i in range(min(len(self.Object.LinearSegments), len(self.ip.lines))):
|
||||
self.ip.lines[i].tangent = self.Object.LinearSegments[i]
|
||||
self.ip.lines[i].updateLine()
|
||||
self.active = True
|
||||
return True
|
||||
return False
|
||||
|
||||
def unsetEdit(self, vobj, mode=0):
|
||||
if isinstance(self.ip, profile_editor.InterpoCurveEditor) and check_pivy():
|
||||
pts = list()
|
||||
typ = list()
|
||||
tans = list()
|
||||
flags = list()
|
||||
# original_links = self.Object.Support
|
||||
new_links = list()
|
||||
for p in self.ip.points:
|
||||
if isinstance(p, profile_editor.MarkerOnShape):
|
||||
pt = p.points[0]
|
||||
pts.append(FreeCAD.Vector(pt[0], pt[1], pt[2]))
|
||||
if p.sublink:
|
||||
new_links.append(p.sublink)
|
||||
typ.append(1)
|
||||
else:
|
||||
typ.append(0)
|
||||
if p.tangent:
|
||||
tans.append(p.tangent)
|
||||
flags.append(True)
|
||||
else:
|
||||
tans.append(FreeCAD.Vector())
|
||||
flags.append(False)
|
||||
self.Object.Tangents = tans
|
||||
self.Object.Flags = flags
|
||||
self.Object.LinearSegments = [li.linear for li in self.ip.lines]
|
||||
self.Object.DataType = typ
|
||||
self.Object.Data = pts
|
||||
self.Object.Support = new_links
|
||||
vobj.Selectable = self.select_state
|
||||
self.ip.quit()
|
||||
self.ip = 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 __getstate__(self):
|
||||
return {"name": self.Object.Name}
|
||||
|
||||
def __setstate__(self, state):
|
||||
self.Object = FreeCAD.ActiveDocument.getObject(state["name"])
|
||||
return None
|
||||
|
||||
|
||||
class GordonProfileCommand:
|
||||
"""Creates a editable interpolation curve"""
|
||||
|
||||
def makeFeature(self, sub, pts, typ):
|
||||
fp = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Freehand BSpline")
|
||||
GordonProfileFP(fp, sub, pts, typ)
|
||||
GordonProfileVP(fp.ViewObject)
|
||||
FreeCAD.Console.PrintMessage(__usage__)
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
FreeCADGui.SendMsgToActiveView("ViewFit")
|
||||
fp.ViewObject.Document.setEdit(fp.ViewObject)
|
||||
|
||||
def Activated(self):
|
||||
s = FreeCADGui.Selection.getSelectionEx()
|
||||
try:
|
||||
ordered = FreeCADGui.activeWorkbench().Selection
|
||||
if ordered:
|
||||
s = ordered
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
sub = list()
|
||||
pts = list()
|
||||
for obj in s:
|
||||
if obj.HasSubObjects:
|
||||
# FreeCAD.Console.PrintMessage("object has subobjects %s\n"%str(obj.SubElementNames))
|
||||
for n in obj.SubElementNames:
|
||||
sub.append((obj.Object, [n]))
|
||||
for p in obj.PickedPoints:
|
||||
pts.append(p)
|
||||
|
||||
if len(pts) == 0:
|
||||
pts = [FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(5, 0, 0), FreeCAD.Vector(10, 0, 0)]
|
||||
typ = [0, 0, 0]
|
||||
elif len(pts) == 1:
|
||||
pts.append(pts[0] + FreeCAD.Vector(5, 0, 0))
|
||||
pts.append(pts[0] + FreeCAD.Vector(10, 0, 0))
|
||||
typ = [1, 0, 0]
|
||||
else:
|
||||
typ = [1] * len(pts)
|
||||
self.makeFeature(sub, pts, typ)
|
||||
|
||||
def IsActive(self):
|
||||
if FreeCAD.ActiveDocument:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap': TOOL_ICON,
|
||||
'MenuText': __title__,
|
||||
'ToolTip': "{}<br><br><b>Usage :</b><br>{}".format(__doc__, "<br>".join(__usage__.splitlines()))}
|
||||
|
||||
|
||||
FreeCADGui.addCommand('gordon_profile', GordonProfileCommand())
|
||||
348
Utils/gordon_profile_FP.py
Normal file
348
Utils/gordon_profile_FP.py
Normal file
@@ -0,0 +1,348 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
__title__ = "Freehand BSpline"
|
||||
__author__ = "Christophe Grellier (Chris_G)"
|
||||
__license__ = "LGPL 2.1"
|
||||
__doc__ = "Creates an freehand BSpline curve"
|
||||
__usage__ = """*** Interpolation curve control keys :
|
||||
|
||||
a - Select all / Deselect
|
||||
i - Insert point in selected segments
|
||||
t - Set / unset tangent (view direction)
|
||||
p - Align selected objects
|
||||
s - Snap points on shape / Unsnap
|
||||
l - Set/unset a linear interpolation
|
||||
x,y,z - Axis constraints during grab
|
||||
q - Apply changes and quit editing"""
|
||||
|
||||
import os
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import Part
|
||||
|
||||
from . import ICONPATH
|
||||
from . import _utils
|
||||
from . import profile_editor
|
||||
|
||||
TOOL_ICON = os.path.join(ICONPATH, 'editableSpline.svg')
|
||||
# debug = _utils.debug
|
||||
debug = _utils.doNothing
|
||||
|
||||
|
||||
def check_pivy():
|
||||
try:
|
||||
profile_editor.MarkerOnShape([FreeCAD.Vector()])
|
||||
return True
|
||||
except Exception as exc:
|
||||
FreeCAD.Console.PrintWarning(str(exc) + "\nPivy interaction library failure\n")
|
||||
return False
|
||||
|
||||
|
||||
def midpoint(e):
|
||||
p = e.FirstParameter + 0.5 * (e.LastParameter - e.FirstParameter)
|
||||
return e.valueAt(p)
|
||||
|
||||
|
||||
class GordonProfileFP:
|
||||
"""Creates an editable interpolation curve"""
|
||||
def __init__(self, obj, s, d, t):
|
||||
"""Add the properties"""
|
||||
obj.addProperty("App::PropertyLinkSubList", "Support", "Profile", "Constraint shapes").Support = s
|
||||
obj.addProperty("App::PropertyFloatConstraint", "Parametrization", "Profile", "Parametrization factor")
|
||||
obj.addProperty("App::PropertyFloat", "Tolerance", "Profile", "Tolerance").Tolerance = 1e-5
|
||||
obj.addProperty("App::PropertyBool", "Periodic", "Profile", "Periodic curve").Periodic = False
|
||||
obj.addProperty("App::PropertyVectorList", "Data", "Profile", "Data list").Data = d
|
||||
obj.addProperty("App::PropertyVectorList", "Tangents", "Profile", "Tangents list")
|
||||
obj.addProperty("App::PropertyBoolList", "Flags", "Profile", "Tangent flags")
|
||||
obj.addProperty("App::PropertyIntegerList", "DataType", "Profile", "Types of interpolated points").DataType = t
|
||||
obj.addProperty("App::PropertyBoolList", "LinearSegments", "Profile", "Linear segment flags")
|
||||
obj.Parametrization = (1.0, 0.0, 1.0, 0.05)
|
||||
obj.Proxy = self
|
||||
|
||||
def get_shapes(self, fp):
|
||||
if hasattr(fp, 'Support'):
|
||||
sl = list()
|
||||
for ob, names in fp.Support:
|
||||
for name in names:
|
||||
if "Vertex" in name:
|
||||
n = eval(name.lstrip("Vertex"))
|
||||
if len(ob.Shape.Vertexes) >= n:
|
||||
sl.append(ob.Shape.Vertexes[n - 1])
|
||||
elif ("Point" in name):
|
||||
sl.append(Part.Vertex(ob.Shape.Point))
|
||||
elif ("Edge" in name):
|
||||
n = eval(name.lstrip("Edge"))
|
||||
if len(ob.Shape.Edges) >= n:
|
||||
sl.append(ob.Shape.Edges[n - 1])
|
||||
elif ("Face" in name):
|
||||
n = eval(name.lstrip("Face"))
|
||||
if len(ob.Shape.Faces) >= n:
|
||||
sl.append(ob.Shape.Faces[n - 1])
|
||||
return sl
|
||||
|
||||
def get_points(self, fp, stretch=True):
|
||||
touched = False
|
||||
shapes = self.get_shapes(fp)
|
||||
if not len(fp.Data) == len(fp.DataType):
|
||||
FreeCAD.Console.PrintError("Gordon Profile : Data and DataType mismatch\n")
|
||||
return(None)
|
||||
pts = list()
|
||||
shape_idx = 0
|
||||
for i in range(len(fp.Data)):
|
||||
if fp.DataType[i] == 0: # Free point
|
||||
pts.append(fp.Data[i])
|
||||
elif (fp.DataType[i] == 1):
|
||||
if (shape_idx < len(shapes)): # project on shape
|
||||
d, p, i = Part.Vertex(fp.Data[i]).distToShape(shapes[shape_idx])
|
||||
if d > fp.Tolerance:
|
||||
touched = True
|
||||
pts.append(p[0][1]) # shapes[shape_idx].valueAt(fp.Data[i].x))
|
||||
shape_idx += 1
|
||||
else:
|
||||
pts.append(fp.Data[i])
|
||||
if stretch and touched:
|
||||
params = [0]
|
||||
knots = [0]
|
||||
moves = [pts[0] - fp.Data[0]]
|
||||
lsum = 0
|
||||
mults = [2]
|
||||
for i in range(1, len(pts)):
|
||||
lsum += fp.Data[i - 1].distanceToPoint(fp.Data[i])
|
||||
params.append(lsum)
|
||||
if fp.DataType[i] == 1:
|
||||
knots.append(lsum)
|
||||
moves.append(pts[i] - fp.Data[i])
|
||||
mults.insert(1, 1)
|
||||
mults[-1] = 2
|
||||
if len(moves) < 2:
|
||||
return(pts)
|
||||
# FreeCAD.Console.PrintMessage("%s\n%s\n%s\n"%(moves,mults,knots))
|
||||
curve = Part.BSplineCurve()
|
||||
curve.buildFromPolesMultsKnots(moves, mults, knots, False, 1)
|
||||
for i in range(1, len(pts)):
|
||||
if fp.DataType[i] == 0:
|
||||
# FreeCAD.Console.PrintMessage("Stretch %s #%d: %s to %s\n"%(fp.Label,i,pts[i],curve.value(params[i])))
|
||||
pts[i] += curve.value(params[i])
|
||||
if touched:
|
||||
return pts
|
||||
else:
|
||||
return False
|
||||
|
||||
def execute(self, obj):
|
||||
try:
|
||||
o = FreeCADGui.ActiveDocument.getInEdit().Object
|
||||
if o == obj:
|
||||
return
|
||||
except:
|
||||
FreeCAD.Console.PrintWarning("execute is disabled during editing\n")
|
||||
pts = self.get_points(obj)
|
||||
if pts:
|
||||
if len(pts) < 2:
|
||||
FreeCAD.Console.PrintError("{} : Not enough points\n".format(obj.Label))
|
||||
return False
|
||||
else:
|
||||
obj.Data = pts
|
||||
else:
|
||||
pts = obj.Data
|
||||
|
||||
tans = [FreeCAD.Vector()] * len(pts)
|
||||
flags = [False] * len(pts)
|
||||
for i in range(len(obj.Tangents)):
|
||||
tans[i] = obj.Tangents[i]
|
||||
for i in range(len(obj.Flags)):
|
||||
flags[i] = obj.Flags[i]
|
||||
# if not (len(obj.LinearSegments) == len(pts)-1):
|
||||
# FreeCAD.Console.PrintError("%s : Points and LinearSegments mismatch\n"%obj.Label)
|
||||
if len(obj.LinearSegments) > 0:
|
||||
for i, b in enumerate(obj.LinearSegments):
|
||||
if b:
|
||||
tans[i] = pts[i + 1] - pts[i]
|
||||
tans[i + 1] = tans[i]
|
||||
flags[i] = True
|
||||
flags[i + 1] = True
|
||||
params = profile_editor.parameterization(pts, obj.Parametrization, obj.Periodic)
|
||||
|
||||
curve = Part.BSplineCurve()
|
||||
if len(pts) == 2:
|
||||
curve.buildFromPoles(pts)
|
||||
elif obj.Periodic and pts[0].distanceToPoint(pts[-1]) < 1e-7:
|
||||
curve.interpolate(Points=pts[:-1], Parameters=params, PeriodicFlag=obj.Periodic, Tolerance=obj.Tolerance, Tangents=tans[:-1], TangentFlags=flags[:-1])
|
||||
else:
|
||||
curve.interpolate(Points=pts, Parameters=params, PeriodicFlag=obj.Periodic, Tolerance=obj.Tolerance, Tangents=tans, TangentFlags=flags)
|
||||
obj.Shape = curve.toShape()
|
||||
|
||||
def onChanged(self, fp, prop):
|
||||
if prop in ("Support", "Data", "DataType", "Periodic"):
|
||||
# FreeCAD.Console.PrintMessage("%s : %s changed\n"%(fp.Label,prop))
|
||||
if (len(fp.Data) == len(fp.DataType)) and (sum(fp.DataType) == len(fp.Support)):
|
||||
new_pts = self.get_points(fp, True)
|
||||
if new_pts:
|
||||
fp.Data = new_pts
|
||||
if prop == "Parametrization":
|
||||
self.execute(fp)
|
||||
|
||||
def onDocumentRestored(self, fp):
|
||||
fp.setEditorMode("Data", 2)
|
||||
fp.setEditorMode("DataType", 2)
|
||||
|
||||
|
||||
class GordonProfileVP:
|
||||
def __init__(self, vobj):
|
||||
vobj.Proxy = self
|
||||
self.select_state = True
|
||||
self.active = False
|
||||
|
||||
def getIcon(self):
|
||||
return TOOL_ICON
|
||||
|
||||
def attach(self, vobj):
|
||||
self.Object = vobj.Object
|
||||
self.active = False
|
||||
self.select_state = vobj.Selectable
|
||||
self.ip = None
|
||||
|
||||
def setEdit(self, vobj, mode=0):
|
||||
if mode == 0 and check_pivy():
|
||||
if vobj.Selectable:
|
||||
self.select_state = True
|
||||
vobj.Selectable = False
|
||||
pts = list()
|
||||
sl = list()
|
||||
for ob, names in self.Object.Support:
|
||||
for name in names:
|
||||
sl.append((ob, (name,)))
|
||||
shape_idx = 0
|
||||
for i in range(len(self.Object.Data)):
|
||||
p = self.Object.Data[i]
|
||||
t = self.Object.DataType[i]
|
||||
if t == 0:
|
||||
pts.append(profile_editor.MarkerOnShape([p]))
|
||||
elif t == 1:
|
||||
pts.append(profile_editor.MarkerOnShape([p], sl[shape_idx]))
|
||||
shape_idx += 1
|
||||
for i in range(len(pts)): # p,t,f in zip(pts, self.Object.Tangents, self.Object.Flags):
|
||||
if i < min(len(self.Object.Flags), len(self.Object.Tangents)):
|
||||
if self.Object.Flags[i]:
|
||||
pts[i].tangent = self.Object.Tangents[i]
|
||||
self.ip = profile_editor.InterpoCurveEditor(pts, self.Object)
|
||||
self.ip.periodic = self.Object.Periodic
|
||||
self.ip.param_factor = self.Object.Parametrization
|
||||
for i in range(min(len(self.Object.LinearSegments), len(self.ip.lines))):
|
||||
self.ip.lines[i].tangent = self.Object.LinearSegments[i]
|
||||
self.ip.lines[i].updateLine()
|
||||
self.active = True
|
||||
return True
|
||||
return False
|
||||
|
||||
def unsetEdit(self, vobj, mode=0):
|
||||
if isinstance(self.ip, profile_editor.InterpoCurveEditor) and check_pivy():
|
||||
pts = list()
|
||||
typ = list()
|
||||
tans = list()
|
||||
flags = list()
|
||||
# original_links = self.Object.Support
|
||||
new_links = list()
|
||||
for p in self.ip.points:
|
||||
if isinstance(p, profile_editor.MarkerOnShape):
|
||||
pt = p.points[0]
|
||||
pts.append(FreeCAD.Vector(pt[0], pt[1], pt[2]))
|
||||
if p.sublink:
|
||||
new_links.append(p.sublink)
|
||||
typ.append(1)
|
||||
else:
|
||||
typ.append(0)
|
||||
if p.tangent:
|
||||
tans.append(p.tangent)
|
||||
flags.append(True)
|
||||
else:
|
||||
tans.append(FreeCAD.Vector())
|
||||
flags.append(False)
|
||||
self.Object.Tangents = tans
|
||||
self.Object.Flags = flags
|
||||
self.Object.LinearSegments = [li.linear for li in self.ip.lines]
|
||||
self.Object.DataType = typ
|
||||
self.Object.Data = pts
|
||||
self.Object.Support = new_links
|
||||
vobj.Selectable = self.select_state
|
||||
self.ip.quit()
|
||||
self.ip = 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 __getstate__(self):
|
||||
return {"name": self.Object.Name}
|
||||
|
||||
def __setstate__(self, state):
|
||||
self.Object = FreeCAD.ActiveDocument.getObject(state["name"])
|
||||
return None
|
||||
|
||||
|
||||
class GordonProfileCommand:
|
||||
"""Creates a editable interpolation curve"""
|
||||
|
||||
def makeFeature(self, sub, pts, typ):
|
||||
fp = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Freehand BSpline")
|
||||
GordonProfileFP(fp, sub, pts, typ)
|
||||
GordonProfileVP(fp.ViewObject)
|
||||
FreeCAD.Console.PrintMessage(__usage__)
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
FreeCADGui.SendMsgToActiveView("ViewFit")
|
||||
fp.ViewObject.Document.setEdit(fp.ViewObject)
|
||||
|
||||
def Activated(self):
|
||||
s = FreeCADGui.Selection.getSelectionEx()
|
||||
try:
|
||||
ordered = FreeCADGui.activeWorkbench().Selection
|
||||
if ordered:
|
||||
s = ordered
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
sub = list()
|
||||
pts = list()
|
||||
for obj in s:
|
||||
if obj.HasSubObjects:
|
||||
# FreeCAD.Console.PrintMessage("object has subobjects %s\n"%str(obj.SubElementNames))
|
||||
for n in obj.SubElementNames:
|
||||
sub.append((obj.Object, [n]))
|
||||
for p in obj.PickedPoints:
|
||||
pts.append(p)
|
||||
|
||||
if len(pts) == 0:
|
||||
pts = [FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(5, 0, 0), FreeCAD.Vector(10, 0, 0)]
|
||||
typ = [0, 0, 0]
|
||||
elif len(pts) == 1:
|
||||
pts.append(pts[0] + FreeCAD.Vector(5, 0, 0))
|
||||
pts.append(pts[0] + FreeCAD.Vector(10, 0, 0))
|
||||
typ = [1, 0, 0]
|
||||
else:
|
||||
typ = [1] * len(pts)
|
||||
self.makeFeature(sub, pts, typ)
|
||||
|
||||
def IsActive(self):
|
||||
if FreeCAD.ActiveDocument:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap': TOOL_ICON,
|
||||
'MenuText': __title__,
|
||||
'ToolTip': "{}<br><br><b>Usage :</b><br>{}".format(__doc__, "<br>".join(__usage__.splitlines()))}
|
||||
|
||||
|
||||
FreeCADGui.addCommand('gordon_profile', GordonProfileCommand())
|
||||
533
Utils/graphics - copia.py
Normal file
533
Utils/graphics - copia.py
Normal file
@@ -0,0 +1,533 @@
|
||||
from pivy import coin
|
||||
#from pivy.utils import getPointOnScreen
|
||||
|
||||
def getPointOnScreen(render_manager, screen_pos, normal="camera", point=None):
|
||||
"""get coordinates from pixel position"""
|
||||
|
||||
pCam = render_manager.getCamera()
|
||||
vol = pCam.getViewVolume()
|
||||
|
||||
point = point or coin.SbVec3f(0, 0, 0)
|
||||
|
||||
if normal == "camera":
|
||||
plane = vol.getPlane(10)
|
||||
normal = plane.getNormal()
|
||||
elif normal == "x":
|
||||
normal = SbVec3f(1, 0, 0)
|
||||
elif normal == "y":
|
||||
normal = SbVec3f(0, 1, 0)
|
||||
elif normal == "z":
|
||||
normal = SbVec3f(0, 0, 1)
|
||||
normal.normalize()
|
||||
x, y = screen_pos
|
||||
vp = render_manager.getViewportRegion()
|
||||
size = vp.getViewportSize()
|
||||
dX, dY = size
|
||||
|
||||
fRatio = vp.getViewportAspectRatio()
|
||||
pX = float(x) / float(vp.getViewportSizePixels()[0])
|
||||
pY = float(y) / float(vp.getViewportSizePixels()[1])
|
||||
|
||||
if (fRatio > 1.0):
|
||||
pX = (pX - 0.5 * dX) * fRatio + 0.5 * dX
|
||||
elif (fRatio < 1.0):
|
||||
pY = (pY - 0.5 * dY) / fRatio + 0.5 * dY
|
||||
|
||||
plane = coin.SbPlane(normal, point)
|
||||
line = coin.SbLine(*vol.projectPointToLine(coin.SbVec2f(pX,pY)))
|
||||
pt = plane.intersect(line)
|
||||
return(pt)
|
||||
|
||||
COLORS = {
|
||||
"black": (0., 0., 0.),
|
||||
"white": (1., 1., 1.),
|
||||
"grey": (.5, .5, .5),
|
||||
"red": (1., 0., 0.),
|
||||
"green": (0., 1., 0.),
|
||||
"blue": (0., 0., 1.),
|
||||
"yellow": (1., 1., 0.),
|
||||
"cyan": (0., 1., 1.),
|
||||
"magenta":(1., 0., 1.)
|
||||
}
|
||||
|
||||
class Object3D(coin.SoSeparator):
|
||||
std_col = "black"
|
||||
ovr_col = "red"
|
||||
sel_col = "yellow"
|
||||
non_col = "grey"
|
||||
|
||||
def __init__(self, points, dynamic=False):
|
||||
super(Object3D, self).__init__()
|
||||
self.data = coin.SoCoordinate3()
|
||||
self.color = coin.SoMaterial()
|
||||
self.set_color()
|
||||
self += [self.color, self.data]
|
||||
self.start_pos = None
|
||||
self.dynamic = dynamic
|
||||
|
||||
# callback function lists
|
||||
self.on_drag = []
|
||||
self.on_drag_release = []
|
||||
self.on_drag_start = []
|
||||
|
||||
self._delete = False
|
||||
self._tmp_points = None
|
||||
self.enabled = True
|
||||
self.points = points
|
||||
|
||||
def set_disabled(self):
|
||||
self.color.diffuseColor = COLORS[self.non_col]
|
||||
self.enabled = False
|
||||
|
||||
def set_enabled(self):
|
||||
self.color.diffuseColor = COLORS[self.std_col]
|
||||
self.enabled = True
|
||||
|
||||
def set_color(self, col=None):
|
||||
self.std_col = col or self.std_col
|
||||
self.color.diffuseColor = COLORS[self.std_col]
|
||||
|
||||
@property
|
||||
def points(self):
|
||||
return self.data.point.getValues()
|
||||
|
||||
@points.setter
|
||||
def points(self, points):
|
||||
self.data.point.setValue(0, 0, 0)
|
||||
self.data.point.setValues(0, len(points), points)
|
||||
|
||||
def set_mouse_over(self):
|
||||
if self.enabled:
|
||||
self.color.diffuseColor = COLORS[self.ovr_col]
|
||||
|
||||
def unset_mouse_over(self):
|
||||
if self.enabled:
|
||||
self.color.diffuseColor = COLORS[self.std_col]
|
||||
|
||||
def select(self):
|
||||
if self.enabled:
|
||||
self.color.diffuseColor = COLORS[self.sel_col]
|
||||
|
||||
def unselect(self):
|
||||
if self.enabled:
|
||||
self.color.diffuseColor = COLORS[self.std_col]
|
||||
|
||||
def drag(self, mouse_coords, fact=1.):
|
||||
if self.enabled:
|
||||
pts = self.points
|
||||
for i, pt in enumerate(pts):
|
||||
pt[0] = mouse_coords[0] * fact + self._tmp_points[i][0]
|
||||
pt[1] = mouse_coords[1] * fact + self._tmp_points[i][1]
|
||||
pt[2] = mouse_coords[2] * fact + self._tmp_points[i][2]
|
||||
self.points = pts
|
||||
for foo in self.on_drag:
|
||||
foo()
|
||||
|
||||
def drag_release(self):
|
||||
if self.enabled:
|
||||
for foo in self.on_drag_release:
|
||||
foo()
|
||||
|
||||
def drag_start(self):
|
||||
self._tmp_points = self.points
|
||||
if self.enabled:
|
||||
for foo in self.on_drag_start:
|
||||
foo()
|
||||
|
||||
@property
|
||||
def drag_objects(self):
|
||||
if self.enabled:
|
||||
return [self]
|
||||
|
||||
def delete(self):
|
||||
if self.enabled and not self._delete:
|
||||
self._delete = True
|
||||
|
||||
def check_dependency(self):
|
||||
pass
|
||||
|
||||
|
||||
class Marker(Object3D):
|
||||
def __init__(self, points, dynamic=False):
|
||||
super(Marker, self).__init__(points, dynamic)
|
||||
self.marker = coin.SoMarkerSet()
|
||||
self.marker.markerIndex = coin.SoMarkerSet.CIRCLE_FILLED_9_9
|
||||
self.addChild(self.marker)
|
||||
|
||||
|
||||
class Line(Object3D):
|
||||
def __init__(self, points, dynamic=False):
|
||||
super(Line, self).__init__(points, dynamic)
|
||||
self.drawstyle = coin.SoDrawStyle()
|
||||
self.line = coin.SoLineSet()
|
||||
self.addChild(self.drawstyle)
|
||||
self.addChild(self.line)
|
||||
|
||||
class Point(Object3D):
|
||||
def __init__(self, points, dynamic=False):
|
||||
super(Point, self).__init__(points, dynamic)
|
||||
self.drawstyle = coin.SoDrawStyle()
|
||||
self.point = coin.SoPointSet()
|
||||
self.addChild(self.drawstyle)
|
||||
self.addChild(self.point)
|
||||
|
||||
class Polygon(Object3D):
|
||||
def __init__(self, points, dynamic=False):
|
||||
super(Polygon, self).__init__(points, dynamic)
|
||||
self.polygon = coin.SoFaceSet()
|
||||
self.addChild(self.polygon)
|
||||
|
||||
class Arrow(Line):
|
||||
def __init__(self, points, dynamic=False, arrow_size=0.04, length=2):
|
||||
super(Arrow, self).__init__(points, dynamic)
|
||||
self.arrow_sep = coin.SoSeparator()
|
||||
self.arrow_rot = coin.SoRotation()
|
||||
self.arrow_scale = coin.SoScale()
|
||||
self.arrow_translate = coin.SoTranslation()
|
||||
self.arrow_scale.scaleFactor.setValue(arrow_size, arrow_size, arrow_size)
|
||||
self.cone = coin.SoCone()
|
||||
arrow_length = coin.SoScale()
|
||||
arrow_length.scaleFactor = (1, length, 1)
|
||||
arrow_origin = coin.SoTranslation()
|
||||
arrow_origin.translation = (0, -1, 0)
|
||||
self.arrow_sep += [self.arrow_translate, self.arrow_rot, self.arrow_scale]
|
||||
self.arrow_sep += [arrow_length, arrow_origin, self.cone]
|
||||
self += [self.arrow_sep]
|
||||
self.set_arrow_direction()
|
||||
|
||||
def set_arrow_direction(self):
|
||||
pts = np.array(self.points)
|
||||
self.arrow_translate.translation = tuple(pts[-1])
|
||||
direction = pts[-1] - pts[-2]
|
||||
direction /= np.linalg.norm(direction)
|
||||
_rot = coin.SbRotation()
|
||||
_rot.setValue(coin.SbVec3f(0, 1, 0), coin.SbVec3f(*direction))
|
||||
self.arrow_rot.rotation.setValue(_rot)
|
||||
|
||||
class InteractionSeparator(coin.SoSeparator):
|
||||
pick_radius = 20
|
||||
ctrl_keys = {"grab": "g",
|
||||
"abort_grab": u"\uff1b",
|
||||
"select_all": "a",
|
||||
"delete": u"\uffff",
|
||||
"axis_x": "x",
|
||||
"axis_y": "y",
|
||||
"axis_z": "z"}
|
||||
|
||||
def __init__(self, render_manager):
|
||||
super(InteractionSeparator, self).__init__()
|
||||
self.render_manager = render_manager
|
||||
self.objects = coin.SoSeparator()
|
||||
self.dynamic_objects = []
|
||||
self.static_objects = []
|
||||
self.over_object = None
|
||||
self.selected_objects = []
|
||||
self.drag_objects = []
|
||||
|
||||
self.on_drag = []
|
||||
self.on_drag_release = []
|
||||
self.on_drag_start = []
|
||||
|
||||
self._direction = None
|
||||
|
||||
self.events = coin.SoEventCallback()
|
||||
self += self.events, self.objects
|
||||
|
||||
def register(self):
|
||||
self._highlightCB = self.events.addEventCallback(
|
||||
coin.SoLocation2Event.getClassTypeId(), self.highlightCB)
|
||||
self._selectCB = self.events.addEventCallback(
|
||||
coin.SoMouseButtonEvent.getClassTypeId(), self.selectCB)
|
||||
self._grabCB = self.events.addEventCallback(
|
||||
coin.SoMouseButtonEvent.getClassTypeId(), self.grabCB)
|
||||
self._deleteCB = self.events.addEventCallback(
|
||||
coin.SoKeyboardEvent.getClassTypeId(), self.deleteCB)
|
||||
self._selectAllCB = self.events.addEventCallback(
|
||||
coin.SoKeyboardEvent.getClassTypeId(), self.selectAllCB)
|
||||
|
||||
def unregister(self):
|
||||
self.events.removeEventCallback(
|
||||
coin.SoLocation2Event.getClassTypeId(), self._highlightCB)
|
||||
self.events.removeEventCallback(
|
||||
coin.SoMouseButtonEvent.getClassTypeId(), self._selectCB)
|
||||
self.events.removeEventCallback(
|
||||
coin.SoMouseButtonEvent.getClassTypeId(), self._grabCB)
|
||||
self.events.removeEventCallback(
|
||||
coin.SoKeyboardEvent.getClassTypeId(), self._deleteCB)
|
||||
self.events.removeEventCallback(
|
||||
coin.SoKeyboardEvent.getClassTypeId(), self._selectAllCB)
|
||||
|
||||
|
||||
def addChild(self, child):
|
||||
if hasattr(child, "dynamic"):
|
||||
self.objects.addChild(child)
|
||||
if child.dynamic:
|
||||
self.dynamic_objects.append(child)
|
||||
else:
|
||||
self.static_objects.append(child)
|
||||
else:
|
||||
super(InteractionSeparator, self).addChild(child)
|
||||
|
||||
#-----------------------HIGHLIGHTING-----------------------#
|
||||
# a SoLocation2Event calling a function which sends rays #
|
||||
# int the scene. This will return the object the mouse is #
|
||||
# currently hoovering. #
|
||||
|
||||
def highlightObject(self, obj):
|
||||
if self.over_object:
|
||||
self.over_object.unset_mouse_over()
|
||||
self.over_object = obj
|
||||
if self.over_object:
|
||||
self.over_object.set_mouse_over()
|
||||
self.colorSelected()
|
||||
|
||||
def highlightCB(self, attr, event_callback):
|
||||
event = event_callback.getEvent()
|
||||
pos = event.getPosition()
|
||||
obj = self.sendRay(pos)
|
||||
self.highlightObject(obj)
|
||||
|
||||
def sendRay(self, mouse_pos):
|
||||
"""sends a ray through the scene and return the nearest entity"""
|
||||
ray_pick = coin.SoRayPickAction(self.render_manager.getViewportRegion())
|
||||
ray_pick.setPoint(coin.SbVec2s(*mouse_pos))
|
||||
ray_pick.setRadius(InteractionSeparator.pick_radius)
|
||||
ray_pick.setPickAll(True)
|
||||
ray_pick.apply(self.render_manager.getSceneGraph())
|
||||
picked_point = ray_pick.getPickedPointList()
|
||||
return self.objByID(picked_point)
|
||||
|
||||
def objByID(self, picked_point):
|
||||
for point in picked_point:
|
||||
path = point.getPath()
|
||||
length = path.getLength()
|
||||
point = path.getNode(length - 2)
|
||||
for o in self.dynamic_objects:
|
||||
if point == o:
|
||||
return(o)
|
||||
# Code below was not working with python 2.7 (pb with getNodeId ?)
|
||||
#point = list(filter(
|
||||
#lambda ctrl: ctrl.getNodeId() == point.getNodeId(),
|
||||
#self.dynamic_objects))
|
||||
#if point != []:
|
||||
#return point[0]
|
||||
return None
|
||||
|
||||
|
||||
|
||||
#------------------------SELECTION------------------------#
|
||||
def selectObject(self, obj, multi=False):
|
||||
if not multi:
|
||||
for o in self.selected_objects:
|
||||
o.unselect()
|
||||
self.selected_objects = []
|
||||
if obj:
|
||||
if obj in self.selected_objects:
|
||||
self.selected_objects.remove(obj)
|
||||
else:
|
||||
self.selected_objects.append(obj)
|
||||
self.colorSelected()
|
||||
self.selectionChanged()
|
||||
|
||||
def selectCB(self, attr, event_callback):
|
||||
event = event_callback.getEvent()
|
||||
if (event.getState() == coin.SoMouseButtonEvent.DOWN and
|
||||
event.getButton() == event.BUTTON1):
|
||||
pos = event.getPosition()
|
||||
obj = self.sendRay(pos)
|
||||
self.selectObject(obj, event.wasCtrlDown())
|
||||
|
||||
def select_all_cb(self, event_callback):
|
||||
event = event_callback.getEvent()
|
||||
if (event.getKey() == ord(InteractionSeparator.ctrl_keys["select_all"])):
|
||||
if event.getState() == event.DOWN:
|
||||
if self.selected_objects:
|
||||
for o in self.selected_objects:
|
||||
o.unselect()
|
||||
self.selected_objects = []
|
||||
else:
|
||||
for obj in self.objects:
|
||||
if obj.dynamic:
|
||||
self.selected_objects.append(obj)
|
||||
self.ColorSelected()
|
||||
self.selection_changed()
|
||||
|
||||
def deselect_all(self):
|
||||
if self.selected_objects:
|
||||
for o in self.selected_objects:
|
||||
o.unselect()
|
||||
self.selected_objects = []
|
||||
|
||||
def colorSelected(self):
|
||||
for obj in self.selected_objects:
|
||||
obj.select()
|
||||
|
||||
def selectionChanged(self):
|
||||
pass
|
||||
|
||||
def selectAllCB(self, attr, event_callback):
|
||||
event = event_callback.getEvent()
|
||||
if (event.getKey() == ord(InteractionSeparator.ctrl_keys["select_all"])):
|
||||
if event.getState() == event.DOWN:
|
||||
if self.selected_objects:
|
||||
for o in self.selected_objects:
|
||||
o.unselect()
|
||||
self.selected_objects = []
|
||||
else:
|
||||
for obj in self.dynamic_objects:
|
||||
if obj.dynamic:
|
||||
self.selected_objects.append(obj)
|
||||
self.colorSelected()
|
||||
self.selectionChanged()
|
||||
|
||||
|
||||
#------------------------INTERACTION------------------------#
|
||||
|
||||
def cursor_pos(self, event):
|
||||
pos = event.getPosition()
|
||||
# print(list(getPointOnScreen1(self.render_manager, pos)))
|
||||
return getPointOnScreen(self.render_manager, pos)
|
||||
|
||||
|
||||
def constrained_vector(self, vector):
|
||||
if self._direction is None:
|
||||
return vector
|
||||
if self._direction == InteractionSeparator.ctrl_keys["axis_x"]:
|
||||
return [vector[0], 0, 0]
|
||||
elif self._direction == InteractionSeparator.ctrl_keys["axis_y"]:
|
||||
return [0, vector[1], 0]
|
||||
elif self._direction == InteractionSeparator.ctrl_keys["axis_z"]:
|
||||
return [0, 0, vector[2]]
|
||||
|
||||
def grabCB(self, attr, event_callback):
|
||||
# press grab key to move an entity
|
||||
event = event_callback.getEvent()
|
||||
# get all drag objects, every selected object can add some drag objects
|
||||
# but the eventhandler is not allowed to call the drag twice on an object
|
||||
#if event.getKey() == ord(InteractionSeparator.ctrl_keys["grab"]):
|
||||
if (event.getState() == coin.SoMouseButtonEvent.DOWN and
|
||||
event.getButton() == event.BUTTON1):
|
||||
pos = event.getPosition()
|
||||
obj = self.sendRay(pos)
|
||||
if obj:
|
||||
#if not obj in self.selected_objects:
|
||||
#self.selectObject(obj, event.wasCtrlDown())
|
||||
self.drag_objects = set()
|
||||
for i in self.selected_objects:
|
||||
for j in i.drag_objects:
|
||||
self.drag_objects.add(j)
|
||||
# check if something is selected
|
||||
if self.drag_objects:
|
||||
# first delete the selection_cb, and higlight_cb
|
||||
self.unregister()
|
||||
# now add a callback that calls the dragfunction of the selected entities
|
||||
self.start_pos = self.cursor_pos(event)
|
||||
self._dragCB = self.events.addEventCallback(
|
||||
coin.SoEvent.getClassTypeId(), self.dragCB)
|
||||
for obj in self.drag_objects:
|
||||
obj.drag_start()
|
||||
for foo in self.on_drag_start:
|
||||
foo()
|
||||
|
||||
def dragCB(self, attr, event_callback, force=False):
|
||||
event = event_callback.getEvent()
|
||||
b = ""
|
||||
s = ""
|
||||
if type(event) == coin.SoMouseButtonEvent:
|
||||
if event.getButton() == coin.SoMouseButtonEvent.BUTTON1:
|
||||
b = "mb1"
|
||||
elif event.getButton() == coin.SoMouseButtonEvent.BUTTON2:
|
||||
b = "mb2"
|
||||
if event.getState() == coin.SoMouseButtonEvent.UP:
|
||||
s = "up"
|
||||
elif event.getState() == coin.SoMouseButtonEvent.DOWN:
|
||||
s = "down"
|
||||
import FreeCAD
|
||||
FreeCAD.Console.PrintMessage("{} {}\n".format(b,s))
|
||||
if ((type(event) == coin.SoMouseButtonEvent and
|
||||
event.getState() == coin.SoMouseButtonEvent.UP
|
||||
and event.getButton() == coin.SoMouseButtonEvent.BUTTON1) or
|
||||
force):
|
||||
self.register()
|
||||
if self._dragCB:
|
||||
self.events.removeEventCallback(
|
||||
coin.SoEvent.getClassTypeId(), self._dragCB)
|
||||
self._direction = None
|
||||
self._dragCB = None
|
||||
self.start_pos = None
|
||||
for obj in self.drag_objects:
|
||||
obj.drag_release()
|
||||
for foo in self.on_drag_release:
|
||||
foo()
|
||||
self.drag_objects = []
|
||||
elif (type(event) == coin.SoKeyboardEvent and
|
||||
event.getState() == coin.SoMouseButtonEvent.DOWN):
|
||||
if event.getKey() == InteractionSeparator.ctrl_keys["abort_grab"]: # esc
|
||||
for obj in self.drag_objects:
|
||||
obj.drag([0, 0, 0], 1) # set back to zero
|
||||
self.dragCB(attr, event_callback, force=True)
|
||||
return
|
||||
try:
|
||||
key = chr(event.getKey())
|
||||
except ValueError:
|
||||
# there is no character for this value
|
||||
key = "_"
|
||||
if key in [InteractionSeparator.ctrl_keys["axis_x"],
|
||||
InteractionSeparator.ctrl_keys["axis_y"],
|
||||
InteractionSeparator.ctrl_keys["axis_z"]] and key != self._direction:
|
||||
self._direction = key
|
||||
else:
|
||||
self._direction = None
|
||||
diff = self.cursor_pos(event) - self.start_pos
|
||||
diff = self.constrained_vector(diff)
|
||||
for obj in self.drag_objects:
|
||||
obj.drag(diff, 1)
|
||||
for foo in self.on_drag:
|
||||
foo()
|
||||
|
||||
elif type(event) == coin.SoLocation2Event:
|
||||
fact = 0.1 if event.wasShiftDown() else 1.
|
||||
diff = self.cursor_pos(event) - self.start_pos
|
||||
diff = self.constrained_vector(diff)
|
||||
for obj in self.drag_objects:
|
||||
obj.drag(diff, fact)
|
||||
for foo in self.on_drag:
|
||||
foo()
|
||||
|
||||
def deleteCB(self, attr, event_callback):
|
||||
event = event_callback.getEvent()
|
||||
# get all drag objects, every selected object can add some drag objects
|
||||
# but the eventhandler is not allowed to call the drag twice on an object
|
||||
if event.getKey() == ord(InteractionSeparator.ctrl_keys["delete"]) and (event.getState() == 1):
|
||||
self.removeSelected()
|
||||
|
||||
def removeSelected(self):
|
||||
temp = []
|
||||
for i in self.selected_objects:
|
||||
i.delete()
|
||||
for i in self.dynamic_objects + self.static_objects:
|
||||
i.check_dependency() #dependency length max = 1
|
||||
for i in self.dynamic_objects + self.static_objects:
|
||||
if i._delete:
|
||||
temp.append(i)
|
||||
self.selected_objects = []
|
||||
self.over_object = None
|
||||
self.selectionChanged()
|
||||
for i in temp:
|
||||
if i in self.dynamic_objects:
|
||||
self.dynamic_objects.remove(i)
|
||||
else:
|
||||
self.static_objects.remove(i)
|
||||
self.objects.removeChild(i)
|
||||
del(i)
|
||||
self.selectionChanged()
|
||||
|
||||
def removeAllChildren(self):
|
||||
for i in self.dynamic_objects:
|
||||
i.delete()
|
||||
self.dynamic_objects = []
|
||||
self.static_objects = []
|
||||
self.selected_objects = []
|
||||
self.over_object = None
|
||||
super(InteractionSeparator, self).removeAllChildren()
|
||||
|
||||
553
Utils/graphics.py
Normal file
553
Utils/graphics.py
Normal file
@@ -0,0 +1,553 @@
|
||||
# from curve workbench
|
||||
|
||||
from pivy import coin
|
||||
|
||||
|
||||
# from pivy.utils import getPointOnScreen
|
||||
|
||||
def getPointOnScreen(render_manager, screen_pos, normal="camera", point=None):
|
||||
"""get coordinates from pixel position"""
|
||||
|
||||
pCam = render_manager.getCamera()
|
||||
vol = pCam.getViewVolume()
|
||||
|
||||
point = point or coin.SbVec3f(0, 0, 0)
|
||||
|
||||
if normal == "camera":
|
||||
plane = vol.getPlane(10)
|
||||
normal = plane.getNormal()
|
||||
elif normal == "x":
|
||||
normal = SbVec3f(1, 0, 0)
|
||||
elif normal == "y":
|
||||
normal = SbVec3f(0, 1, 0)
|
||||
elif normal == "z":
|
||||
normal = SbVec3f(0, 0, 1)
|
||||
normal.normalize()
|
||||
x, y = screen_pos
|
||||
vp = render_manager.getViewportRegion()
|
||||
size = vp.getViewportSize()
|
||||
dX, dY = size
|
||||
|
||||
fRatio = vp.getViewportAspectRatio()
|
||||
pX = float(x) / float(vp.getViewportSizePixels()[0])
|
||||
pY = float(y) / float(vp.getViewportSizePixels()[1])
|
||||
|
||||
if fRatio > 1.0:
|
||||
pX = (pX - 0.5 * dX) * fRatio + 0.5 * dX
|
||||
elif fRatio < 1.0:
|
||||
pY = (pY - 0.5 * dY) / fRatio + 0.5 * dY
|
||||
|
||||
plane = coin.SbPlane(normal, point)
|
||||
line = coin.SbLine(*vol.projectPointToLine(coin.SbVec2f(pX, pY)))
|
||||
pt = plane.intersect(line)
|
||||
return pt
|
||||
|
||||
|
||||
COLORS = {
|
||||
"black": (0., 0., 0.),
|
||||
"white": (1., 1., 1.),
|
||||
"grey": (.5, .5, .5),
|
||||
"red": (1., 0., 0.),
|
||||
"green": (0., 1., 0.),
|
||||
"blue": (0., 0., 1.),
|
||||
"yellow": (1., 1., 0.),
|
||||
"cyan": (0., 1., 1.),
|
||||
"magenta": (1., 0., 1.)
|
||||
}
|
||||
|
||||
|
||||
class Object3D(coin.SoSeparator):
|
||||
std_col = "black"
|
||||
ovr_col = "red"
|
||||
sel_col = "yellow"
|
||||
non_col = "grey"
|
||||
|
||||
def __init__(self, points, dynamic=False):
|
||||
super(Object3D, self).__init__()
|
||||
self.data = coin.SoCoordinate3()
|
||||
self.color = coin.SoMaterial()
|
||||
self.set_color()
|
||||
self += [self.color, self.data]
|
||||
self.start_pos = None
|
||||
self.dynamic = dynamic
|
||||
|
||||
# callback function lists
|
||||
self.on_drag = []
|
||||
self.on_drag_release = []
|
||||
self.on_drag_start = []
|
||||
|
||||
self._delete = False
|
||||
self._tmp_points = None
|
||||
self.enabled = True
|
||||
self.points = points
|
||||
|
||||
def set_disabled(self):
|
||||
self.color.diffuseColor = COLORS[self.non_col]
|
||||
self.enabled = False
|
||||
|
||||
def set_enabled(self):
|
||||
self.color.diffuseColor = COLORS[self.std_col]
|
||||
self.enabled = True
|
||||
|
||||
def set_color(self, col=None):
|
||||
self.std_col = col or self.std_col
|
||||
self.color.diffuseColor = COLORS[self.std_col]
|
||||
|
||||
@property
|
||||
def points(self):
|
||||
return self.data.point.getValues()
|
||||
|
||||
@points.setter
|
||||
def points(self, points):
|
||||
self.data.point.setValue(0, 0, 0)
|
||||
self.data.point.setValues(0, len(points), points)
|
||||
|
||||
def set_mouse_over(self):
|
||||
if self.enabled:
|
||||
self.color.diffuseColor = COLORS[self.ovr_col]
|
||||
|
||||
def unset_mouse_over(self):
|
||||
if self.enabled:
|
||||
self.color.diffuseColor = COLORS[self.std_col]
|
||||
|
||||
def select(self):
|
||||
if self.enabled:
|
||||
self.color.diffuseColor = COLORS[self.sel_col]
|
||||
|
||||
def unselect(self):
|
||||
if self.enabled:
|
||||
self.color.diffuseColor = COLORS[self.std_col]
|
||||
|
||||
def drag(self, mouse_coords, fact=1.):
|
||||
if self.enabled:
|
||||
pts = self.points
|
||||
for i, pt in enumerate(pts):
|
||||
pt[0] = mouse_coords[0] * fact + self._tmp_points[i][0]
|
||||
pt[1] = mouse_coords[1] * fact + self._tmp_points[i][1]
|
||||
pt[2] = mouse_coords[2] * fact + self._tmp_points[i][2]
|
||||
self.points = pts
|
||||
for foo in self.on_drag:
|
||||
foo()
|
||||
|
||||
def drag_release(self):
|
||||
if self.enabled:
|
||||
for foo in self.on_drag_release:
|
||||
foo()
|
||||
|
||||
def drag_start(self):
|
||||
self._tmp_points = self.points
|
||||
if self.enabled:
|
||||
for foo in self.on_drag_start:
|
||||
foo()
|
||||
|
||||
@property
|
||||
def drag_objects(self):
|
||||
if self.enabled:
|
||||
return [self]
|
||||
|
||||
def delete(self):
|
||||
if self.enabled and not self._delete:
|
||||
self._delete = True
|
||||
|
||||
def check_dependency(self):
|
||||
pass
|
||||
|
||||
|
||||
class Marker(Object3D):
|
||||
def __init__(self, points, dynamic=False):
|
||||
super(Marker, self).__init__(points, dynamic)
|
||||
self.marker = coin.SoMarkerSet()
|
||||
self.marker.markerIndex = coin.SoMarkerSet.CIRCLE_FILLED_9_9
|
||||
self.addChild(self.marker)
|
||||
|
||||
class Line(Object3D):
|
||||
def __init__(self, points, dynamic=False):
|
||||
super(Line, self).__init__(points, dynamic)
|
||||
self.drawstyle = coin.SoDrawStyle()
|
||||
self.line = coin.SoLineSet()
|
||||
self.addChild(self.drawstyle)
|
||||
self.addChild(self.line)
|
||||
|
||||
class Arc(Object3D):
|
||||
def __init__(self, points, dynamic=False):
|
||||
super(Line, self).__init__(points, dynamic)
|
||||
self.drawstyle = coin.SoDrawStyle()
|
||||
self.line = coin.SoLineSet()
|
||||
self.addChild(self.drawstyle)
|
||||
self.addChild(self.line)
|
||||
|
||||
class Point(Object3D):
|
||||
def __init__(self, points, dynamic=False):
|
||||
super(Point, self).__init__(points, dynamic)
|
||||
self.drawstyle = coin.SoDrawStyle()
|
||||
self.point = coin.SoPointSet()
|
||||
self.addChild(self.drawstyle)
|
||||
self.addChild(self.point)
|
||||
|
||||
class Polygon(Object3D):
|
||||
def __init__(self, points, dynamic=False):
|
||||
super(Polygon, self).__init__(points, dynamic)
|
||||
self.polygon = coin.SoFaceSet()
|
||||
self.addChild(self.polygon)
|
||||
|
||||
class Arrow(Line):
|
||||
def __init__(self, points, dynamic=False, arrow_size=0.04, length=2):
|
||||
super(Arrow, self).__init__(points, dynamic)
|
||||
self.arrow_sep = coin.SoSeparator()
|
||||
self.arrow_rot = coin.SoRotation()
|
||||
self.arrow_scale = coin.SoScale()
|
||||
self.arrow_translate = coin.SoTranslation()
|
||||
self.arrow_scale.scaleFactor.setValue(arrow_size, arrow_size, arrow_size)
|
||||
self.cone = coin.SoCone()
|
||||
arrow_length = coin.SoScale()
|
||||
arrow_length.scaleFactor = (1, length, 1)
|
||||
arrow_origin = coin.SoTranslation()
|
||||
arrow_origin.translation = (0, -1, 0)
|
||||
self.arrow_sep += [self.arrow_translate, self.arrow_rot, self.arrow_scale]
|
||||
self.arrow_sep += [arrow_length, arrow_origin, self.cone]
|
||||
self += [self.arrow_sep]
|
||||
self.set_arrow_direction()
|
||||
|
||||
def set_arrow_direction(self):
|
||||
pts = np.array(self.points)
|
||||
self.arrow_translate.translation = tuple(pts[-1])
|
||||
direction = pts[-1] - pts[-2]
|
||||
direction /= np.linalg.norm(direction)
|
||||
_rot = coin.SbRotation()
|
||||
_rot.setValue(coin.SbVec3f(0, 1, 0), coin.SbVec3f(*direction))
|
||||
self.arrow_rot.rotation.setValue(_rot)
|
||||
|
||||
|
||||
class InteractionSeparator(coin.SoSeparator):
|
||||
pick_radius = 20
|
||||
ctrl_keys = {"grab": "g",
|
||||
"abort_grab": u"\uff1b",
|
||||
"select_all": "a",
|
||||
"delete": u"\uffff",
|
||||
"axis_x": "x",
|
||||
"axis_y": "y",
|
||||
"axis_z": "z"}
|
||||
|
||||
def __init__(self, render_manager):
|
||||
super(InteractionSeparator, self).__init__()
|
||||
self.render_manager = render_manager
|
||||
self.objects = coin.SoSeparator()
|
||||
self.dynamic_objects = []
|
||||
self.static_objects = []
|
||||
self.over_object = None
|
||||
self.selected_objects = []
|
||||
self.drag_objects = []
|
||||
|
||||
self.on_drag = []
|
||||
self.on_drag_release = []
|
||||
self.on_drag_start = []
|
||||
|
||||
self._direction = None
|
||||
|
||||
self.events = coin.SoEventCallback()
|
||||
self += self.events, self.objects
|
||||
|
||||
def register(self):
|
||||
self._highlightCB = self.events.addEventCallback(coin.SoLocation2Event.getClassTypeId(), self.highlightCB)
|
||||
self._selectCB = self.events.addEventCallback(coin.SoMouseButtonEvent.getClassTypeId(), self.selectCB)
|
||||
self._grabCB = self.events.addEventCallback(coin.SoMouseButtonEvent.getClassTypeId(), self.grabCB)
|
||||
self._deleteCB = self.events.addEventCallback(coin.SoKeyboardEvent.getClassTypeId(), self.deleteCB)
|
||||
self._selectAllCB = self.events.addEventCallback(coin.SoKeyboardEvent.getClassTypeId(), self.selectAllCB)
|
||||
|
||||
def unregister(self):
|
||||
self.events.removeEventCallback(coin.SoLocation2Event.getClassTypeId(), self._highlightCB)
|
||||
self.events.removeEventCallback(coin.SoMouseButtonEvent.getClassTypeId(), self._selectCB)
|
||||
self.events.removeEventCallback(coin.SoMouseButtonEvent.getClassTypeId(), self._grabCB)
|
||||
self.events.removeEventCallback(coin.SoKeyboardEvent.getClassTypeId(), self._deleteCB)
|
||||
self.events.removeEventCallback(coin.SoKeyboardEvent.getClassTypeId(), self._selectAllCB)
|
||||
|
||||
def addChild(self, child):
|
||||
if hasattr(child, "dynamic"):
|
||||
self.objects.addChild(child)
|
||||
if child.dynamic:
|
||||
self.dynamic_objects.append(child)
|
||||
else:
|
||||
self.static_objects.append(child)
|
||||
else:
|
||||
super(InteractionSeparator, self).addChild(child)
|
||||
|
||||
# -----------------------HIGHLIGHTING-----------------------#
|
||||
|
||||
# a SoLocation2Event calling a function which sends rays #
|
||||
# int the scene. This will return the object the mouse is #
|
||||
# currently hoovering. #
|
||||
|
||||
def highlightObject(self, obj):
|
||||
if self.over_object:
|
||||
self.over_object.unset_mouse_over()
|
||||
self.over_object = obj
|
||||
if self.over_object:
|
||||
self.over_object.set_mouse_over()
|
||||
self.colorSelected()
|
||||
|
||||
def highlightCB(self, attr, event_callback):
|
||||
event = event_callback.getEvent()
|
||||
pos = event.getPosition()
|
||||
obj = self.sendRay(pos)
|
||||
self.highlightObject(obj)
|
||||
|
||||
def sendRay(self, mouse_pos):
|
||||
""" sends a ray through the scene and return the nearest entity """
|
||||
|
||||
ray_pick = coin.SoRayPickAction(self.render_manager.getViewportRegion())
|
||||
ray_pick.setPoint(coin.SbVec2s(*mouse_pos))
|
||||
ray_pick.setRadius(InteractionSeparator.pick_radius)
|
||||
ray_pick.setPickAll(True)
|
||||
ray_pick.apply(self.render_manager.getSceneGraph())
|
||||
picked_point = ray_pick.getPickedPointList()
|
||||
return self.objByID(picked_point)
|
||||
|
||||
def objByID(self, picked_point):
|
||||
for point in picked_point:
|
||||
path = point.getPath()
|
||||
length = path.getLength()
|
||||
point = path.getNode(length - 2)
|
||||
for o in self.dynamic_objects:
|
||||
if point == o:
|
||||
return o
|
||||
return None
|
||||
|
||||
# ------------------------SELECTION------------------------#
|
||||
|
||||
def selectObject(self, obj, multi=False):
|
||||
if not multi:
|
||||
for o in self.selected_objects:
|
||||
o.unselect()
|
||||
self.selected_objects = []
|
||||
if obj:
|
||||
if obj in self.selected_objects:
|
||||
self.selected_objects.remove(obj)
|
||||
else:
|
||||
self.selected_objects.append(obj)
|
||||
self.colorSelected()
|
||||
self.selectionChanged()
|
||||
|
||||
def selectCB(self, attr, event_callback):
|
||||
event = event_callback.getEvent()
|
||||
if (event.getState() == coin.SoMouseButtonEvent.DOWN and
|
||||
event.getButton() == event.BUTTON1):
|
||||
pos = event.getPosition()
|
||||
obj = self.sendRay(pos)
|
||||
self.selectObject(obj, event.wasCtrlDown())
|
||||
|
||||
def select_all_cb(self, event_callback):
|
||||
event = event_callback.getEvent()
|
||||
if event.getKey() == ord(InteractionSeparator.ctrl_keys["select_all"]):
|
||||
if event.getState() == event.DOWN:
|
||||
if self.selected_objects:
|
||||
for o in self.selected_objects:
|
||||
o.unselect()
|
||||
self.selected_objects = []
|
||||
else:
|
||||
for obj in self.objects:
|
||||
if obj.dynamic:
|
||||
self.selected_objects.append(obj)
|
||||
self.ColorSelected()
|
||||
self.selection_changed()
|
||||
|
||||
def deselect_all(self):
|
||||
if self.selected_objects:
|
||||
for o in self.selected_objects:
|
||||
o.unselect()
|
||||
self.selected_objects = []
|
||||
|
||||
def colorSelected(self):
|
||||
for obj in self.selected_objects:
|
||||
obj.select()
|
||||
|
||||
def selectionChanged(self):
|
||||
pass
|
||||
|
||||
def selectAllCB(self, attr, event_callback):
|
||||
event = event_callback.getEvent()
|
||||
if event.getKey() == ord(InteractionSeparator.ctrl_keys["select_all"]):
|
||||
if event.getState() == event.DOWN:
|
||||
if self.selected_objects:
|
||||
for o in self.selected_objects:
|
||||
o.unselect()
|
||||
self.selected_objects = []
|
||||
else:
|
||||
for obj in self.dynamic_objects:
|
||||
if obj.dynamic:
|
||||
self.selected_objects.append(obj)
|
||||
self.colorSelected()
|
||||
self.selectionChanged()
|
||||
|
||||
# ------------------------INTERACTION------------------------#
|
||||
|
||||
def cursor_pos(self, event):
|
||||
pos = event.getPosition()
|
||||
# print(list(getPointOnScreen1(self.render_manager, pos)))
|
||||
return getPointOnScreen(self.render_manager, pos)
|
||||
|
||||
def constrained_vector(self, vector):
|
||||
if self._direction is None:
|
||||
return vector
|
||||
|
||||
if self._direction == InteractionSeparator.ctrl_keys["axis_x"]:
|
||||
return [vector[0], 0, 0]
|
||||
elif self._direction == InteractionSeparator.ctrl_keys["axis_y"]:
|
||||
return [0, vector[1], 0]
|
||||
elif self._direction == InteractionSeparator.ctrl_keys["axis_z"]:
|
||||
return [0, 0, vector[2]]
|
||||
|
||||
def grabCB(self, attr, event_callback):
|
||||
# press grab key to move an entity
|
||||
|
||||
event = event_callback.getEvent()
|
||||
# get all drag objects, every selected object can add some drag objects
|
||||
# but the eventhandler is not allowed to call the drag twice on an object
|
||||
# if event.getKey() == ord(InteractionSeparator.ctrl_keys["grab"]):
|
||||
if (event.getState() == coin.SoMouseButtonEvent.DOWN and
|
||||
event.getButton() == event.BUTTON1):
|
||||
pos = event.getPosition()
|
||||
obj = self.sendRay(pos)
|
||||
if obj:
|
||||
# if not obj in self.selected_objects:
|
||||
# self.selectObject(obj, event.wasCtrlDown())
|
||||
self.drag_objects = set()
|
||||
for i in self.selected_objects:
|
||||
for j in i.drag_objects:
|
||||
self.drag_objects.add(j)
|
||||
# check if something is selected
|
||||
if self.drag_objects:
|
||||
# first delete the selection_cb, and higlight_cb
|
||||
self.unregister()
|
||||
# now add a callback that calls the dragfunction of the selected entities
|
||||
self.start_pos = self.cursor_pos(event)
|
||||
self._dragCB = self.events.addEventCallback(
|
||||
coin.SoEvent.getClassTypeId(), self.dragCB)
|
||||
for obj in self.drag_objects:
|
||||
obj.drag_start()
|
||||
for foo in self.on_drag_start:
|
||||
foo()
|
||||
|
||||
def dragCB(self, attr, event_callback, force=False):
|
||||
event = event_callback.getEvent()
|
||||
b = ""
|
||||
s = ""
|
||||
if type(event) == coin.SoMouseButtonEvent:
|
||||
if event.getButton() == coin.SoMouseButtonEvent.BUTTON1:
|
||||
b = "mb1"
|
||||
elif event.getButton() == coin.SoMouseButtonEvent.BUTTON2:
|
||||
b = "mb2"
|
||||
if event.getState() == coin.SoMouseButtonEvent.UP:
|
||||
s = "up"
|
||||
elif event.getState() == coin.SoMouseButtonEvent.DOWN:
|
||||
s = "down"
|
||||
import FreeCAD
|
||||
FreeCAD.Console.PrintMessage("{} {}\n".format(b, s))
|
||||
if ((type(event) == coin.SoMouseButtonEvent and
|
||||
event.getState() == coin.SoMouseButtonEvent.UP
|
||||
and event.getButton() == coin.SoMouseButtonEvent.BUTTON1) or
|
||||
force):
|
||||
self.register()
|
||||
if self._dragCB:
|
||||
self.events.removeEventCallback(
|
||||
coin.SoEvent.getClassTypeId(), self._dragCB)
|
||||
self._direction = None
|
||||
self._dragCB = None
|
||||
self.start_pos = None
|
||||
for obj in self.drag_objects:
|
||||
obj.drag_release()
|
||||
for foo in self.on_drag_release:
|
||||
foo()
|
||||
self.drag_objects = []
|
||||
elif (type(event) == coin.SoKeyboardEvent and
|
||||
event.getState() == coin.SoMouseButtonEvent.DOWN):
|
||||
if event.getKey() == InteractionSeparator.ctrl_keys["abort_grab"]: # esc
|
||||
for obj in self.drag_objects:
|
||||
obj.drag([0, 0, 0], 1) # set back to zero
|
||||
self.dragCB(attr, event_callback, force=True)
|
||||
return
|
||||
try:
|
||||
key = chr(event.getKey())
|
||||
except ValueError:
|
||||
# there is no character for this value
|
||||
key = "_"
|
||||
if key in [InteractionSeparator.ctrl_keys["axis_x"],
|
||||
InteractionSeparator.ctrl_keys["axis_y"],
|
||||
InteractionSeparator.ctrl_keys["axis_z"]] and key != self._direction:
|
||||
self._direction = key
|
||||
else:
|
||||
self._direction = None
|
||||
diff = self.cursor_pos(event) - self.start_pos
|
||||
diff = self.constrained_vector(diff)
|
||||
for obj in self.drag_objects:
|
||||
obj.drag(diff, 1)
|
||||
for foo in self.on_drag:
|
||||
foo()
|
||||
|
||||
elif type(event) == coin.SoLocation2Event:
|
||||
fact = 0.1 if event.wasShiftDown() else 1.
|
||||
diff = self.cursor_pos(event) - self.start_pos
|
||||
diff = self.constrained_vector(diff)
|
||||
for obj in self.drag_objects:
|
||||
obj.drag(diff, fact)
|
||||
for foo in self.on_drag:
|
||||
foo()
|
||||
|
||||
def deleteCB(self, attr, event_callback):
|
||||
event = event_callback.getEvent()
|
||||
# get all drag objects, every selected object can add some drag objects
|
||||
# but the eventhandler is not allowed to call the drag twice on an object
|
||||
if event.getKey() == ord(InteractionSeparator.ctrl_keys["delete"]) and (event.getState() == 1):
|
||||
self.removeSelected()
|
||||
|
||||
def removeSelected(self):
|
||||
temp = []
|
||||
for i in self.selected_objects:
|
||||
i.delete()
|
||||
for i in self.dynamic_objects + self.static_objects:
|
||||
i.check_dependency() # dependency length max = 1
|
||||
for i in self.dynamic_objects + self.static_objects:
|
||||
if i._delete:
|
||||
temp.append(i)
|
||||
self.selected_objects = []
|
||||
self.over_object = None
|
||||
self.selectionChanged()
|
||||
for i in temp:
|
||||
if i in self.dynamic_objects:
|
||||
self.dynamic_objects.remove(i)
|
||||
else:
|
||||
self.static_objects.remove(i)
|
||||
self.objects.removeChild(i)
|
||||
del i
|
||||
self.selectionChanged()
|
||||
|
||||
def removeAllChildren(self):
|
||||
for i in self.dynamic_objects:
|
||||
i.delete()
|
||||
self.dynamic_objects = []
|
||||
self.static_objects = []
|
||||
self.selected_objects = []
|
||||
self.over_object = None
|
||||
super(InteractionSeparator, self).removeAllChildren()
|
||||
|
||||
'''
|
||||
from pivy import coin
|
||||
import FreeCADGui
|
||||
def mouse_over_cb( event_callback):
|
||||
event = event_callback.getEvent()
|
||||
pos = event.getPosition().getValue()
|
||||
listObjects = FreeCADGui.ActiveDocument.ActiveView.getObjectsInfo((int(pos[0]),int(pos[1])))
|
||||
if listObjects:
|
||||
print(listObjects)
|
||||
obj = []
|
||||
if listObjects:
|
||||
FreeCAD.Console.PrintMessage("\n *** Objects under mouse pointer ***")
|
||||
for o in listObjects:
|
||||
label = str(o["Object"])
|
||||
if not label in obj:
|
||||
obj.append(label)
|
||||
FreeCAD.Console.PrintMessage("\n"+str(obj))
|
||||
|
||||
|
||||
mouse_over = view.addEventCallbackPivy( coin.SoLocation2Event.getClassTypeId(), mouse_over_cb )
|
||||
view.removeEventCallbackPivy( coin.SoLocation2Event.getClassTypeId(), mouse_over)'''
|
||||
|
||||
115
Utils/importDEM.py
Normal file
115
Utils/importDEM.py
Normal file
@@ -0,0 +1,115 @@
|
||||
# /**********************************************************************
|
||||
# * *
|
||||
# * 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 *
|
||||
# * *
|
||||
# ***********************************************************************
|
||||
|
||||
def getFile():
|
||||
from PySide import QtGui
|
||||
filters = "Esri ASC (*.asc);;CSV (*.csv);;All files (*.*)"
|
||||
return QtGui.QFileDialog.getOpenFileName(None, "Open DEM,","",filters)[0]
|
||||
|
||||
|
||||
def openEsri(filename):
|
||||
grid_space = 1
|
||||
file = open(filename, "r")
|
||||
templist = [line.split() for line in file.readlines()]
|
||||
file.close()
|
||||
del file
|
||||
|
||||
# Read meta data:
|
||||
meta = templist[0:6]
|
||||
nx = int(meta[0][1]) # NCOLS
|
||||
ny = int(meta[1][1]) # NROWS
|
||||
xllref = meta[2][0] # XLLCENTER / XLLCORNER
|
||||
xllvalue = round(float(meta[2][1]), 3)
|
||||
yllref = meta[3][0] # YLLCENTER / XLLCORNER
|
||||
yllvalue = round(float(meta[3][1]), 3)
|
||||
cellsize = round(float(meta[4][1]), 3) # CELLSIZE
|
||||
nodata_value = float(meta[5][1]) # NODATA_VALUE
|
||||
|
||||
# set coarse_factor
|
||||
coarse_factor = max(round(grid_space / cellsize), 1)
|
||||
|
||||
# Get z values
|
||||
templist = templist[6:(6 + ny)]
|
||||
templist = [templist[i][0::coarse_factor] for i in np.arange(0, len(templist), coarse_factor)]
|
||||
datavals = np.array(templist).astype(float)
|
||||
del templist
|
||||
|
||||
# create xy coordinates: Coordenada X-Y del origen (por el centro o la esquina inferior
|
||||
# izquierda de la celda)
|
||||
if xllref.upper() == "XLLCENTER":
|
||||
xllvalue = xllvalue - cellsize * nx / 2.0
|
||||
if yllref.upper() == "YLLCENTER":
|
||||
yllvalue = yllvalue - cellsize * ny / 2.0
|
||||
x = 1000 * (cellsize * np.arange(nx)[0::coarse_factor] + xllvalue)
|
||||
y = 1000 * (cellsize * np.arange(ny)[-1::-1][0::coarse_factor] + yllvalue)
|
||||
z = 1000 * datavals
|
||||
|
||||
return x, y, z, cellsize, nodata_value
|
||||
|
||||
|
||||
def openCSV(filename, delim = ','):
|
||||
import csv
|
||||
pts = list()
|
||||
with open(filename, newline='\n') as csvfile:
|
||||
reader = csv.reader(csvfile, delimiter = delim)
|
||||
for row in reader:
|
||||
pts.append(FreeCAD.Vector(float(row[1]), float(row[2]), float(row[3])))
|
||||
|
||||
return pts
|
||||
|
||||
|
||||
def interpolatePoints(points, cellsize = 2.0):
|
||||
import numpy as np
|
||||
import scipy as sp
|
||||
import scipy.interpolate
|
||||
|
||||
pts = list()
|
||||
x = list()
|
||||
y = list()
|
||||
z = list()
|
||||
for point in points:
|
||||
x.append(point.x)
|
||||
y.append(point.y)
|
||||
z.append(point.z)
|
||||
|
||||
x = np.array(x)
|
||||
y = np.array(y)
|
||||
z = np.array(z)
|
||||
spline = sp.interpolate.Rbf(x, y, z, function='thin-plate')
|
||||
xtotalsteps = int((max(x) - min(x)) / cellsize)
|
||||
#ytotalsteps = int((max(y) - min(y)) / cellsize)
|
||||
xi = np.linspace(min(x), max(x), xtotalsteps)
|
||||
step = 100
|
||||
ini = min(y)
|
||||
while ini < max(y):
|
||||
end = min(ini + (step - 1) * cellsize, max(y))
|
||||
yi = np.linspace(ini, end, step)
|
||||
ini = end + cellsize
|
||||
X, Y = np.meshgrid(xi, yi)
|
||||
Z = spline(X, Y)
|
||||
xx = X.flatten()
|
||||
yy = Y.flatten()
|
||||
zz = Z.flatten()
|
||||
for i, point in enumerate(xx):
|
||||
pts.append(FreeCAD.Vector(xx[i], yy[i], zz[i]) * 1000)
|
||||
|
||||
return pts
|
||||
1059
Utils/m_gui_edit.py
Normal file
1059
Utils/m_gui_edit.py
Normal file
File diff suppressed because it is too large
Load Diff
379
Utils/polyline.py
Normal file
379
Utils/polyline.py
Normal file
@@ -0,0 +1,379 @@
|
||||
from draftobjects.base import DraftObject
|
||||
from draftutils.utils import get_param
|
||||
|
||||
|
||||
class circle(DraftObject):
|
||||
def __init__(self, obj):
|
||||
''' Creates a circle object '''
|
||||
super(circle, self).__init__(obj, "Circle")
|
||||
self.type = 'circle'
|
||||
self.components = []
|
||||
|
||||
def execute(self, obj):
|
||||
''' Executes the circle '''
|
||||
import FreeCAD as App
|
||||
import DraftGeomUtils
|
||||
import DraftVecUtils
|
||||
|
||||
center = get_param(obj, "center")
|
||||
radius = get_param(obj, "radius")
|
||||
|
||||
if center is None:
|
||||
center = App.Vector(0, 0, 0)
|
||||
if radius is None:
|
||||
radius = 1
|
||||
|
||||
center = DraftVecUtils.to_vector(center)
|
||||
radius = DraftVecUtils.to_vector(radius)
|
||||
|
||||
circle = DraftGeomUtils.make_circle(center, radius)
|
||||
|
||||
self.components.append(circle)
|
||||
|
||||
class line(DraftObject):
|
||||
def __init__(self, obj):
|
||||
''' Creates a line object '''
|
||||
super(line, self).__init__(obj, "Line")
|
||||
self.type = 'line'
|
||||
self.components = []
|
||||
|
||||
def execute(self, obj):
|
||||
''' Executes the line '''
|
||||
import FreeCAD as App
|
||||
import DraftGeomUtils
|
||||
import DraftVecUtils
|
||||
|
||||
start = get_param(obj, "start")
|
||||
end = get_param(obj, "end")
|
||||
|
||||
if start is None:
|
||||
start = App.Vector(0, 0, 0)
|
||||
if end is None:
|
||||
end = App.Vector(1, 1, 1)
|
||||
|
||||
start = DraftVecUtils.to_vector(start)
|
||||
end = DraftVecUtils.to_vector(end)
|
||||
|
||||
line = DraftGeomUtils.make_line(start, end)
|
||||
|
||||
self.components.append(line)
|
||||
|
||||
|
||||
class polyline(DraftObject):
|
||||
def __init__(self, obj):
|
||||
''' Creates a polyline object '''
|
||||
super(Wire, self).__init__(obj, "Wire")
|
||||
self.type = 'polyline'
|
||||
self.components = []
|
||||
|
||||
def execute(self, obj):
|
||||
''' Executes the polyline '''
|
||||
import FreeCAD as App
|
||||
import Part
|
||||
V1 = App.Vector(0, 10, 0)
|
||||
V2 = App.Vector(30, 10, 0)
|
||||
V3 = App.Vector(30, -10, 0)
|
||||
V4 = App.Vector(0, -10, 0)
|
||||
|
||||
VC1 = App.Vector(-10, 0, 0)
|
||||
C1 = Part.Arc(V1, VC1, V4)
|
||||
VC2 = App.Vector(40, 0, 0)
|
||||
C2 = Part.Arc(V2, VC2, V3)
|
||||
|
||||
L1 = Part.LineSegment(V1, V2)
|
||||
L2 = Part.LineSegment(V3, V4)
|
||||
|
||||
S1 = Part.Shape([C1, L1, C2, L2])
|
||||
|
||||
W = Part.Wire(S1.Edges)
|
||||
|
||||
|
||||
|
||||
import FreeCAD as App
|
||||
import Part
|
||||
|
||||
class Polyline:
|
||||
def __init__(self):
|
||||
self.segments = []
|
||||
self.setProperties(obj)
|
||||
|
||||
def setProperties(self, obj):
|
||||
''' do something '''
|
||||
|
||||
def onDocumentRestored(self,obj):
|
||||
self.setProperties(obj)
|
||||
|
||||
def add_line(self, start, end):
|
||||
line_segment = Part.LineSegment(start, end)
|
||||
self.segments.append(line_segment)
|
||||
|
||||
def add_arc(self, start, end, center, tangent=True):
|
||||
if tangent:
|
||||
arc = Part.ArcOfCircle(start, end, center)
|
||||
else:
|
||||
start_point = start - center
|
||||
end_point = end - center
|
||||
arc = Part.ArcOfCircle(start_point, end_point, center)
|
||||
self.segments.append(arc)
|
||||
|
||||
def create_wire(self):
|
||||
wire = Part.Wire(self.segments)
|
||||
return wire
|
||||
|
||||
def create_shape(self):
|
||||
wire = self.create_wire()
|
||||
shape = Part.Shape(wire)
|
||||
return shape
|
||||
|
||||
def create_object(self, name):
|
||||
shape = self.create_shape()
|
||||
obj = App.ActiveDocument.addObject("Part::Feature", name)
|
||||
obj.Shape = shape
|
||||
return obj
|
||||
|
||||
def execute(self,obj):
|
||||
''' '''
|
||||
|
||||
class editPolyline:
|
||||
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)
|
||||
|
||||
print("end Registering callbacks")
|
||||
|
||||
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
|
||||
print("End unregistering callbacks")
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# SCENE EVENT HANDLERS
|
||||
# -------------------------------------------------------------------------
|
||||
def keyPressed(self, event_callback):
|
||||
event = event_callback.getEvent()
|
||||
print(event.getKey())
|
||||
if event.getState() == event.UP:
|
||||
if event.getKey() == 65307:
|
||||
self.quit()
|
||||
elif event.getKey() == 65293:
|
||||
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])
|
||||
|
||||
'''listObjects = FreeCADGui.ActiveDocument.ActiveView.getObjectsInfo((int(pos[0]), int(pos[1])))
|
||||
if listObjects:
|
||||
# {'x': 483239.09375, 'y': 783855.8125, 'z': 797695.0, 'Document': 'Salinas_II_GRG___v3',
|
||||
# 'Object': 'Wire023', 'Component': 'Edge30'}]
|
||||
for obj in listObjects:
|
||||
print(listObjects)
|
||||
if obj.Object == self.obj.Name:
|
||||
print(listObjects)
|
||||
'''
|
||||
|
||||
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()
|
||||
501
Utils/profile_editor - copia (2).py
Normal file
501
Utils/profile_editor - copia (2).py
Normal file
@@ -0,0 +1,501 @@
|
||||
# from curve workbench
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import Part
|
||||
import PySide.QtCore as QtCore
|
||||
import PySide.QtGui as QtGui
|
||||
from pivy import coin
|
||||
|
||||
from Utils import graphics
|
||||
|
||||
|
||||
def parameterization(points, a, closed):
|
||||
"""Computes a knot Sequence for a set of points
|
||||
fac (0-1) : parameterization factor
|
||||
fac=0 -> Uniform / fac=0.5 -> Centripetal / fac=1.0 -> Chord-Length"""
|
||||
pts = points.copy()
|
||||
if closed and pts[0].distanceToPoint(pts[-1]) > 1e-7: # we need to add the first point as the end point
|
||||
pts.append(pts[0])
|
||||
params = [0]
|
||||
for i in range(1, len(pts)):
|
||||
p = pts[i] - pts[i - 1]
|
||||
if isinstance(p, FreeCAD.Vector):
|
||||
le = p.Length
|
||||
else:
|
||||
le = p.length()
|
||||
pl = pow(le, a)
|
||||
params.append(params[-1] + pl)
|
||||
return params
|
||||
|
||||
class ConnectionMarker(graphics.Marker):
|
||||
def __init__(self, points):
|
||||
super(ConnectionMarker, self).__init__(points, True)
|
||||
|
||||
class MarkerOnShape(graphics.Marker):
|
||||
def __init__(self, points, sh=None):
|
||||
super(MarkerOnShape, self).__init__(points, True)
|
||||
self._shape = None
|
||||
self._sublink = None
|
||||
self._tangent = None
|
||||
self._translate = coin.SoTranslation()
|
||||
self._text_font = coin.SoFont()
|
||||
self._text_font.name = "Arial:Bold"
|
||||
self._text_font.size = 13.0
|
||||
self._text = coin.SoText2()
|
||||
self._text_switch = coin.SoSwitch()
|
||||
self._text_switch.addChild(self._translate)
|
||||
self._text_switch.addChild(self._text_font)
|
||||
self._text_switch.addChild(self._text)
|
||||
self.on_drag_start.append(self.add_text)
|
||||
self.on_drag_release.append(self.remove_text)
|
||||
self.addChild(self._text_switch)
|
||||
if isinstance(sh, Part.Shape):
|
||||
self.snap_shape = sh
|
||||
elif isinstance(sh, (tuple, list)):
|
||||
self.sublink = sh
|
||||
|
||||
def subshape_from_sublink(self, o):
|
||||
name = o[1][0]
|
||||
print(name, " selected")
|
||||
if 'Vertex' in name:
|
||||
n = eval(name.lstrip('Vertex'))
|
||||
return o[0].Shape.Vertexes[n - 1]
|
||||
elif 'Edge' in name:
|
||||
n = eval(name.lstrip('Edge'))
|
||||
return o[0].Shape.Edges[n - 1]
|
||||
elif 'Face' in name:
|
||||
n = eval(name.lstrip('Face'))
|
||||
return o[0].Shape.Faces[n - 1]
|
||||
|
||||
def add_text(self):
|
||||
self._text_switch.whichChild = coin.SO_SWITCH_ALL
|
||||
self.on_drag.append(self.update_text)
|
||||
|
||||
def remove_text(self):
|
||||
self._text_switch.whichChild = coin.SO_SWITCH_NONE
|
||||
self.on_drag.remove(self.update_text)
|
||||
|
||||
def update_text(self):
|
||||
p = self.points[0]
|
||||
coords = ['{: 9.3f}'.format(p[0]), '{: 9.3f}'.format(p[1]), '{: 9.3f}'.format(p[2])]
|
||||
self._translate.translation = p
|
||||
self._text.string.setValues(0, 3, coords)
|
||||
|
||||
@property
|
||||
def tangent(self):
|
||||
return self._tangent
|
||||
|
||||
@tangent.setter
|
||||
def tangent(self, t):
|
||||
if isinstance(t, FreeCAD.Vector):
|
||||
if t.Length > 1e-7:
|
||||
self._tangent = t
|
||||
self._tangent.normalize()
|
||||
self.marker.markerIndex = coin.SoMarkerSet.DIAMOND_FILLED_9_9
|
||||
else:
|
||||
self._tangent = None
|
||||
self.marker.markerIndex = coin.SoMarkerSet.CIRCLE_FILLED_9_9
|
||||
else:
|
||||
self._tangent = None
|
||||
self.marker.markerIndex = coin.SoMarkerSet.CIRCLE_FILLED_9_9
|
||||
|
||||
@property
|
||||
def snap_shape(self):
|
||||
return self._shape
|
||||
|
||||
@snap_shape.setter
|
||||
def snap_shape(self, sh):
|
||||
if isinstance(sh, Part.Shape):
|
||||
self._shape = sh
|
||||
else:
|
||||
self._shape = None
|
||||
self.alter_color()
|
||||
|
||||
@property
|
||||
def sublink(self):
|
||||
return self._sublink
|
||||
|
||||
@sublink.setter
|
||||
def sublink(self, sl):
|
||||
if isinstance(sl, (tuple, list)) and not (sl == self._sublink):
|
||||
self._shape = self.subshape_from_sublink(sl)
|
||||
self._sublink = sl
|
||||
else:
|
||||
self._shape = None
|
||||
self._sublink = None
|
||||
self.alter_color()
|
||||
|
||||
def alter_color(self):
|
||||
if isinstance(self._shape, Part.Vertex):
|
||||
self.set_color("white")
|
||||
elif isinstance(self._shape, Part.Edge):
|
||||
self.set_color("cyan")
|
||||
elif isinstance(self._shape, Part.Face):
|
||||
self.set_color("magenta")
|
||||
else:
|
||||
self.set_color("black")
|
||||
|
||||
def __repr__(self):
|
||||
return "MarkerOnShape({})".format(self._shape)
|
||||
|
||||
def drag(self, mouse_coords, fact=1.):
|
||||
if self.enabled:
|
||||
pts = self.points
|
||||
for i, p in enumerate(pts):
|
||||
p[0] = mouse_coords[0] * fact + self._tmp_points[i][0]
|
||||
p[1] = mouse_coords[1] * fact + self._tmp_points[i][1]
|
||||
p[2] = mouse_coords[2] * fact + self._tmp_points[i][2]
|
||||
if self._shape:
|
||||
v = Part.Vertex(p[0], p[1], p[2])
|
||||
proj = v.distToShape(self._shape)[1][0][1]
|
||||
# FreeCAD.Console.PrintMessage("%s -> %s\n"%(p.getValue(), proj))
|
||||
p[0] = proj.x
|
||||
p[1] = proj.y
|
||||
p[2] = proj.z
|
||||
self.points = pts
|
||||
for foo in self.on_drag:
|
||||
foo()
|
||||
|
||||
|
||||
class ConnectionPolygon(graphics.Polygon):
|
||||
std_col = "green"
|
||||
|
||||
def __init__(self, markers):
|
||||
super(ConnectionPolygon, self).__init__(
|
||||
sum([m.points for m in markers], []), True)
|
||||
self.markers = markers
|
||||
|
||||
for m in self.markers:
|
||||
m.on_drag.append(self.updatePolygon)
|
||||
|
||||
def updatePolygon(self):
|
||||
self.points = sum([m.points for m in self.markers], [])
|
||||
|
||||
@property
|
||||
def drag_objects(self):
|
||||
return self.markers
|
||||
|
||||
def check_dependency(self):
|
||||
if any([m._delete for m in self.markers]):
|
||||
self.delete()
|
||||
|
||||
class ConnectionLine(graphics.Line):
|
||||
def __init__(self, markers):
|
||||
super(ConnectionLine, self).__init__(
|
||||
sum([m.points for m in markers], []), True)
|
||||
self.markers = markers
|
||||
self._linear = False
|
||||
for m in self.markers:
|
||||
m.on_drag.append(self.updateLine)
|
||||
|
||||
def updateLine(self):
|
||||
self.points = sum([m.points for m in self.markers], [])
|
||||
if self._linear:
|
||||
p1 = self.markers[0].points[0]
|
||||
p2 = self.markers[-1].points[0]
|
||||
t = p2 - p1
|
||||
tan = FreeCAD.Vector(t[0], t[1], t[2])
|
||||
for m in self.markers:
|
||||
m.tangent = tan
|
||||
|
||||
@property
|
||||
def linear(self):
|
||||
return self._linear
|
||||
|
||||
@linear.setter
|
||||
def linear(self, b):
|
||||
self._linear = bool(b)
|
||||
|
||||
@property
|
||||
def drag_objects(self):
|
||||
return self.markers
|
||||
|
||||
def check_dependency(self):
|
||||
if any([m._delete for m in self.markers]):
|
||||
self.delete()
|
||||
|
||||
class Edit(object):
|
||||
|
||||
def __init__(self, points=[], obj=None):
|
||||
self.points = list()
|
||||
self.lines = list()
|
||||
self.obj = obj
|
||||
self.root_inserted = False
|
||||
self.root = None
|
||||
|
||||
self.editing = None
|
||||
|
||||
# event callbacks
|
||||
self.selection_callback = None
|
||||
self._keyPressedCB = None
|
||||
self._mouseMovedCB = None
|
||||
self._mousePressedCB = None
|
||||
|
||||
for p in points:
|
||||
if isinstance(p, FreeCAD.Vector):
|
||||
self.points.append(MarkerOnShape([p]))
|
||||
elif isinstance(p, (tuple, list)):
|
||||
self.points.append(MarkerOnShape([p[0]], p[1]))
|
||||
elif isinstance(p, (MarkerOnShape, ConnectionMarker)):
|
||||
self.points.append(p)
|
||||
else:
|
||||
FreeCAD.Console.PrintError("InterpoCurveEditor : bad input")
|
||||
|
||||
# Setup coin objects
|
||||
if self.obj:
|
||||
self.guidoc = self.obj.ViewObject.Document
|
||||
else:
|
||||
if not FreeCADGui.ActiveDocument:
|
||||
FreeCAD.newDocument("New")
|
||||
self.guidoc = FreeCADGui.ActiveDocument
|
||||
self.view = self.guidoc.ActiveView
|
||||
self.rm = self.view.getViewer().getSoRenderManager()
|
||||
self.sg = self.view.getSceneGraph()
|
||||
self.setupInteractionSeparator()
|
||||
|
||||
# Callbacks
|
||||
#self.unregister_editing_callbacks()
|
||||
#self.register_editing_callbacks()
|
||||
|
||||
def setupInteractionSeparator(self):
|
||||
if self.root_inserted:
|
||||
self.sg.removeChild(self.root)
|
||||
self.root = graphics.InteractionSeparator(self.rm)
|
||||
self.root.setName("InteractionSeparator")
|
||||
self.root.pick_radius = 40
|
||||
|
||||
# Populate root node
|
||||
self.root += self.points
|
||||
self.build_lines()
|
||||
self.root += self.lines
|
||||
|
||||
# set FreeCAD color scheme
|
||||
for o in self.points + self.lines:
|
||||
o.ovr_col = "yellow"
|
||||
o.sel_col = "green"
|
||||
|
||||
self.root.register()
|
||||
self.sg.addChild(self.root)
|
||||
self.root_inserted = True
|
||||
self.root.selected_objects = list()
|
||||
|
||||
def build_lines(self):
|
||||
for i in range(len(self.points) - 1):
|
||||
line = ConnectionLine([self.points[i], self.points[i + 1]])
|
||||
line.set_color("blue")
|
||||
self.lines.append(line)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# SCENE EVENTS CALLBACKS
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
def register_editing_callbacks(self):
|
||||
""" Register editing callbacks (former action function) """
|
||||
|
||||
if self._keyPressedCB is None:
|
||||
self._keyPressedCB = self.root.events.addEventCallback(coin.SoKeyboardEvent.getClassTypeId(), self.keyPressed)
|
||||
|
||||
if self._mousePressedCB is None:
|
||||
self._mousePressedCB = self.root.events.addEventCallback(coin.SoMouseButtonEvent.getClassTypeId(), self.mousePressed)
|
||||
|
||||
if self._mouseMovedCB is None:
|
||||
self._mouseMovedCB = self.root.events.addEventCallback(coin.SoLocation2Event.getClassTypeId(), self.mouseMoved)
|
||||
|
||||
def unregister_editing_callbacks(self):
|
||||
""" Remove callbacks used during editing if they exist """
|
||||
|
||||
if self._keyPressedCB:
|
||||
self.root.events.removeEventCallback(coin.SoKeyboardEvent.getClassTypeId(), self._keyPressedCB)
|
||||
self._keyPressedCB = None
|
||||
|
||||
if self._mousePressedCB:
|
||||
self.root.events.removeEventCallback(coin.SoMouseButtonEvent.getClassTypeId(), self._mousePressedCB)
|
||||
self._mousePressedCB = None
|
||||
|
||||
if self._mouseMovedCB:
|
||||
self.root.events.removeEventCallback(coin.SoLocation2Event.getClassTypeId(), self._mouseMovedCB)
|
||||
self._mouseMovedCB = None
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# SCENE EVENT HANDLERS
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
def keyPressed(self, attr, event_callback):
|
||||
event = event_callback.getEvent()
|
||||
if event.getState() == event.UP:
|
||||
#FreeCAD.Console.PrintMessage("Key pressed : %s\n"%event.getKey())
|
||||
if event.getKey() == ord("i"):
|
||||
self.subdivide()
|
||||
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.root.selected_objects)):
|
||||
if isinstance(self.root.selected_objects[i], MarkerOnShape):
|
||||
self.root.selected_objects[i].sublink = tup
|
||||
#FreeCAD.Console.PrintMessage("Snapped to {}\n".format(str(self.root.selected_objects[i].sublink)))
|
||||
self.root.selected_objects[i].drag_start()
|
||||
self.root.selected_objects[i].drag((0, 0, 0.))
|
||||
self.root.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.root.dynamic_objects:
|
||||
if isinstance(o, MarkerOnShape):
|
||||
pts.append(o)
|
||||
self.points = pts
|
||||
self.setupInteractionSeparator()
|
||||
|
||||
def mousePressed(self, attr, event_callback):
|
||||
""" Mouse button event handler, calls: startEditing, endEditing, addPoint, delPoint """
|
||||
event = event_callback.getEvent()
|
||||
if (event.getState() == coin.SoMouseButtonEvent.DOWN) and (event.getButton() == event.BUTTON1): # left click
|
||||
if not event.wasAltDown():
|
||||
''' do something '''
|
||||
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, attr, event_callback):
|
||||
""" Execute as callback for mouse movement. Update tracker position and update preview ghost. """
|
||||
|
||||
event = event_callback.getEvent()
|
||||
pos = event.getPosition()
|
||||
|
||||
'''
|
||||
if self.editing is not None:
|
||||
self.updateTrackerAndGhost(event)
|
||||
else:
|
||||
# look for a node in mouse position and highlight it
|
||||
pos = event.getPosition()
|
||||
node = self.getEditNode(pos)
|
||||
ep = self.getEditNodeIndex(node)
|
||||
if ep is not None:
|
||||
if self.overNode is not None:
|
||||
self.overNode.setColor(COLORS["default"])
|
||||
self.trackers[str(node.objectName.getValue())][ep].setColor(COLORS["red"])
|
||||
self.overNode = self.trackers[str(node.objectName.getValue())][ep]
|
||||
print("show menu")
|
||||
# self.display_tracker_menu(event)
|
||||
else:
|
||||
if self.overNode is not None:
|
||||
self.overNode.setColor(COLORS["default"])
|
||||
self.overNode = None
|
||||
'''
|
||||
|
||||
def endEditing(self, obj, nodeIndex=None, v=None):
|
||||
self.editing = None
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
# DRAFT EDIT Context menu
|
||||
# ------------------------------------------------------------------------
|
||||
|
||||
def display_tracker_menu(self, event):
|
||||
self.tracker_menu = QtGui.QMenu()
|
||||
self.event = event
|
||||
actions = None
|
||||
actions = ["add point"]
|
||||
|
||||
'''
|
||||
if self.overNode:
|
||||
# if user is over a node
|
||||
doc = self.overNode.get_doc_name()
|
||||
obj = App.getDocument(doc).getObject(self.overNode.get_obj_name())
|
||||
ep = self.overNode.get_subelement_index()
|
||||
|
||||
obj_gui_tools = self.get_obj_gui_tools(obj)
|
||||
if obj_gui_tools:
|
||||
actions = obj_gui_tools.get_edit_point_context_menu(obj, ep)
|
||||
|
||||
else:
|
||||
# try if user is over an edited object
|
||||
pos = self.event.getPosition()
|
||||
obj = self.get_selected_obj_at_position(pos)
|
||||
if utils.get_type(obj) in ["Line", "Wire", "BSpline", "BezCurve"]:
|
||||
actions = ["add point"]
|
||||
elif utils.get_type(obj) in ["Circle"] and obj.FirstAngle != obj.LastAngle:
|
||||
actions = ["invert arc"]
|
||||
|
||||
if actions is None:
|
||||
return
|
||||
'''
|
||||
for a in actions:
|
||||
self.tracker_menu.addAction(a)
|
||||
|
||||
self.tracker_menu.popup(FreeCADGui.getMainWindow().cursor().pos())
|
||||
QtCore.QObject.connect(self.tracker_menu,
|
||||
QtCore.SIGNAL("triggered(QAction *)"),
|
||||
self.evaluate_menu_action)
|
||||
|
||||
def evaluate_menu_action(self, labelname):
|
||||
action_label = str(labelname.text())
|
||||
|
||||
doc = None
|
||||
obj = None
|
||||
idx = None
|
||||
|
||||
if action_label == "add point":
|
||||
self.addPoint(self.event)
|
||||
|
||||
del self.event
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# EDIT functions
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
def addPoint(self, event):
|
||||
''' add point to the end '''
|
||||
pos = event.getPosition()
|
||||
pts = self.points.copy()
|
||||
new_select = list()
|
||||
point = FreeCAD.Vector(pos)
|
||||
mark = MarkerOnShape([point])
|
||||
pts.append(mark)
|
||||
new_select.append(mark)
|
||||
self.points = pts
|
||||
self.setupInteractionSeparator()
|
||||
self.root.selected_objects = new_select
|
||||
|
||||
def subdivide(self):
|
||||
# get selected lines and subdivide them
|
||||
pts = list()
|
||||
new_select = list()
|
||||
for o in self.lines:
|
||||
#FreeCAD.Console.PrintMessage("object %s\n"%str(o))
|
||||
if isinstance(o, ConnectionLine):
|
||||
pts.append(o.markers[0])
|
||||
if o in self.root.selected_objects:
|
||||
#idx = self.lines.index(o)
|
||||
#FreeCAD.Console.PrintMessage("Subdividing line #{}\n".format(idx))
|
||||
p1 = o.markers[0].points[0]
|
||||
p2 = o.markers[1].points[0]
|
||||
midpar = (FreeCAD.Vector(p1) + FreeCAD.Vector(p2)) / 2.0
|
||||
mark = MarkerOnShape([midpar])
|
||||
pts.append(mark)
|
||||
new_select.append(mark)
|
||||
pts.append(self.points[-1])
|
||||
self.points = pts
|
||||
self.setupInteractionSeparator()
|
||||
self.root.selected_objects = new_select
|
||||
return True
|
||||
|
||||
def quit(self):
|
||||
self.unregister_editing_callbacks()
|
||||
self.root.unregister()
|
||||
self.sg.removeChild(self.root)
|
||||
self.root_inserted = False
|
||||
533
Utils/profile_editor - copia.py
Normal file
533
Utils/profile_editor - copia.py
Normal file
@@ -0,0 +1,533 @@
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import Part
|
||||
from freecad.Curves import graphics
|
||||
from pivy import coin
|
||||
|
||||
|
||||
# from graphics import COLORS
|
||||
# FreeCAD.Console.PrintMessage("Using local Pivy.graphics library\n")
|
||||
|
||||
|
||||
def parameterization(points, a, closed):
|
||||
"""Computes a knot Sequence for a set of points
|
||||
fac (0-1) : parameterization factor
|
||||
fac=0 -> Uniform / fac=0.5 -> Centripetal / fac=1.0 -> Chord-Length"""
|
||||
pts = points.copy()
|
||||
if closed and pts[0].distanceToPoint(pts[-1]) > 1e-7: # we need to add the first point as the end point
|
||||
pts.append(pts[0])
|
||||
params = [0]
|
||||
for i in range(1, len(pts)):
|
||||
p = pts[i] - pts[i - 1]
|
||||
if isinstance(p, FreeCAD.Vector):
|
||||
le = p.Length
|
||||
else:
|
||||
le = p.length()
|
||||
pl = pow(le, a)
|
||||
params.append(params[-1] + pl)
|
||||
return params
|
||||
|
||||
|
||||
class ConnectionMarker(graphics.Marker):
|
||||
def __init__(self, points):
|
||||
super(ConnectionMarker, self).__init__(points, True)
|
||||
|
||||
|
||||
class MarkerOnShape(graphics.Marker):
|
||||
def __init__(self, points, sh=None):
|
||||
super(MarkerOnShape, self).__init__(points, True)
|
||||
self._shape = None
|
||||
self._sublink = None
|
||||
self._tangent = None
|
||||
self._translate = coin.SoTranslation()
|
||||
self._text_font = coin.SoFont()
|
||||
self._text_font.name = "Arial:Bold"
|
||||
self._text_font.size = 13.0
|
||||
self._text = coin.SoText2()
|
||||
self._text_switch = coin.SoSwitch()
|
||||
self._text_switch.addChild(self._translate)
|
||||
self._text_switch.addChild(self._text_font)
|
||||
self._text_switch.addChild(self._text)
|
||||
self.on_drag_start.append(self.add_text)
|
||||
self.on_drag_release.append(self.remove_text)
|
||||
self.addChild(self._text_switch)
|
||||
if isinstance(sh, Part.Shape):
|
||||
self.snap_shape = sh
|
||||
elif isinstance(sh, (tuple, list)):
|
||||
self.sublink = sh
|
||||
|
||||
def subshape_from_sublink(self, o):
|
||||
name = o[1][0]
|
||||
if 'Vertex' in name:
|
||||
n = eval(name.lstrip('Vertex'))
|
||||
return(o[0].Shape.Vertexes[n - 1])
|
||||
elif 'Edge' in name:
|
||||
n = eval(name.lstrip('Edge'))
|
||||
return(o[0].Shape.Edges[n - 1])
|
||||
elif 'Face' in name:
|
||||
n = eval(name.lstrip('Face'))
|
||||
return(o[0].Shape.Faces[n - 1])
|
||||
|
||||
def add_text(self):
|
||||
self._text_switch.whichChild = coin.SO_SWITCH_ALL
|
||||
self.on_drag.append(self.update_text)
|
||||
|
||||
def remove_text(self):
|
||||
self._text_switch.whichChild = coin.SO_SWITCH_NONE
|
||||
self.on_drag.remove(self.update_text)
|
||||
|
||||
def update_text(self):
|
||||
p = self.points[0]
|
||||
coords = ['{: 9.3f}'.format(p[0]), '{: 9.3f}'.format(p[1]), '{: 9.3f}'.format(p[2])]
|
||||
self._translate.translation = p
|
||||
self._text.string.setValues(0, 3, coords)
|
||||
|
||||
@property
|
||||
def tangent(self):
|
||||
return self._tangent
|
||||
|
||||
@tangent.setter
|
||||
def tangent(self, t):
|
||||
if isinstance(t, FreeCAD.Vector):
|
||||
if t.Length > 1e-7:
|
||||
self._tangent = t
|
||||
self._tangent.normalize()
|
||||
self.marker.markerIndex = coin.SoMarkerSet.DIAMOND_FILLED_9_9
|
||||
else:
|
||||
self._tangent = None
|
||||
self.marker.markerIndex = coin.SoMarkerSet.CIRCLE_FILLED_9_9
|
||||
else:
|
||||
self._tangent = None
|
||||
self.marker.markerIndex = coin.SoMarkerSet.CIRCLE_FILLED_9_9
|
||||
|
||||
@property
|
||||
def snap_shape(self):
|
||||
return self._shape
|
||||
|
||||
@snap_shape.setter
|
||||
def snap_shape(self, sh):
|
||||
if isinstance(sh, Part.Shape):
|
||||
self._shape = sh
|
||||
else:
|
||||
self._shape = None
|
||||
self.alter_color()
|
||||
|
||||
@property
|
||||
def sublink(self):
|
||||
return self._sublink
|
||||
|
||||
@sublink.setter
|
||||
def sublink(self, sl):
|
||||
if isinstance(sl, (tuple, list)) and not (sl == self._sublink):
|
||||
self._shape = self.subshape_from_sublink(sl)
|
||||
self._sublink = sl
|
||||
else:
|
||||
self._shape = None
|
||||
self._sublink = None
|
||||
self.alter_color()
|
||||
|
||||
def alter_color(self):
|
||||
if isinstance(self._shape, Part.Vertex):
|
||||
self.set_color("white")
|
||||
elif isinstance(self._shape, Part.Edge):
|
||||
self.set_color("cyan")
|
||||
elif isinstance(self._shape, Part.Face):
|
||||
self.set_color("magenta")
|
||||
else:
|
||||
self.set_color("black")
|
||||
|
||||
def __repr__(self):
|
||||
return("MarkerOnShape({})".format(self._shape))
|
||||
|
||||
def drag(self, mouse_coords, fact=1.):
|
||||
if self.enabled:
|
||||
pts = self.points
|
||||
for i, p in enumerate(pts):
|
||||
p[0] = mouse_coords[0] * fact + self._tmp_points[i][0]
|
||||
p[1] = mouse_coords[1] * fact + self._tmp_points[i][1]
|
||||
p[2] = mouse_coords[2] * fact + self._tmp_points[i][2]
|
||||
if self._shape:
|
||||
v = Part.Vertex(p[0], p[1], p[2])
|
||||
proj = v.distToShape(self._shape)[1][0][1]
|
||||
# FreeCAD.Console.PrintMessage("%s -> %s\n"%(p.getValue(), proj))
|
||||
p[0] = proj.x
|
||||
p[1] = proj.y
|
||||
p[2] = proj.z
|
||||
self.points = pts
|
||||
for foo in self.on_drag:
|
||||
foo()
|
||||
|
||||
|
||||
class ConnectionPolygon(graphics.Polygon):
|
||||
std_col = "green"
|
||||
|
||||
def __init__(self, markers):
|
||||
super(ConnectionPolygon, self).__init__(
|
||||
sum([m.points for m in markers], []), True)
|
||||
self.markers = markers
|
||||
|
||||
for m in self.markers:
|
||||
m.on_drag.append(self.updatePolygon)
|
||||
|
||||
def updatePolygon(self):
|
||||
self.points = sum([m.points for m in self.markers], [])
|
||||
|
||||
@property
|
||||
def drag_objects(self):
|
||||
return self.markers
|
||||
|
||||
def check_dependency(self):
|
||||
if any([m._delete for m in self.markers]):
|
||||
self.delete()
|
||||
|
||||
|
||||
class ConnectionLine(graphics.Line):
|
||||
def __init__(self, markers):
|
||||
super(ConnectionLine, self).__init__(
|
||||
sum([m.points for m in markers], []), True)
|
||||
self.markers = markers
|
||||
self._linear = False
|
||||
for m in self.markers:
|
||||
m.on_drag.append(self.updateLine)
|
||||
|
||||
def updateLine(self):
|
||||
self.points = sum([m.points for m in self.markers], [])
|
||||
if self._linear:
|
||||
p1 = self.markers[0].points[0]
|
||||
p2 = self.markers[-1].points[0]
|
||||
t = p2 - p1
|
||||
tan = FreeCAD.Vector(t[0], t[1], t[2])
|
||||
for m in self.markers:
|
||||
m.tangent = tan
|
||||
|
||||
@property
|
||||
def linear(self):
|
||||
return self._linear
|
||||
|
||||
@linear.setter
|
||||
def linear(self, b):
|
||||
self._linear = bool(b)
|
||||
|
||||
@property
|
||||
def drag_objects(self):
|
||||
return self.markers
|
||||
|
||||
def check_dependency(self):
|
||||
if any([m._delete for m in self.markers]):
|
||||
self.delete()
|
||||
|
||||
|
||||
class InterpoCurveEditor(object):
|
||||
"""Interpolation curve free-hand editor
|
||||
my_editor = InterpoCurveEditor([points], obj)
|
||||
obj is the FreeCAD object that will receive
|
||||
the curve shape at the end of editing.
|
||||
points can be :
|
||||
- Vector (free point)
|
||||
- (Vector, shape) (point on shape)"""
|
||||
def __init__(self, points=[], fp=None):
|
||||
self.points = list()
|
||||
self.curve = Part.BSplineCurve()
|
||||
self.fp = fp
|
||||
self.root_inserted = False
|
||||
self.periodic = False
|
||||
self.param_factor = 1.0
|
||||
# self.support = None # Not yet implemented
|
||||
for p in points:
|
||||
if isinstance(p, FreeCAD.Vector):
|
||||
self.points.append(MarkerOnShape([p]))
|
||||
elif isinstance(p, (tuple, list)):
|
||||
self.points.append(MarkerOnShape([p[0]], p[1]))
|
||||
elif isinstance(p, (MarkerOnShape, ConnectionMarker)):
|
||||
self.points.append(p)
|
||||
else:
|
||||
FreeCAD.Console.PrintError("InterpoCurveEditor : bad input")
|
||||
# Setup coin objects
|
||||
if self.fp:
|
||||
self.guidoc = self.fp.ViewObject.Document
|
||||
else:
|
||||
if not FreeCADGui.ActiveDocument:
|
||||
FreeCAD.newDocument("New")
|
||||
self.guidoc = FreeCADGui.ActiveDocument
|
||||
self.view = self.guidoc.ActiveView
|
||||
self.rm = self.view.getViewer().getSoRenderManager()
|
||||
self.sg = self.view.getSceneGraph()
|
||||
self.setup_InteractionSeparator()
|
||||
self.update_curve()
|
||||
|
||||
def setup_InteractionSeparator(self):
|
||||
if self.root_inserted:
|
||||
self.sg.removeChild(self.root)
|
||||
self.root = graphics.InteractionSeparator(self.rm)
|
||||
self.root.setName("InteractionSeparator")
|
||||
# self.root.ovr_col = "yellow"
|
||||
# self.root.sel_col = "green"
|
||||
self.root.pick_radius = 40
|
||||
self.root.on_drag.append(self.update_curve)
|
||||
# Keyboard callback
|
||||
# self.events = coin.SoEventCallback()
|
||||
self._controlCB = self.root.events.addEventCallback(coin.SoKeyboardEvent.getClassTypeId(), self.controlCB)
|
||||
# populate root node
|
||||
# self.root.addChild(self.events)
|
||||
self.root += self.points
|
||||
self.build_lines()
|
||||
self.root += self.lines
|
||||
# set FreeCAD color scheme
|
||||
for o in self.points + self.lines:
|
||||
o.ovr_col = "yellow"
|
||||
o.sel_col = "green"
|
||||
self.root.register()
|
||||
self.sg.addChild(self.root)
|
||||
self.root_inserted = True
|
||||
self.root.selected_objects = list()
|
||||
|
||||
def compute_tangents(self):
|
||||
tans = list()
|
||||
flags = list()
|
||||
for i in range(len(self.points)):
|
||||
if isinstance(self.points[i].snap_shape, Part.Face):
|
||||
for vec in self.points[i].points:
|
||||
u, v = self.points[i].snap_shape.Surface.parameter(FreeCAD.Vector(vec))
|
||||
norm = self.points[i].snap_shape.normalAt(u, v)
|
||||
cp = self.curve.parameter(FreeCAD.Vector(vec))
|
||||
t = self.curve.tangent(cp)[0]
|
||||
pl = Part.Plane(FreeCAD.Vector(), norm)
|
||||
ci = Part.Geom2d.Circle2d()
|
||||
ci.Radius = t.Length * 2
|
||||
w = Part.Wire([ci.toShape(pl)])
|
||||
f = Part.Face(w)
|
||||
# proj = f.project([Part.Vertex(t)])
|
||||
proj = Part.Vertex(t).distToShape(f)[1][0][1]
|
||||
# pt = proj.Vertexes[0].Point
|
||||
# FreeCAD.Console.PrintMessage("Projection %s -> %s\n"%(t, proj))
|
||||
if proj.Length > 1e-7:
|
||||
tans.append(proj)
|
||||
flags.append(True)
|
||||
else:
|
||||
tans.append(FreeCAD.Vector(1, 0, 0))
|
||||
flags.append(False)
|
||||
elif self.points[i].tangent:
|
||||
for j in range(len(self.points[i].points)):
|
||||
tans.append(self.points[i].tangent)
|
||||
flags.append(True)
|
||||
else:
|
||||
for j in range(len(self.points[i].points)):
|
||||
tans.append(FreeCAD.Vector(0, 0, 0))
|
||||
flags.append(False)
|
||||
return(tans, flags)
|
||||
|
||||
def update_curve(self):
|
||||
pts = list()
|
||||
for p in self.points:
|
||||
pts += p.points
|
||||
# FreeCAD.Console.PrintMessage("pts :\n%s\n"%str(pts))
|
||||
if len(pts) > 1:
|
||||
fac = self.param_factor
|
||||
if self.fp:
|
||||
fac = self.fp.Parametrization
|
||||
params = parameterization(pts, fac, self.periodic)
|
||||
self.curve.interpolate(Points=pts, Parameters=params, PeriodicFlag=self.periodic)
|
||||
tans, flags = self.compute_tangents()
|
||||
if any(flags):
|
||||
if (len(tans) == len(pts)) and (len(flags) == len(pts)):
|
||||
self.curve.interpolate(Points=pts, Parameters=params, PeriodicFlag=self.periodic, Tangents=tans, TangentFlags=flags)
|
||||
if self.fp:
|
||||
self.fp.Shape = self.curve.toShape()
|
||||
|
||||
def build_lines(self):
|
||||
self.lines = list()
|
||||
for i in range(len(self.points) - 1):
|
||||
line = ConnectionLine([self.points[i], self.points[i + 1]])
|
||||
line.set_color("blue")
|
||||
self.lines.append(line)
|
||||
|
||||
def controlCB(self, attr, event_callback):
|
||||
event = event_callback.getEvent()
|
||||
if event.getState() == event.UP:
|
||||
# FreeCAD.Console.PrintMessage("Key pressed : %s\n"%event.getKey())
|
||||
if event.getKey() == ord("i"):
|
||||
self.subdivide()
|
||||
elif event.getKey() == ord("p"):
|
||||
self.set_planar()
|
||||
elif event.getKey() == ord("t"):
|
||||
self.set_tangents()
|
||||
elif event.getKey() == ord("q"):
|
||||
if self.fp:
|
||||
self.fp.ViewObject.Proxy.doubleClicked(self.fp.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.root.selected_objects)):
|
||||
if isinstance(self.root.selected_objects[i], MarkerOnShape):
|
||||
self.root.selected_objects[i].sublink = tup
|
||||
FreeCAD.Console.PrintMessage("Snapped to {}\n".format(str(self.root.selected_objects[i].sublink)))
|
||||
self.root.selected_objects[i].drag_start()
|
||||
self.root.selected_objects[i].drag((0, 0, 0.))
|
||||
self.root.selected_objects[i].drag_release()
|
||||
self.update_curve()
|
||||
elif event.getKey() == ord("l"):
|
||||
self.toggle_linear()
|
||||
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.root.dynamic_objects:
|
||||
if isinstance(o, MarkerOnShape):
|
||||
pts.append(o)
|
||||
self.points = pts
|
||||
self.setup_InteractionSeparator()
|
||||
self.update_curve()
|
||||
|
||||
def toggle_linear(self):
|
||||
for o in self.root.selected_objects:
|
||||
if isinstance(o, ConnectionLine):
|
||||
o.linear = not o.linear
|
||||
i = self.lines.index(o)
|
||||
if i > 0:
|
||||
self.lines[i - 1].linear = False
|
||||
if i < len(self.lines) - 1:
|
||||
self.lines[i + 1].linear = False
|
||||
o.updateLine()
|
||||
o.drag_start()
|
||||
o.drag((0, 0, 0.00001))
|
||||
o.drag_release()
|
||||
self.update_curve()
|
||||
|
||||
def set_tangents(self):
|
||||
# view_dir = FreeCAD.Vector(0, 0, 1)
|
||||
view_dir = FreeCADGui.ActiveDocument.ActiveView.getViewDirection()
|
||||
markers = list()
|
||||
for o in self.root.selected_objects:
|
||||
if isinstance(o, MarkerOnShape):
|
||||
markers.append(o)
|
||||
elif isinstance(o, ConnectionLine):
|
||||
markers.extend(o.markers)
|
||||
if len(markers) > 0:
|
||||
for m in markers:
|
||||
if m.tangent:
|
||||
m.tangent = None
|
||||
else:
|
||||
i = self.points.index(m)
|
||||
if i == 0:
|
||||
m.tangent = -view_dir
|
||||
else:
|
||||
m.tangent = view_dir
|
||||
self.update_curve()
|
||||
|
||||
def set_planar(self):
|
||||
# view_dir = FreeCAD.Vector(0, 0, 1)
|
||||
view_dir = FreeCADGui.ActiveDocument.ActiveView.getViewDirection()
|
||||
markers = list()
|
||||
for o in self.root.selected_objects:
|
||||
if isinstance(o, MarkerOnShape):
|
||||
markers.append(o)
|
||||
elif isinstance(o, ConnectionLine):
|
||||
markers.extend(o.markers)
|
||||
if len(markers) > 2:
|
||||
vec0 = markers[0].points[0]
|
||||
vec1 = markers[-1].points[0]
|
||||
p0 = FreeCAD.Vector(vec0[0], vec0[1], vec0[2])
|
||||
p1 = FreeCAD.Vector(vec1[0], vec1[1], vec1[2])
|
||||
pl = Part.Plane(p0, p1, p1 + view_dir)
|
||||
for o in markers:
|
||||
if isinstance(o.snap_shape, Part.Vertex):
|
||||
FreeCAD.Console.PrintMessage("Snapped to Vertex\n")
|
||||
elif isinstance(o.snap_shape, Part.Edge):
|
||||
FreeCAD.Console.PrintMessage("Snapped to Edge\n")
|
||||
c = o.snap_shape.Curve
|
||||
pts = pl.intersect(c)[0]
|
||||
new_pts = list()
|
||||
for ip in o.points:
|
||||
iv = FreeCAD.Vector(ip[0], ip[1], ip[2])
|
||||
dmin = 1e50
|
||||
new = None
|
||||
for op in pts:
|
||||
ov = FreeCAD.Vector(op.X, op.Y, op.Z)
|
||||
if iv.distanceToPoint(ov) < dmin:
|
||||
dmin = iv.distanceToPoint(ov)
|
||||
new = ov
|
||||
new_pts.append(new)
|
||||
o.points = new_pts
|
||||
elif isinstance(o.snap_shape, Part.Face):
|
||||
FreeCAD.Console.PrintMessage("Snapped to Face\n")
|
||||
s = o.snap_shape.Surface
|
||||
cvs = pl.intersect(s)
|
||||
new_pts = list()
|
||||
for ip in o.points:
|
||||
iv = Part.Vertex(FreeCAD.Vector(ip[0], ip[1], ip[2]))
|
||||
dmin = 1e50
|
||||
new = None
|
||||
for c in cvs:
|
||||
e = c.toShape()
|
||||
d, pts, info = iv.distToShape(e)
|
||||
if d < dmin:
|
||||
dmin = d
|
||||
new = pts[0][1]
|
||||
new_pts.append(new)
|
||||
o.points = new_pts
|
||||
else:
|
||||
FreeCAD.Console.PrintMessage("Not snapped\n")
|
||||
new_pts = list()
|
||||
for ip in o.points:
|
||||
iv = FreeCAD.Vector(ip[0], ip[1], ip[2])
|
||||
u, v = pl.parameter(iv)
|
||||
new_pts.append(pl.value(u, v))
|
||||
o.points = new_pts
|
||||
for li in self.lines:
|
||||
li.updateLine()
|
||||
self.update_curve()
|
||||
|
||||
def subdivide(self):
|
||||
# get selected lines and subdivide them
|
||||
pts = list()
|
||||
new_select = list()
|
||||
for o in self.lines:
|
||||
# FreeCAD.Console.PrintMessage("object %s\n"%str(o))
|
||||
if isinstance(o, ConnectionLine):
|
||||
pts.append(o.markers[0])
|
||||
if o in self.root.selected_objects:
|
||||
idx = self.lines.index(o)
|
||||
FreeCAD.Console.PrintMessage("Subdividing line #{}\n".format(idx))
|
||||
p1 = o.markers[0].points[0]
|
||||
p2 = o.markers[1].points[0]
|
||||
par1 = self.curve.parameter(FreeCAD.Vector(p1))
|
||||
par2 = self.curve.parameter(FreeCAD.Vector(p2))
|
||||
midpar = (par1 + par2) / 2.0
|
||||
mark = MarkerOnShape([self.curve.value(midpar)])
|
||||
pts.append(mark)
|
||||
new_select.append(mark)
|
||||
pts.append(self.points[-1])
|
||||
self.points = pts
|
||||
self.setup_InteractionSeparator()
|
||||
self.root.selected_objects = new_select
|
||||
self.update_curve()
|
||||
return(True)
|
||||
|
||||
def quit(self):
|
||||
self.root.events.removeEventCallback(coin.SoKeyboardEvent.getClassTypeId(), self._controlCB)
|
||||
self.root.unregister()
|
||||
# self.root.removeAllChildren()
|
||||
self.sg.removeChild(self.root)
|
||||
self.root_inserted = False
|
||||
|
||||
|
||||
def get_guide_params():
|
||||
sel = FreeCADGui.Selection.getSelectionEx()
|
||||
pts = list()
|
||||
for s in sel:
|
||||
pts.extend(list(zip(s.PickedPoints, s.SubObjects)))
|
||||
return(pts)
|
||||
|
||||
|
||||
def main():
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::Spline", "profile")
|
||||
tups = get_guide_params()
|
||||
InterpoCurveEditor(tups, obj)
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
501
Utils/profile_editor.py
Normal file
501
Utils/profile_editor.py
Normal file
@@ -0,0 +1,501 @@
|
||||
# from curve workbench
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import Part
|
||||
import PySide.QtCore as QtCore
|
||||
import PySide.QtGui as QtGui
|
||||
from pivy import coin
|
||||
|
||||
from Utils import graphics
|
||||
|
||||
|
||||
def parameterization(points, a, closed):
|
||||
"""Computes a knot Sequence for a set of points
|
||||
fac (0-1) : parameterization factor
|
||||
fac=0 -> Uniform / fac=0.5 -> Centripetal / fac=1.0 -> Chord-Length"""
|
||||
pts = points.copy()
|
||||
if closed and pts[0].distanceToPoint(pts[-1]) > 1e-7: # we need to add the first point as the end point
|
||||
pts.append(pts[0])
|
||||
params = [0]
|
||||
for i in range(1, len(pts)):
|
||||
p = pts[i] - pts[i - 1]
|
||||
if isinstance(p, FreeCAD.Vector):
|
||||
le = p.Length
|
||||
else:
|
||||
le = p.length()
|
||||
pl = pow(le, a)
|
||||
params.append(params[-1] + pl)
|
||||
return params
|
||||
|
||||
class ConnectionMarker(graphics.Marker):
|
||||
def __init__(self, points):
|
||||
super(ConnectionMarker, self).__init__(points, True)
|
||||
|
||||
class MarkerOnShape(graphics.Marker):
|
||||
def __init__(self, points, sh=None):
|
||||
super(MarkerOnShape, self).__init__(points, True)
|
||||
self._shape = None
|
||||
self._sublink = None
|
||||
self._tangent = None
|
||||
self._translate = coin.SoTranslation()
|
||||
self._text_font = coin.SoFont()
|
||||
self._text_font.name = "Arial:Bold"
|
||||
self._text_font.size = 13.0
|
||||
self._text = coin.SoText2()
|
||||
self._text_switch = coin.SoSwitch()
|
||||
self._text_switch.addChild(self._translate)
|
||||
self._text_switch.addChild(self._text_font)
|
||||
self._text_switch.addChild(self._text)
|
||||
self.on_drag_start.append(self.add_text)
|
||||
self.on_drag_release.append(self.remove_text)
|
||||
self.addChild(self._text_switch)
|
||||
if isinstance(sh, Part.Shape):
|
||||
self.snap_shape = sh
|
||||
elif isinstance(sh, (tuple, list)):
|
||||
self.sublink = sh
|
||||
|
||||
def subshape_from_sublink(self, o):
|
||||
name = o[1][0]
|
||||
print(name, " selected")
|
||||
if 'Vertex' in name:
|
||||
n = eval(name.lstrip('Vertex'))
|
||||
return o[0].Shape.Vertexes[n - 1]
|
||||
elif 'Edge' in name:
|
||||
n = eval(name.lstrip('Edge'))
|
||||
return o[0].Shape.Edges[n - 1]
|
||||
elif 'Face' in name:
|
||||
n = eval(name.lstrip('Face'))
|
||||
return o[0].Shape.Faces[n - 1]
|
||||
|
||||
def add_text(self):
|
||||
self._text_switch.whichChild = coin.SO_SWITCH_ALL
|
||||
self.on_drag.append(self.update_text)
|
||||
|
||||
def remove_text(self):
|
||||
self._text_switch.whichChild = coin.SO_SWITCH_NONE
|
||||
self.on_drag.remove(self.update_text)
|
||||
|
||||
def update_text(self):
|
||||
p = self.points[0]
|
||||
coords = ['{: 9.3f}'.format(p[0]), '{: 9.3f}'.format(p[1]), '{: 9.3f}'.format(p[2])]
|
||||
self._translate.translation = p
|
||||
self._text.string.setValues(0, 3, coords)
|
||||
|
||||
@property
|
||||
def tangent(self):
|
||||
return self._tangent
|
||||
|
||||
@tangent.setter
|
||||
def tangent(self, t):
|
||||
if isinstance(t, FreeCAD.Vector):
|
||||
if t.Length > 1e-7:
|
||||
self._tangent = t
|
||||
self._tangent.normalize()
|
||||
self.marker.markerIndex = coin.SoMarkerSet.DIAMOND_FILLED_9_9
|
||||
else:
|
||||
self._tangent = None
|
||||
self.marker.markerIndex = coin.SoMarkerSet.CIRCLE_FILLED_9_9
|
||||
else:
|
||||
self._tangent = None
|
||||
self.marker.markerIndex = coin.SoMarkerSet.CIRCLE_FILLED_9_9
|
||||
|
||||
@property
|
||||
def snap_shape(self):
|
||||
return self._shape
|
||||
|
||||
@snap_shape.setter
|
||||
def snap_shape(self, sh):
|
||||
if isinstance(sh, Part.Shape):
|
||||
self._shape = sh
|
||||
else:
|
||||
self._shape = None
|
||||
self.alter_color()
|
||||
|
||||
@property
|
||||
def sublink(self):
|
||||
return self._sublink
|
||||
|
||||
@sublink.setter
|
||||
def sublink(self, sl):
|
||||
if isinstance(sl, (tuple, list)) and not (sl == self._sublink):
|
||||
self._shape = self.subshape_from_sublink(sl)
|
||||
self._sublink = sl
|
||||
else:
|
||||
self._shape = None
|
||||
self._sublink = None
|
||||
self.alter_color()
|
||||
|
||||
def alter_color(self):
|
||||
if isinstance(self._shape, Part.Vertex):
|
||||
self.set_color("white")
|
||||
elif isinstance(self._shape, Part.Edge):
|
||||
self.set_color("cyan")
|
||||
elif isinstance(self._shape, Part.Face):
|
||||
self.set_color("magenta")
|
||||
else:
|
||||
self.set_color("black")
|
||||
|
||||
def __repr__(self):
|
||||
return "MarkerOnShape({})".format(self._shape)
|
||||
|
||||
def drag(self, mouse_coords, fact=1.):
|
||||
if self.enabled:
|
||||
pts = self.points
|
||||
for i, p in enumerate(pts):
|
||||
p[0] = mouse_coords[0] * fact + self._tmp_points[i][0]
|
||||
p[1] = mouse_coords[1] * fact + self._tmp_points[i][1]
|
||||
p[2] = mouse_coords[2] * fact + self._tmp_points[i][2]
|
||||
if self._shape:
|
||||
v = Part.Vertex(p[0], p[1], p[2])
|
||||
proj = v.distToShape(self._shape)[1][0][1]
|
||||
# FreeCAD.Console.PrintMessage("%s -> %s\n"%(p.getValue(), proj))
|
||||
p[0] = proj.x
|
||||
p[1] = proj.y
|
||||
p[2] = proj.z
|
||||
self.points = pts
|
||||
for foo in self.on_drag:
|
||||
foo()
|
||||
|
||||
|
||||
class ConnectionPolygon(graphics.Polygon):
|
||||
std_col = "green"
|
||||
|
||||
def __init__(self, markers):
|
||||
super(ConnectionPolygon, self).__init__(
|
||||
sum([m.points for m in markers], []), True)
|
||||
self.markers = markers
|
||||
|
||||
for m in self.markers:
|
||||
m.on_drag.append(self.updatePolygon)
|
||||
|
||||
def updatePolygon(self):
|
||||
self.points = sum([m.points for m in self.markers], [])
|
||||
|
||||
@property
|
||||
def drag_objects(self):
|
||||
return self.markers
|
||||
|
||||
def check_dependency(self):
|
||||
if any([m._delete for m in self.markers]):
|
||||
self.delete()
|
||||
|
||||
class ConnectionLine(graphics.Line):
|
||||
def __init__(self, markers):
|
||||
super(ConnectionLine, self).__init__(
|
||||
sum([m.points for m in markers], []), True)
|
||||
self.markers = markers
|
||||
self._linear = False
|
||||
for m in self.markers:
|
||||
m.on_drag.append(self.updateLine)
|
||||
|
||||
def updateLine(self):
|
||||
self.points = sum([m.points for m in self.markers], [])
|
||||
if self._linear:
|
||||
p1 = self.markers[0].points[0]
|
||||
p2 = self.markers[-1].points[0]
|
||||
t = p2 - p1
|
||||
tan = FreeCAD.Vector(t[0], t[1], t[2])
|
||||
for m in self.markers:
|
||||
m.tangent = tan
|
||||
|
||||
@property
|
||||
def linear(self):
|
||||
return self._linear
|
||||
|
||||
@linear.setter
|
||||
def linear(self, b):
|
||||
self._linear = bool(b)
|
||||
|
||||
@property
|
||||
def drag_objects(self):
|
||||
return self.markers
|
||||
|
||||
def check_dependency(self):
|
||||
if any([m._delete for m in self.markers]):
|
||||
self.delete()
|
||||
|
||||
|
||||
class Edit(object):
|
||||
|
||||
def __init__(self, obj=None):
|
||||
self.obj = obj
|
||||
self.root_inserted = False
|
||||
self.root = None
|
||||
|
||||
self.editing = None
|
||||
|
||||
# event callbacks
|
||||
self.selection_callback = None
|
||||
self._keyPressedCB = None
|
||||
self._mouseMovedCB = None
|
||||
self._mousePressedCB = None
|
||||
|
||||
# Setup coin objects
|
||||
self.guidoc = self.obj.ViewObject.Document
|
||||
self.view = self.guidoc.ActiveView
|
||||
self.rm = self.view.getViewer().getSoRenderManager()
|
||||
self.sg = self.view.getSceneGraph()
|
||||
self.setupInteractionSeparator()
|
||||
|
||||
# Callbacks
|
||||
self.unregister_editing_callbacks()
|
||||
self.register_editing_callbacks()
|
||||
|
||||
def setupInteractionSeparator(self):
|
||||
if self.root_inserted:
|
||||
self.sg.removeChild(self.root)
|
||||
self.root = graphics.InteractionSeparator(self.rm)
|
||||
self.root.setName("InteractionSeparator")
|
||||
self.root.pick_radius = 40
|
||||
|
||||
# Populate root node
|
||||
#self.root += self.points
|
||||
#self.build_lines()
|
||||
#self.root += self.lines
|
||||
|
||||
# set FreeCAD color scheme
|
||||
'''for o in self.points + self.lines:
|
||||
o.ovr_col = "yellow"
|
||||
o.sel_col = "green"'''
|
||||
|
||||
self.root.register()
|
||||
self.sg.addChild(self.root)
|
||||
self.root_inserted = True
|
||||
self.root.selected_objects = list()
|
||||
|
||||
def build_lines(self):
|
||||
for i in range(len(self.points) - 1):
|
||||
line = ConnectionLine([self.points[i], self.points[i + 1]])
|
||||
line.set_color("blue")
|
||||
self.lines.append(line)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# 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.root.events.addEventCallback(coin.SoKeyboardEvent.getClassTypeId(), self.keyPressed)
|
||||
|
||||
if self._mousePressedCB is None:
|
||||
self._mousePressedCB = self.root.events.addEventCallback(coin.SoMouseButtonEvent.getClassTypeId(), self.mousePressed)
|
||||
|
||||
if self._mouseMovedCB is None:
|
||||
self._mouseMovedCB = self.root.events.addEventCallback(coin.SoLocation2Event.getClassTypeId(), self.mouseMoved)
|
||||
|
||||
def unregister_editing_callbacks(self):
|
||||
""" Remove callbacks used during editing if they exist """
|
||||
|
||||
print("Unregistering")
|
||||
|
||||
if self._keyPressedCB:
|
||||
self.root.events.removeEventCallback(coin.SoKeyboardEvent.getClassTypeId(), self._keyPressedCB)
|
||||
self._keyPressedCB = None
|
||||
|
||||
if self._mousePressedCB:
|
||||
self.root.events.removeEventCallback(coin.SoMouseButtonEvent.getClassTypeId(), self._mousePressedCB)
|
||||
self._mousePressedCB = None
|
||||
|
||||
if self._mouseMovedCB:
|
||||
self.root.events.removeEventCallback(coin.SoLocation2Event.getClassTypeId(), self._mouseMovedCB)
|
||||
self._mouseMovedCB = None
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# SCENE EVENT HANDLERS
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
def keyPressed(self, attr, event_callback):
|
||||
event = event_callback.getEvent()
|
||||
print(event.getKey())
|
||||
if event.getState() == event.UP:
|
||||
#FreeCAD.Console.PrintMessage("Key pressed : %s\n"%event.getKey())
|
||||
if event.getKey() == ord("i"):
|
||||
self.subdivide()
|
||||
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.root.selected_objects)):
|
||||
if isinstance(self.root.selected_objects[i], MarkerOnShape):
|
||||
self.root.selected_objects[i].sublink = tup
|
||||
#FreeCAD.Console.PrintMessage("Snapped to {}\n".format(str(self.root.selected_objects[i].sublink)))
|
||||
self.root.selected_objects[i].drag_start()
|
||||
self.root.selected_objects[i].drag((0, 0, 0.))
|
||||
self.root.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.root.dynamic_objects:
|
||||
if isinstance(o, MarkerOnShape):
|
||||
pts.append(o)
|
||||
self.points = pts
|
||||
self.setupInteractionSeparator()
|
||||
elif event.getKey() == 65307:
|
||||
self.quit()
|
||||
|
||||
def mousePressed(self, attr, event_callback):
|
||||
""" Mouse button event handler, calls: startEditing, endEditing, addPoint, delPoint """
|
||||
event = event_callback.getEvent()
|
||||
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, attr, event_callback):
|
||||
""" Execute as callback for mouse movement. Update tracker position and update preview ghost. """
|
||||
|
||||
event = event_callback.getEvent()
|
||||
pos = event.getPosition()
|
||||
listObjects = FreeCADGui.ActiveDocument.ActiveView.getObjectsInfo((int(pos[0]), int(pos[1])))
|
||||
if listObjects:
|
||||
#{'x': 483239.09375, 'y': 783855.8125, 'z': 797695.0, 'Document': 'Salinas_II_GRG___v3',
|
||||
# 'Object': 'Wire023', 'Component': 'Edge30'}]
|
||||
for obj in listObjects:
|
||||
print(listObjects)
|
||||
if obj.Object == self.obj.Name:
|
||||
print(listObjects)
|
||||
|
||||
'''
|
||||
if self.editing is not None:
|
||||
self.updateTrackerAndGhost(event)
|
||||
else:
|
||||
# look for a node in mouse position and highlight it
|
||||
pos = event.getPosition()
|
||||
node = self.getEditNode(pos)
|
||||
ep = self.getEditNodeIndex(node)
|
||||
if ep is not None:
|
||||
if self.overNode is not None:
|
||||
self.overNode.setColor(COLORS["default"])
|
||||
self.trackers[str(node.objectName.getValue())][ep].setColor(COLORS["red"])
|
||||
self.overNode = self.trackers[str(node.objectName.getValue())][ep]
|
||||
print("show menu")
|
||||
# self.display_tracker_menu(event)
|
||||
else:
|
||||
if self.overNode is not None:
|
||||
self.overNode.setColor(COLORS["default"])
|
||||
self.overNode = None
|
||||
'''
|
||||
|
||||
def endEditing(self, obj, nodeIndex=None, v=None):
|
||||
self.editing = None
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
# DRAFT EDIT Context menu
|
||||
# ------------------------------------------------------------------------
|
||||
|
||||
def display_tracker_menu(self, event):
|
||||
self.tracker_menu = QtGui.QMenu()
|
||||
self.event = event
|
||||
actions = None
|
||||
actions = ["add point"]
|
||||
|
||||
'''
|
||||
if self.overNode:
|
||||
# if user is over a node
|
||||
doc = self.overNode.get_doc_name()
|
||||
obj = App.getDocument(doc).getObject(self.overNode.get_obj_name())
|
||||
ep = self.overNode.get_subelement_index()
|
||||
|
||||
obj_gui_tools = self.get_obj_gui_tools(obj)
|
||||
if obj_gui_tools:
|
||||
actions = obj_gui_tools.get_edit_point_context_menu(obj, ep)
|
||||
|
||||
else:
|
||||
# try if user is over an edited object
|
||||
pos = self.event.getPosition()
|
||||
obj = self.get_selected_obj_at_position(pos)
|
||||
if utils.get_type(obj) in ["Line", "Wire", "BSpline", "BezCurve"]:
|
||||
actions = ["add point"]
|
||||
elif utils.get_type(obj) in ["Circle"] and obj.FirstAngle != obj.LastAngle:
|
||||
actions = ["invert arc"]
|
||||
|
||||
if actions is None:
|
||||
return
|
||||
'''
|
||||
for a in actions:
|
||||
self.tracker_menu.addAction(a)
|
||||
|
||||
self.tracker_menu.popup(FreeCADGui.getMainWindow().cursor().pos())
|
||||
QtCore.QObject.connect(self.tracker_menu,
|
||||
QtCore.SIGNAL("triggered(QAction *)"),
|
||||
self.evaluate_menu_action)
|
||||
|
||||
def evaluate_menu_action(self, labelname):
|
||||
action_label = str(labelname.text())
|
||||
|
||||
doc = None
|
||||
obj = None
|
||||
idx = None
|
||||
|
||||
if action_label == "add point":
|
||||
self.addPoint(self.event)
|
||||
|
||||
del self.event
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# EDIT functions
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
def addPoint(self, event):
|
||||
''' add point to the end '''
|
||||
pos = event.getPosition()
|
||||
pts = self.points.copy()
|
||||
new_select = list()
|
||||
point = FreeCAD.Vector(pos)
|
||||
mark = MarkerOnShape([point])
|
||||
pts.append(mark)
|
||||
new_select.append(mark)
|
||||
self.points = pts
|
||||
self.setupInteractionSeparator()
|
||||
self.root.selected_objects = new_select
|
||||
|
||||
def subdivide(self):
|
||||
# get selected lines and subdivide them
|
||||
pts = list()
|
||||
new_select = list()
|
||||
for o in self.lines:
|
||||
#FreeCAD.Console.PrintMessage("object %s\n"%str(o))
|
||||
if isinstance(o, ConnectionLine):
|
||||
pts.append(o.markers[0])
|
||||
if o in self.root.selected_objects:
|
||||
#idx = self.lines.index(o)
|
||||
#FreeCAD.Console.PrintMessage("Subdividing line #{}\n".format(idx))
|
||||
p1 = o.markers[0].points[0]
|
||||
p2 = o.markers[1].points[0]
|
||||
midpar = (FreeCAD.Vector(p1) + FreeCAD.Vector(p2)) / 2.0
|
||||
mark = MarkerOnShape([midpar])
|
||||
pts.append(mark)
|
||||
new_select.append(mark)
|
||||
pts.append(self.points[-1])
|
||||
self.points = pts
|
||||
self.setupInteractionSeparator()
|
||||
self.root.selected_objects = new_select
|
||||
return True
|
||||
|
||||
def quit(self):
|
||||
print("Quit")
|
||||
self.unregister_editing_callbacks()
|
||||
self.root.unregister()
|
||||
#self.sg.removeChild(self.root)
|
||||
self.root_inserted = False
|
||||
Reference in New Issue
Block a user