Limpieza: eliminados archivos viejos (PVPLantPlacement-old_2022.py, -copia, -old, .bak)
This commit is contained in:
@@ -1,348 +0,0 @@
|
||||
# -*- 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())
|
||||
@@ -1,533 +0,0 @@
|
||||
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()
|
||||
|
||||
@@ -1,501 +0,0 @@
|
||||
# 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
|
||||
@@ -1,533 +0,0 @@
|
||||
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()
|
||||
Reference in New Issue
Block a user