CadQuery / CQ-editor

CadQuery GUI editor based on PyQT
Apache License 2.0
772 stars 119 forks source link

switch to perspective view #151

Closed vlad0337187 closed 2 years ago

vlad0337187 commented 4 years ago

Hello.

Maybe this is a simple question, but I can't figure out how to switch viewer widget from orthographic view to perspective view ?

Looked to viewer.py code, python-occ code, but can't figure it out.

Found V3d_ORTHOGRAPHIC here: https://www.opencascade.com/doc/occt-6.9.0/refman/html/class_v3d___view.html , so I suggest it can be switched to perspective somehow.

Also the question is: how to do this from my .py file with 3d model reference.

Because in console I can access it:

self.viewer
Out[15]: <cq_editor.widgets.viewer.OCCViewer at 0x7f1d9cb47b88>

But model .py file is imported as module, so it doesn't have access to self.

Thanks.

vlad0337187 commented 4 years ago

Progress

Comment where I describe my progress.

  1. Seems, I find how to do this. Just launch this in console:
def switch_to_perspective_view():
    self.viewer.canvas._display.SetPerspectiveProjection()
    self.viewer.canvas._display.Repaint()
    #self.viewer.redraw()

def switch_to_ortho_view():
    self.viewer.canvas._display.SetOrthographicProjection()
    self.viewer.canvas._display.Repaint()
    #self.viewer.redraw()

