1
$\begingroup$

How do I get a RegionView3D object from a Region object?

I'm working on an operator in the quad view of the 3D view and I need to determine which region the user clicked in and the relative position to that specific region

  • I've tried event.mouse_region_x and y but those are relative to the original region that the operator was ran in
  • I've also tried using the ctx object provided to the operator's modal function as well as bpy.context but both seem to be frozen on the original place the operator was ran in?
  • Current solution is to check all areas in bpy.context.screen.areas, find the one clicked in and then find the region in that area. This means I now have a region but I can't get the RegionView3D from it. This is because RegionView3D is accessed from a Space object and that doesn't have any actual positional information on the screen.
$\endgroup$

2 Answers 2

2
$\begingroup$

A RegionView3D is actually a space, the access path is bpy.context.screen.areas[#].spaces.active.region_3d (area being of type VIEW_3D), or in case of quadview mode, ... spaces.active.region_quadviews[#].

There are no direct references from a region to a RegionView3D.

It's possible to find the right quadview region and its space, here's a (modal timer) example:

import bpy
import math

def get_view_orientation(space, quadview):

    if quadview.view_perspective == 'CAMERA':
        view_orientation = "Camera Persp"

    else:
        r = lambda x: round(x, 2)
        view_rot = quadview.view_matrix.to_euler()

        orientation_dict = {(0.0, 0.0, 0.0) : 'TOP',
                            (r(math.pi), 0.0, 0.0) : 'BOTTOM',
                            (r(-math.pi/2), 0.0, 0.0) : 'FRONT',
                            (r(math.pi/2), 0.0, r(-math.pi)) : 'BACK',
                            (r(-math.pi/2), r(math.pi/2), 0.0) : 'LEFT',
                            (r(-math.pi/2), r(-math.pi/2), 0.0) : 'RIGHT'}

        view_orientation =  orientation_dict.get(tuple(map(r, view_rot)), 'USER').capitalize()
        view_orientation += " " + quadview.view_perspective.capitalize()

    if space.local_view is not None:
        view_orientation += " (Local)"

    return view_orientation


def get_quadview_index(context, x, y):
    for area in context.screen.areas:
        if area.type != 'VIEW_3D':
            continue
        if len(area.spaces.active.region_quadviews) == 0:
            return
        i = -1
        for region in area.regions:
            if region.type == 'WINDOW':
                i += 1
                if (x >= region.x and
                    y >= region.y and
                    x < region.width + region.x and
                    y < region.height + region.y):

                    return (area.spaces.active, i)
    return (None, None)

class ModalTimerOperator(bpy.types.Operator):
    """Operator which runs its self from a timer"""
    bl_idname = "wm.modal_timer_operator"
    bl_label = "Modal Timer Operator"

    _timer = None

    def modal(self, context, event):
        if event.type in {'RIGHTMOUSE', 'ESC'}:
            return self.cancel(context)

        if event.type == 'TIMER':
            space, i = get_quadview_index(context, event.mouse_x, event.mouse_y)
            if space is not None:
                quadview = space.region_quadviews[i]
                print(get_view_orientation(space, quadview))

        return {'PASS_THROUGH'}

    def execute(self, context):
        wm = context.window_manager
        self._timer = wm.event_timer_add(0.5, context.window)
        wm.modal_handler_add(self)
        return {'RUNNING_MODAL'}

    def cancel(self, context):
        wm = context.window_manager
        wm.event_timer_remove(self._timer)
        return {'FINISHED'}


def register():
    bpy.utils.register_class(ModalTimerOperator)


def unregister():
    bpy.utils.unregister_class(ModalTimerOperator)


if __name__ == "__main__":
    register()

    # test call
    bpy.ops.wm.modal_timer_operator()
$\endgroup$
2
  • $\begingroup$ Is it safe to assume that in area.regions will have regions in the same order every time? Same with spaces.active.region_quadviews? Because then that simplifies things a lot $\endgroup$
    – Cobertos
    Commented Jan 19, 2015 at 18:51
  • $\begingroup$ I believe they are. You may wanna check the code that many all 4 regions available to python. Here's the related bug report: developer.blender.org/T39028 $\endgroup$
    – CodeManX
    Commented Jan 20, 2015 at 9:25
1
$\begingroup$

It doesn't actually seem possible.

Using this other topic "Getting the active region" as sort of a jumping off point, you'll have to go through and compare all the regions positions to the indices of the quadviews list (having to assume that quadviews is a fixed list where 0 is Front, 1 is Top, etc.)

Couldn't come up with a better way to sort these but here it is

#+-------------------------------------+
#| reg 1  (TOP/XY)  | reg 3 (USER/USR) |
#|------------------+------------------|
#| reg 0 (FRONT/XZ) | reg 2 (RIGHT/YZ) |
#+-------------------------------------+
#First find reg 0
minReg = None
for reg in ctx.area.regions:
    if reg.type != "WINDOW":
        continue
    if not minReg:
        minReg = reg
        continue
    if reg.x <= minReg.x and reg.y <= minReg.y:
        minReg = reg

#Place all the other regions with respect to reg 0
regions = [None, None, None, None]
regions[0] = minReg
for reg in ctx.area.regions:
    if reg.type != "WINDOW" or reg == regions[0]:
        continue
    if reg.x > regions[0].x and reg.y > regions[0].y:
        regions[3] = reg
    elif reg.x > regions[0].x:
        regions[2] = reg
    elif reg.y > regions[0].y:
        regions[1] = reg

#Make the dictionary
self.viewDict = {
    "XY" : (ctx.space_data.region_quadviews[0], regions[0])
    "XZ" : (ctx.space_data.region_quadviews[1], regions[1])
    "YZ" : (ctx.space_data.region_quadviews[2], regions[2])
    "USER" : (ctx.space_data.region_quadviews[3], regions[3])
}
$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .