Files
PVPlant/Utils/profile_editor - copia (2).py
2025-01-28 00:04:13 +01:00

501 lines
18 KiB
Python

# 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