switch_to_perspective_view()
  1. Useful methods:

    dir(self.viewer.canvas._display) ``` Out[14]: ['ChangeRenderingParams', 'Context', 'Create', 'DisableAntiAliasing', 'DisableTextureEnv', 'DisplayColoredShape', 'DisplayMessage', 'DisplayShape', 'DisplayVector', 'DynamicZoom', 'EnableAntiAliasing', 'EnableTextureEnv', 'EraseAll', 'ExportToImage', 'FitAll', 'GetContext', 'GetImageData', 'GetOverLayer', 'GetSelectedShape', 'GetSelectedShapes', 'GetSize', 'GetView', 'GetViewer', 'Init', 'InitOffscreen', 'IsOffscreen', 'MoveTo', 'OnResize', 'OverLayer', 'Pan', 'Repaint', 'ResetView', 'Rotation', 'Select', 'SelectArea', 'SetAnaglyphMode', 'SetBackgroundImage', 'SetModeHLR', 'SetModeShaded', 'SetModeWireFrame', 'SetOrthographicProjection', 'SetPerspectiveProjection', 'SetRasterizationMode', 'SetRaytracingMode', 'SetRenderingParams', 'SetSelectionMode', 'SetSelectionModeEdge', 'SetSelectionModeFace', 'SetSelectionModeNeutral', 'SetSelectionModeShape', 'SetSelectionModeVertex', 'SetSize', 'SetVBBO', 'ShiftSelect', 'StartRotation', 'Test', 'Tumble', 'UnsetVBBO', 'View', 'View_Bottom', 'View_Front', 'View_Iso', 'View_Left', 'View_Rear', 'View_Right', 'View_Top', 'Viewer', 'Zoom', 'ZoomArea', 'ZoomFactor', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__swig_destroy__', '__weakref__', '_inited', '_is_offscreen', '_local_context_opened', '_overlay_items', '_select_callbacks', '_struc_mgr', '_window_handle', 'camera', 'default_drawer', 'display_graduated_trihedron', 'display_trihedron', 'register_overlay_item', 'register_select_callback', 'selected_shape', 'selected_shapes', 'set_bg_gradient_color', 'this', 'thisown', 'unregister_callback'] ```
  2. Noticed that: (solved by message: https://github.com/CadQuery/CQ-editor/issues/151#issuecomment-647934462)

    self.viewer.canvas
    Out[7]: <OCC.Display.qtDisplay.qtViewer3d at 0x7fb176fb3678>

points to wrong instance, it should point to ./cq_editor/widgets/occt_widget OCCTWidget instance.

So I cannot reinit view instances.

adam-urbanczyk commented 4 years ago

It is not implemented. You'd need to use this method in of self.viewer.canvas.viewer:

https://www.opencascade.com/doc/occt-7.4.0/refman/html/class_v3d___viewer.html#ae6763624106d1f9a06efaec9ab0eb95a

and then recreate the V3d_View.

Note that master has changed the OCCT widget implementation, judging by what you reported above you are using older (e.g. 0.1) version of CQ-editor.

vlad0337187 commented 4 years ago

@adam-urbanczyk , thanks, didn't notice this, will try to do so.

I'm just always confused with orthographic view - for my eyes it distorts object proportions. So I'm just trying to enable it manually.

And can I access self of self.viewer from .py script with my object ? (to automate switching to perspective view when I press "render" button)

P.S. Updated my progress comment, was able to switch existing widget to perspective view.

vlad0337187 commented 4 years ago

I could try to add a button for this, but afraid using conda in my system.

(last time it replaced my system python with own version, so that even I was unable to launch gnome-terminal. Decided not to find all it has done manually, but to reinstall system)

If this perspective view is not planned, seems, this issue can be closed. If planned - I'll probably try implement in in future, after installing CQ-editor's dependencies using pipenv (pip), not conda.

jmwright commented 4 years ago

but afraid using conda in my system.

I used to have similar problems with conda. I ended up commenting out the conda initialization lines in .bashrc (or maybe .bash_profile) and running the initialization manually. That way I didn't have to worry about conda manipulating things like my PATH and PYTHONPATH variables and messing things up.

adam-urbanczyk commented 4 years ago

@vlad0337187 you cannot access self in the script. Thanks for the investigation, but note that we are not using PythonOCC anymore. With that being said, the best way to achieve this is indeed to use Graphic3d_Camera.SetProjectionType. To enable this in OCP I need to wrap inner enums.

mitchins commented 4 years ago

I'm surprised at this as well.. the orthogonal view trips me out as when I tilt the object my brain expects to see projections, seeing a square as a trapezoid just really does a number on me. From reading here.. is there anything we can currently do via document script without touching the application code? It seems at last post some workings needed to be exposed yet to do so...

adam-urbanczyk commented 4 years ago

Something has to happen in the bindings first OCP then in the CQ-editor code. So I'm afraid nothing in the model @mitchins

vlad0337187 commented 4 years ago

@mitchins , if you use CQ-editor-0.1RC1-linux64 - the last release without conda, you can use my code template.

Just press Render button - and it will be displayed in perspective view automatically.

If you'll want to use it in your own drawing - just copy on_script_run() and related to it functions to your file.

My code template
"""
Used prefebraly non-centered boxes (planes), because it's more easy to calculate
    their position, no need to divide box widthes onto 2, etc.

So center point of scene corresponds right-back-bottom vertex of shelve.
"""

import cadquery as cq

def create_shelve_kitchen_above_window():
    SHELVE_WIDTH  = 150
    SHELVE_HEIGHT = 30
    SHELVE_DEPTH  = 30

    DSP_PLANE_THIKNESS = 1.8
    DVP_PLATE_THIKNESS = 0.25

    SPACE_BETWEEN_DOORS = 0.2
    # for them to be able to open and close
    # size taken from:  http://promebelclub.ru/forum/showthread.php?t=4395

    def create_objects():
        objects = {}

        objects['plate_bottom'] = cq.Workplane('XY').box(
                SHELVE_WIDTH, SHELVE_DEPTH, DSP_PLANE_THIKNESS,
                centered=(False, False, False),
        )
        objects['plate_top'] = objects['plate_bottom'].translate((0, 0, SHELVE_HEIGHT + DSP_PLANE_THIKNESS))

        objects['plate_back'] = (cq.Workplane('XY')
            .box(
                    SHELVE_WIDTH,
                    DVP_PLATE_THIKNESS,
                    SHELVE_HEIGHT + DSP_PLANE_THIKNESS * 2,
                    centered=(False, False, False),
            )
            .translate((
                    0,
                    - DVP_PLATE_THIKNESS,
                    0,
            ))
        )

        objects['plate_right'] = (cq.Workplane('XY')
            .box(
                    DSP_PLANE_THIKNESS,
                    SHELVE_DEPTH,
                    SHELVE_HEIGHT,
                    centered=(False, False, False),
            )
            .translate((
                    0,
                    0,
                    DSP_PLANE_THIKNESS,
            ))
        )

        objects['plate_left']                 = objects['plate_right'].translate((SHELVE_WIDTH - DSP_PLANE_THIKNESS, 0, 0))
        objects['plate_division_left']   = objects['plate_right'].translate((SHELVE_WIDTH * (1 / 3) - DSP_PLANE_THIKNESS / 2, 0, 0))
        objects['plate_division_right'] = objects['plate_right'].translate((SHELVE_WIDTH * (2 / 3) - DSP_PLANE_THIKNESS / 2, 0, 0))

        objects['door_right'] =  (cq.Workplane('XY')
            .box(
                    SHELVE_WIDTH / 3 - SPACE_BETWEEN_DOORS / 2,
                    DSP_PLANE_THIKNESS,
                    SHELVE_HEIGHT + DSP_PLANE_THIKNESS * 2,
                    centered=(False, False, False)
            )
            #.center(SHELVE_WIDTH / 3 / 2, 0)       # throws an error
            #.moveTo((SHELVE_WIDTH / 3 / 2, 0))  # throws an error
            .translate((
                    0,
                    SHELVE_DEPTH,
                    0,
            ))
        )

        objects['door_center'] = objects['door_right'].translate((SHELVE_WIDTH / 3 + SPACE_BETWEEN_DOORS / 2, 0, 0))
        objects['door_left']      = objects['door_right'].translate((SHELVE_WIDTH - SHELVE_WIDTH / 3 + SPACE_BETWEEN_DOORS / 2, 0, 0))

        return objects

    def draw_objects(objects):
        door_right = objects.pop('door_right')
        # we'll handle it manually, add special color

        for obj_name, obj in objects.items():
            show_object(obj, obj_name)

        # manual rendering
        # color options doesn't work until 0.2 release
        # so it'll be ignored now
        show_object(door_right, 'door_right', {'color': 'blue', 'alpha': 0.5})

    def rotate_door(door_name, angle):
        door                       = objects[door_name]
        vertical_edges       = door.edges('+Z')
        edge_to_rotate_by = vertical_edges.first() if angle > 0 else vertical_edges.last()
        verticle_bottom_workplane = edge_to_rotate_by.vertices('Z').first()
        verticle_bottom                   = verticle_bottom_workplane.objects[0]
        verticle_top                         = verticle_top_workplane      .objects[0]
        rotate_around_line = (verticle_bottom.Center(), verticle_top.Center())

        door = door.rotate(*rotate_around_line, angle)
        objects[door_name] = door

    objects = create_objects()
    rotate_door('door_center', angle=15)
    rotate_door('door_left',      angle=15)
    rotate_door('door_right',    angle=-15)
    draw_objects(objects)

def switch_to_perspective_view(main_window):
    """
    Should be launched from console window.
    """
    main_window.viewer.canvas._display.SetPerspectiveProjection()
    main_window.viewer.canvas._display.Repaint()
    #self.viewer.redraw()

def switch_to_ortho_view():
    main_window.viewer.canvas._display.SetOrthographicProjection()
    main_window.viewer.canvas._display.Repaint()
    #self.viewer.redraw()

def on_script_run():
    import __main__
    main_window = __main__.self
    switch_to_perspective_view(main_window)

on_script_run()
create_shelve_kitchen_above_window()

When there'll be a new release - I'll try to adopt it as Adam suggested. But as it works on current release - so I'm satisfied.

vlad0337187 commented 3 years ago

Little update. Tried 2 ways:

Now looking, maybe I can access something like display earlier.

Code

def switch_to_perspective_view(main_window):
    """
    Should be launched from console window.
    """
    #main_window.viewer.canvas._display.SetPerspectiveProjection()
    #main_window.viewer.canvas._display.Repaint()
    #self.viewer.redraw()

    import OCP.V3d
    import OCP.Graphic3d
    viewer = main_window.viewer._get_viewer()
    view   = main_window.viewer._get_view()
    camera = view.Camera()

    # way 1
    viewer.SetDefaultTypeOfView(OCP.V3d.V3d_TypeOfView.V3d_PERSPECTIVE)
    #viewer._get_view().SetFocale(75)

    viewer.Redraw()
    view.Redraw()

    main_window.viewer.canvas.update()

    # way 2
    #OCP.Graphic3d.Graphic3d_Camera.Projection
    import ctypes
    c_lib = ctypes.CDLL(OCP.__spec__.origin)

    projection_perspective = ctypes.c_int(1)
    camera.ProjectionType()
    view.Camera().SetProjectionType(projection_perspective)

waynegramlich commented 2 years ago

I'm running version 0.3.0dev and I have been unable to get out of orthographic mode. I've tried some the example code above to no avail. Any further suggestions? (BTW,I really like cq-editor!)

voneiden commented 2 years ago

@adam-urbanczyk I just tried adding menu option for switching perspective to CQ-Editor and it works out fine. I can make a PR for this.

image

1) Do we want separate buttons for switching Orthogonal/Perspective or would a single toggle work? 2) If toggle, then while we're at it, should Wireframe/Shaded also be changed to be toggle?

