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

534 lines
19 KiB
Python

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()