Display: Orbit Camera
Closed this issue · 13 comments
def wheelEvent(self, event):
delta = event.angleDelta().y()
zoom_factor = 2.0 if delta > 0 else 0.5
self._display.ZoomFactor(zoom_factor)
@property
def cursor(self):
return self._current_cursor
@cursor.setter
def cursor(self, value):
if self._current_cursor != value:
self._current_cursor = value
if cursor := self._available_cursors.get(value):
self.qApp.setOverrideCursor(cursor)
else:
self.qApp.restoreOverrideCursor()
def mousePressEvent(self, event):
self.setFocus()
ev = event.pos()
self.dragStartPosX = ev.x()
self.dragStartPosY = ev.y()
self._display.StartRotation(self.dragStartPosX, self.dragStartPosY)
def mouseReleaseEvent(self, event):
pt = event.pos()
modifiers = event.modifiers()
if event.button() == QtCore.Qt.LeftButton:
if self._select_area:
[Xmin, Ymin, dx, dy] = self._drawbox
self._display.SelectArea(Xmin, Ymin, Xmin + dx, Ymin + dy)
self._select_area = False
elif modifiers == QtCore.Qt.ShiftModifier:
self._display.ShiftSelect(pt.x(), pt.y())
else:
# single select otherwise
self._display.Select(pt.x(), pt.y())
if self._display.selected_shapes is not None:
self.sig_topods_selected.emit(self._display.selected_shapes)
elif event.button() == QtCore.Qt.RightButton:
if self._zoom_area:
[Xmin, Ymin, dx, dy] = self._drawbox
self._display.ZoomArea(Xmin, Ymin, Xmin + dx, Ymin + dy)
self._zoom_area = False
self.cursor = "arrow"
def DrawBox(self, event):
tolerance = 2
pt = event.pos()
dx = pt.x() - self.dragStartPosX
dy = pt.y() - self.dragStartPosY
if abs(dx) <= tolerance and abs(dy) <= tolerance:
return
self._drawbox = [self.dragStartPosX, self.dragStartPosY, dx, dy]
def mouseMoveEvent(self, evt):
pt = evt.pos()
# buttons = int(evt.buttons())
buttons = evt.buttons()
modifiers = evt.modifiers()
# ROTATE
if buttons == QtCore.Qt.LeftButton and modifiers != QtCore.Qt.ShiftModifier:
self.cursor = "rotate"
self._display.Rotation(pt.x(), pt.y())
self._drawbox = False
elif buttons == QtCore.Qt.RightButton and modifiers != QtCore.Qt.ShiftModifier:
self.cursor = "zoom"
self._display.Repaint()
self._display.DynamicZoom(
abs(self.dragStartPosX),
abs(self.dragStartPosY),
abs(pt.x()),
abs(pt.y()),
)
self.dragStartPosX = pt.x()
self.dragStartPosY = pt.y()
self._drawbox = False
elif buttons == QtCore.Qt.MiddleButton:
dx = pt.x() - self.dragStartPosX
dy = pt.y() - self.dragStartPosY
self.dragStartPosX = pt.x()
self.dragStartPosY = pt.y()
self.cursor = "pan"
self._display.Pan(dx, -dy)
self._drawbox = False
elif buttons == QtCore.Qt.RightButton:
self._zoom_area = True
self.cursor = "zoom-area"
self.DrawBox(evt)
self.update()
elif buttons == QtCore.Qt.LeftButton:
self._select_area = True
self.DrawBox(evt)
self.update()
else:
self._drawbox = False
self._display.MoveTo(pt.x(), pt.y())
self.cursor = "arrow"
May need to create a new viewer that changes these mouse controls. This stack overflow has a good reference:
Maybe create a mouse mode controller? Passes the mouse event and viewer into a configurable class to handle mouse actions
Change it so right mouse button controls the rotation and yaw and mouse wheel still handles zooming
Focal point should be base or middle of cylinder? Maybe a keyboard command can raise or lower the view.
Right mouse x axis movement will rotate camera along a positive z axis. Right mouse 6 axis will rotate along a y axis that has been rotated by the right mouses x axis rotation
Here's a link to the OpenCascade reference:
https://dev.opencascade.org/doc/refman/html/class_v3d___view.html
self._display.View.Turn(angleX, 0, 0) spins the whole scene around, not the desired effect
import logging
import os
import sys
from OCC.Core import V3d
from OCC.Core.AIS import AIS_Manipulator
from OCC.Core.gp import gp_Trsf, gp
from OCC.Display import OCCViewer
from OCC.Display.qtDisplay import qtBaseViewer
from OCC.Display.backend import get_qt_modules
QtCore, QtGui, QtWidgets, QtOpenGL = get_qt_modules()
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
log = logging.getLogger(__name__)
# source:
# https://github.com/tpaviot/pythonocc-core/blob/d2c3401f8565128518a0a9cb58455088f27a6af0/src/Display/qtDisplay.py#L63
class OrbitCameraViewer(qtBaseViewer):
# emit signal when selection is changed
# is a list of TopoDS_*
sig_topods_selected = QtCore.pyqtSignal(list)
def __init__(self, *args):
qtBaseViewer.__init__(self, *args)
self.setObjectName("qt_viewer_3d")
self._drawbox = False
self._zoom_area = False
self._select_area = False
self._inited = False
self._leftisdown = False
self._middleisdown = False
self._rightisdown = False
self._selection = None
self._drawtext = True
self._qApp = QtWidgets.QApplication.instance()
self._key_map = {}
self._current_cursor = "arrow"
self._available_cursors = {}
@property
def qApp(self):
# reference to QApplication instance
return self._qApp
@qApp.setter
def qApp(self, value):
self._qApp = value
def InitDriver(self):
self._display.Create(window_handle=int(self.winId()), parent=self)
# background gradient
self._display.SetModeShaded()
self._inited = True
# dict mapping keys to functions
self._key_map = {
ord("W"): self._display.SetModeWireFrame,
ord("S"): self._display.SetModeShaded,
ord("A"): self._display.EnableAntiAliasing,
ord("B"): self._display.DisableAntiAliasing,
ord("H"): self._display.SetModeHLR,
ord("F"): self._display.FitAll,
ord("G"): self._display.SetSelectionMode,
}
self.createCursors()
def createCursors(self):
module_pth = os.path.abspath(os.path.dirname(__file__))
icon_pth = os.path.join(module_pth, "icons")
_CURSOR_PIX_ROT = QtGui.QPixmap(os.path.join(icon_pth, "cursor-rotate.png"))
_CURSOR_PIX_PAN = QtGui.QPixmap(os.path.join(icon_pth, "cursor-pan.png"))
_CURSOR_PIX_ZOOM = QtGui.QPixmap(os.path.join(icon_pth, "cursor-magnify.png"))
_CURSOR_PIX_ZOOM_AREA = QtGui.QPixmap(
os.path.join(icon_pth, "cursor-magnify-area.png")
)
self._available_cursors = {
"arrow": QtGui.QCursor(QtCore.Qt.CursorShape.ArrowCursor), # default
"pan": QtGui.QCursor(_CURSOR_PIX_PAN),
"rotate": QtGui.QCursor(_CURSOR_PIX_ROT),
"zoom": QtGui.QCursor(_CURSOR_PIX_ZOOM),
"zoom-area": QtGui.QCursor(_CURSOR_PIX_ZOOM_AREA),
}
self._current_cursor = "arrow"
def keyPressEvent(self, event):
super(OrbitCameraViewer, self).keyPressEvent(event)
code = event.key()
if code in self._key_map:
self._key_map[code]()
elif code in range(256):
log.info(
'key: "%s"(code %i) not mapped to any function' % (chr(code), code)
)
else:
log.info("key: code %i not mapped to any function" % code)
def focusInEvent(self, event):
if self._inited:
self._display.Repaint()
def focusOutEvent(self, event):
if self._inited:
self._display.Repaint()
def wheelEvent(self, event):
delta = event.angleDelta().y()
zoom_factor = 2.0 if delta > 0 else 0.5
self._display.ZoomFactor(zoom_factor)
@property
def cursor(self):
return self._current_cursor
@cursor.setter
def cursor(self, value):
if self._current_cursor != value:
self._current_cursor = value
if cursor := self._available_cursors.get(value):
self.qApp.setOverrideCursor(cursor)
else:
self.qApp.restoreOverrideCursor()
def mousePressEvent(self, event):
self.setFocus()
ev = event.pos()
self.dragStartPosX = ev.x()
self.dragStartPosY = ev.y()
self.eyeStartPos = self._display.View.Eye()
self._display.StartRotation(self.dragStartPosX, self.dragStartPosY)
def mouseReleaseEvent(self, event):
pt = event.pos()
modifiers = event.modifiers()
if event.button() == QtCore.Qt.LeftButton:
# single select otherwise
self._display.Select(pt.x(), pt.y())
if self._display.selected_shapes is not None:
self.sig_topods_selected.emit(self._display.selected_shapes)
self.cursor = "arrow"
def mouseMoveEvent(self, evt):
pt = evt.pos()
# buttons = int(evt.buttons())
buttons = evt.buttons()
modifiers = evt.modifiers()
# ROTATE
if buttons == QtCore.Qt.RightButton:
self.cursor = "rotate"
sensitivity = 0.10
#self._display.View.SetAxis(0, 0, 0, 0, 1, 1)
angleX = self.eyeStartPos[0] - (pt.x() + self.dragStartPosX) #pt.x() * 3.149 / 18000 # convert to radians
angleY = self.eyeStartPos[2] + (pt.y() - self.dragStartPosY) #(pt.y() - self.dragStartPosY) * 3.14159 / 180
#self._display.Rotation(pt.x(), 0)
self._display.View.SetEye(angleX, self.eyeStartPos[1], angleY)
#self._display.View.Rotate(angleX, 0, 0)
if buttons == QtCore.Qt.MiddleButton:
dy = (self.dragStartPosY + pt.y()) * 3.14159 / 180
self.dragStartPosY = pt.y()
self.cursor = "pan"
self._display.View.SetEye(0, 0, dy)
Now using a custom viewer and made it so only the right mouse button controls rotation. Need to limit the rotation somehow
This is working pretty well imo. Close for now? Or is there more to do?
More to do, low priority though.