In toggle mode, Wireframe/Shaded toggle would then have shortcut Shift+F9 and Orthogonal/Perspective would be Shift+F10.

Default mode would remain orthogonal.


Meanwhile those who are running CQ-Editor in conda and would like to switch "permanently" to perspective mode you can open up occt_widget.py around line 37 and change

        self.viewer = V3d_Viewer(self.graphics_driver)
        self.view = self.viewer.CreateView()

to

from OCP.V3d import V3d_Viewer, V3d_TypeOfView

...

        self.viewer = V3d_Viewer(self.graphics_driver)
        self.viewer.SetDefaultTypeOfView(V3d_TypeOfView.V3d_PERSPECTIVE)
        self.view = self.viewer.CreateView()
voneiden commented 2 years ago

As a temporary workaround in the console you can also type

from OCP.Graphic3d import Graphic3d_Camera
self.viewer._get_view().Camera().SetProjectionType(Graphic3d_Camera.Projection_Perspective)
self.viewer._get_view().Redraw()

edit: whoops, fixed as pointed out by @cybervegan

waynegramlich commented 2 years ago

Matti:

Thank you for the 2-line fix.  It works fine.

I think having a ortho/perspective toggle would be appreciated by many people other than myself.

I also agree that the wireframe/shaded should be a toggle as well.

