Opened 6 years ago
Last modified 6 years ago
#2552 assigned enhancement
Enhancement: mousemode to Z-shift CofR
Reported by: | Tristan Croll | Owned by: | Tom Goddard |
---|---|---|---|
Priority: | moderate | Milestone: | |
Component: | Graphics | Version: | |
Keywords: | Cc: | ||
Blocked By: | Blocking: | ||
Notify when closed: | Platform: | all | |
Project: | ChimeraX |
Description
I've just added this to Clipper, but it might make sense as part of ChimeraX in general to go with the "cofr centerOfView" mode? Will attach a quick movie to demonstrate.
class Z_Shift_CofR(MouseMode): def __init__(self, session): self._step_multiplier = 1 super().__init__(session) from chimerax.core.graphics import Drawing d = self._drawing = Drawing('Depth Indicator') d.set_geometry(*self._drawing_geometry()) d.display = False self.view.drawing.add_drawing(d) def cleanup(self): self.view.drawing.remove_drawing(self._drawing) def mouse_down(self, event): MouseMode.mouse_down(self, event) self._set_drawing_position() self._set_drawing_color() self._drawing.display=True def mouse_drag(self, event): dx, dy = self.mouse_motion(event) self.move_camera_and_cofr(-3*self.pixel_size()*dy) self._set_drawing_position() def mouse_up(self, event): self._drawing.display=False MouseMode.mouse_up(self, event) def wheel(self, event): d = event.wheel_value() psize = self.pixel_size() self.move_camera_and_cofr(-100*d*psize) def move_camera_and_cofr(self, dz): cofr = self.view.center_of_rotation cofr_method = self.view.center_of_rotation_method camera = self.view.camera cpos = self.camera_position.origin() vd = camera.view_direction() import numpy cc = cofr-cpos shift_vec = numpy.dot(cc/numpy.linalg.norm(cc), vd) *vd * dz from chimerax.core.geometry import translation t = translation(shift_vec) self.view.center_of_rotation += shift_vec self.view.center_of_rotation_method = cofr_method camera.set_position(t*camera.position) def _drawing_geometry(self): from chimerax.surface.shapes import box_geometry return box_geometry((-1,-1,-0.01), (1,1,0.01)) def _set_drawing_position(self): from chimerax.core.geometry import Place, scale scale = 300*self.pixel_size() p = Place(axes=self.view.camera.position.axes()*scale, origin=self.view.center_of_rotation) self._drawing.position = p def _set_drawing_color(self): from chimerax.core.colors import contrast_with, Color import numpy color = numpy.array([0,0,0,0.5], numpy.float32) color[:3] = contrast_with(self.view.background_color) self._drawing.color = Color(color).uint8x4()
Attachments (1)
Change History (13)
by , 6 years ago
Attachment: | z_shift.mp4 added |
---|
comment:1 by , 6 years ago
comment:2 by , 6 years ago
A current limitation as written is that it will only work correctly with the orthographic camera since it does move the camera alongside the cofr. This is because my ZoomMouseMode in Clipper sets the front and back clipping planes based on fractional distances between cofr and camera, so it needs to move to keep things consistent. Would need to rethink things somewhat for other camera modes.
comment:3 by , 6 years ago
Please describe in words what your new feature is supposed to do. Providing lots of code makes it very time consuming to figure out.
Is this new mouse mode specifically for center of view rotation method? If so, why doesn't it just move the near/far clip planes back and forward since the center of view rotation is mid-way between those?
I agree we aren't going to add modes that should work in perspective projection and don't -- that will just look broken to the user.
The transparent square I guess is intended to tell the user they are invoking the mode. If the model is transparent it will likely make the model invisible, not so good. I don't understand the purpose of the indicator if near/far clip planes are moving, since the clipping will then visually convey the change.
I don't think ctrl-right mouse binding will work well on Mac since I believe Qt reports ctrl-left on Mac as ctrl-right because ctrl-left is the standard emulation for right click on Mac.
comment:4 by , 6 years ago
The point is to provide a method to "pan" in the Z direction. Without it, in center of view rotation mode the only way to change the depth of the rotation point is to rotate by 90 degrees, shift sideways, then rotate back - cumbersome and fiddly for the user. Yes, probably only useful for center of rotation method.
Moving the cofr and camera backwards and forwards seems better to me than moving the clip planes, since it should work even if clip planes are disabled. Not sure where the callback is that's achieving it, but moving cofr and camera like this leads to the clipping planes automatically shifting.
I just tried it out in projection mode and it actually comes out quite well - gives a fairly pleasing sensation of "flying through" the model. Turns out the Clipper ZoomMouseMode
is broken for that camera, though - will fix that ASAP.
The point of the transparent square is to make it easier to see exactly where the new centre of rotation will be - it sits in the plane of the cofr, so the depth of the new centre can be inferred from what's in front and what's behind. It's more precise than trying to visually interpolate between the clipping planes (particularly if they're fairly far apart).
I'd forgotten about the ctrl-right issue on the Mac. Will think about alternatives.
comment:5 by , 6 years ago
Here is the documentation (from core/graphics/view.py) of where the center of view rotation point lies. The position is defined by the clip planes or scene bounding box. So moving the center of view point independently doesn't make sense with this definition. Of course the definition can be changed, but then you run into the problem of how to define the position when the camera is moved (arbitrary rotation and translation). You would need a complete solution. def _center_of_view_cofr(self): ''' Keep the center of rotation in the middle of the view at a depth midway between near and far clip planes. If only the near plane or only the far plane is enabled use center on that plane. If neither near nor far planes are enabled use depth equal to center of bounding box. '''
follow-up: 5 comment:6 by , 6 years ago
Hmm... I think that's changed since the last time I looked? Will make the change, but now I'm curious why the approach I'm taking here works at all! I'm only touching camera and cofr positions, but if I watch in the side view the clipping planes move in perfect sync exactly as desired. But looking through your code I can't for the life of me see why that is. On 2019-11-12 18:48, ChimeraX wrote:
follow-up: 6 comment:7 by , 6 years ago
Ok, the documentation does seem out of data. Looking at the code fo the _center_of_view_cofr() routine shows it doesn't using the scene bounding box when no clip planes are enabled, instead it preserves the distance from the camera to the rotation point. So your method should work as long as near or far clip planes are not enabled, but should not work if those are enabled.
follow-up: 7 comment:8 by , 6 years ago
Actually I mis-stated it. It does not remember the prior distance from camera to rotation center. The comment in the code says what it does. # No clip planes. # Keep the center of rotation in the middle of the view at a depth # such that the new and previous center of rotation are in the same # plane perpendicular to the camera view direction.
follow-up: 8 comment:9 by , 6 years ago
The implementation of _center_of_view_cofr() has drifted somewhat from my original intent (the code beyond the `else:`), which is that in this mode, after the center of rotation is initially set then normal interactions should only ever move it perpendicular to the camera axis. I think the shortcut of placing it midway between near and far clip planes when they're defined is a sensible one, but I don't think the behaviour when only one clip plane is defined makes sense - you end up either with half your model invisible, or rotating around a point well outside the model. In those cases I think it'd be better to use the no-clip-planes code path. Anyway, I just found out why my code as written is working for me: Clipper's ZoomMouseMode is defining the near and far clip planes as CameraClipPlane, so of course they move when the camera moves. Can rewrite if you'd prefer near and far clip to always be defined in scene coords. On 2019-11-12 19:46, ChimeraX wrote:
follow-up: 9 comment:10 by , 6 years ago
Not sure what you mean "should only ever move perpendicular to the camera axis". If the camera rotates what camera axis are you talking about, the one before it rotated, after it rotated? The code is using the axis after it rotated. I agree the rotation point position with just near clip plane on the plane is bad. How would you place the point in that case? Currently the no clip planes case simply takes the current center of rotation (which is from a different center of rotation mode if you are just switching into center of view mode) and moves it perpendicular to the camera view axis to the center. It could do that for the single plane case.
follow-up: 10 comment:11 by , 6 years ago
Yes, I wasn’t really clear there. The idea is that once it’s placed at some point on the camera ray, moving the camera via normal user interaction should only cause the cofr to move in the plane perpendicular to the *new* view direction. At the user level, what it means is that the cofr remains fixed when rotating and zooming, and only moves when they translate. Adding this new mode provides the missing degree of freedom to give them full mouse-driven control over exactly where the centre of rotation is. I do understand the question of where to put the cofr in general when it needs to be reset (when the view jumps or when “cofr center” is first called, though - and I guess it makes sense to move it when the clipping planes change to ensure it doesn’t end up lost outside the displayed region. The following might work: - if the far clipping plane is missing, replace its plane point with the intersection of the camera ray and the back of the bounding box. - if the near plane is missing, use the front of the bounding box. - Set it midway between the above points, as per your existing code when both clip planes are present. Those rules should keep the existing behaviour when both clip planes are active, and I think would do a reasonable job in most other cases.
follow-up: 11 comment:12 by , 6 years ago
You describe how it works now. The initial placement with a single clip plane could work using the bounding box as you describe. The current code does not distinguish initial placement from updating the placement. I think I would instead just use the no-clip plane behavior of use the depth of the previous center. If you enable the near clip plane and the depth of the rotation point is not tied to the clip plane then it won't move -- and may end up in front of the clip plane. Discussion of how center of view works with a single clip plane should be in a separate ticket. Sticking to the topic of this ticket your proposed translate z mode will have to move the clip planes if near/far planes are enabled and the rotation point is mid-way between those planes, and it will need to work in all camera modes where it makes sense, with and without clip planes.
Oh yeah - in Clipper I've mapped it to ctrl-right.