I hope your PR gets approved.

Regards,

-Wayne On 2/8/22 23:59, Matti Eiden wrote:

@adam-urbanczyk https://github.com/adam-urbanczyk I just tried adding menu option for switching perspective to CQ-Editor and it works out fine. I can make a PR for this.

image https://user-images.githubusercontent.com/437576/153145752-b794f50b-9119-43a9-b444-0bf25ace9776.png

  1. Do we want separate buttons for switching Orthogonal/Perspective or would a single toggle work?
  2. If toggle, then while we're at it, should Wireframe/Shaded also be changed to be toggle?

In toggle mode, Wireframe/Shaded toggle would then have shortcut Shift+F9 and Orthogonal/Perspective would be Shift+F10.

Default mode would remain orthogonal.


Meanwhile those who are running CQ-Editor in conda and would like to switch "permanently" to perspective mode you can open up occt_widget.py around line 37 and change

|self.viewer = V3d_Viewer(self.graphics_driver) self.view = self.viewer.CreateView() |

to

|from OCP.V3d import V3d_Viewer, V3d_TypeOfView ... self.viewer = V3d_Viewer(self.graphics_driver) self.viewer.SetDefaultTypeOfView(V3d_TypeOfView.V3d_PERSPECTIVE) self.view = self.viewer.CreateView() |

— Reply to this email directly, view it on GitHub https://github.com/CadQuery/CQ-editor/issues/151#issuecomment-1033460408, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAS6ATP4EDEI7FCS7RBBEVLU2INGJANCNFSM4OFICU7Q. Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

You are receiving this because you commented.Message ID: @.***>

cybervegan commented 2 years ago

To set the view to perspective in the console, it's: self.viewer._get_view().Camera().SetProjectionType(Graphic3d_Camera.Projection_Perspective) self.viewer._get_view().Redraw()

bigfighter commented 1 month ago

I got NameError: name 'Graphic3d_Camera' is not defined from this, but the following worked for me:

self.viewer._get_view().Camera().SetProjectionType(self.viewer._get_view().Camera().Projection_Perspective) (followed by redraw, of course)