[HELP] Maya navigation style

Here's the place for discussion related to coding in FreeCAD, C++ or Python. Design, interfaces and structures.
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
User avatar
Eneen
Posts: 61
Joined: Wed Aug 12, 2015 9:06 pm

Re: [HELP] Maya navigation style

Post by Eneen »

Sure:
- MMB pans - best thing that it's consistent for both 3D and 2D view
- Alt or Shift +MMB orbits - disabled for 2D view
- turntable orbit style
Used in: 3dsmax, Rhino (Alt+MMB), AutoCAD, ArchiCAD, Revit (Shift+MMB)

- zoom by scrool wheel
Rhino also uses Ctrl+MMB to zoom, Shift+Alt+MMB to yaw and Ctrl+Alt+MMB to "game style" orbit (cool with interiors)
3dsmax also uses Ctrl+Alt+MMB to zoom.

If there are no conflicts IHMO best option is from Rhino.
Hope I helped a little.
User avatar
pablogil
Posts: 882
Joined: Wed Nov 26, 2014 3:19 pm
Location: Badajoz (Spain)
Contact:

Re: [HELP] Maya navigation style

Post by pablogil »

A nice project would be to open the mouse and 3D behavior a fully user configurable setup as the "keyboard configuration" does but with the 3D view.
That way anyone could set Pan, Rotate, Zoom, Orbit, etc to the mouse event they want...
Additionally it would be nice to have presets, so that it config every parameter to your preferred software defaults (Blender, Maya, Inventor, etc...)

Anyone? ;)
Dark and Light stylesheets v2.0 to theme your FreeCAD UI, more information here
User avatar
Eneen
Posts: 61
Joined: Wed Aug 12, 2015 9:06 pm

Re: [HELP] Maya navigation style

Post by Eneen »

That would be best. I think that first thing user will do: open freecad and say "hey, here is template for my inventor, nice"
User avatar
Eneen
Posts: 61
Joined: Wed Aug 12, 2015 9:06 pm

Re: [HELP] Maya navigation style

Post by Eneen »

Hi,

I've quickly modified blender navigation style, replaced names to "revit" and changed shift to drag here:

Code: Select all

    case BUTTON3DOWN:
        newmode = NavigationStyle::PANNING;
        break;
    case SHIFTDOWN|BUTTON3DOWN:
        if (newmode != NavigationStyle::DRAGGING) {
            saveCursorPosition(ev);
        }
        newmode = NavigationStyle::DRAGGING;
        break;
I'm not sure how to replace "shift" with "alt" to get "max style", I'm not a coder unfortunately :/
Please check:

Code: Select all

/***************************************************************************
 *   Copyright (c) 2011 Werner Mayer <wmayer[at]users.sourceforge.net>     *
 *                                                                         *
 *   This file is part of the FreeCAD CAx development system.              *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU Library General Public           *
 *   License as published by the Free Software Foundation; either          *
 *   version 2 of the License, or (at your option) any later version.      *
 *                                                                         *
 *   This library  is distributed in the hope that it will be useful,      *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU Library General Public License for more details.                  *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; see the file COPYING.LIB. If not,    *
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
 *   Suite 330, Boston, MA  02111-1307, USA                                *
 *                                                                         *
 ***************************************************************************/


#include "PreCompiled.h"
#ifndef _PreComp_
# include <cfloat>
# include "InventorAll.h"
# include <QAction>
# include <QActionGroup>
# include <QApplication>
# include <QByteArray>
# include <QCursor>
# include <QList>
# include <QMenu>
# include <QMetaObject>
# include <QRegExp>
#endif

#include <Inventor/sensors/SoTimerSensor.h>

#include <App/Application.h>
#include "NavigationStyle.h"
#include "View3DInventorViewer.h"
#include "Application.h"
#include "MenuManager.h"
#include "MouseSelection.h"

using namespace Gui;

// ----------------------------------------------------------------------------------

/* TRANSLATOR Gui::RevitNavigationStyle */

TYPESYSTEM_SOURCE(Gui::RevitNavigationStyle, Gui::UserNavigationStyle);

RevitNavigationStyle::RevitNavigationStyle() : lockButton1(false)
{
}

RevitNavigationStyle::~RevitNavigationStyle()
{
}

const char* RevitNavigationStyle::mouseButtons(ViewerMode mode)
{
    switch (mode) {
    case NavigationStyle::SELECTION:
        return QT_TR_NOOP("Press left mouse button");
    case NavigationStyle::PANNING:
        return QT_TR_NOOP("Press middle mouse button");
    case NavigationStyle::DRAGGING:
        return QT_TR_NOOP("Press ALT and middle mouse button");
    case NavigationStyle::ZOOMING:
        return QT_TR_NOOP("Scroll middle mouse button");
    default:
        return "No description";
    }
}

SbBool RevitNavigationStyle::processSoEvent(const SoEvent * const ev)
{
    // Events when in "ready-to-seek" mode are ignored, except those
    // which influence the seek mode itself -- these are handled further
    // up the inheritance hierarchy.
    if (this->isSeekMode()) { return inherited::processSoEvent(ev); }
    // Switch off viewing mode (Bug #0000911)
    if (!this->isSeekMode() && !this->isAnimating() && this->isViewing())
        this->setViewing(false); // by default disable viewing mode to render the scene

    const SoType type(ev->getTypeId());

    const SbViewportRegion & vp = viewer->getSoRenderManager()->getViewportRegion();
    const SbVec2s size(vp.getViewportSizePixels());
    const SbVec2f prevnormalized = this->lastmouseposition;
    const SbVec2s pos(ev->getPosition());
    const SbVec2f posn((float) pos[0] / (float) std::max((int)(size[0] - 1), 1),
                       (float) pos[1] / (float) std::max((int)(size[1] - 1), 1));

    this->lastmouseposition = posn;

    // Set to true if any event processing happened. Note that it is not
    // necessary to restrict ourselves to only do one "action" for an
    // event, we only need this flag to see if any processing happened
    // at all.
    SbBool processed = false;

    const ViewerMode curmode = this->currentmode;
    ViewerMode newmode = curmode;

    // Mismatches in state of the modifier keys happens if the user
    // presses or releases them outside the viewer window.
    if (this->ctrldown != ev->wasCtrlDown()) {
        this->ctrldown = ev->wasCtrlDown();
    }
    if (this->shiftdown != ev->wasShiftDown()) {
        this->shiftdown = ev->wasShiftDown();
    }
    if (this->altdown != ev->wasAltDown()) {
        this->altdown = ev->wasAltDown();
    }

    // give the nodes in the foreground root the chance to handle events (e.g color bar)
    if (!processed && !viewer->isEditing()) {
        processed = handleEventInForeground(ev);
        if (processed)
            return true;
    }

    // Keyboard handling
    if (type.isDerivedFrom(SoKeyboardEvent::getClassTypeId())) {
        const SoKeyboardEvent * const event = (const SoKeyboardEvent *) ev;
        const SbBool press = event->getState() == SoButtonEvent::DOWN ? true : false;
        switch (event->getKey()) {
        case SoKeyboardEvent::LEFT_CONTROL:
        case SoKeyboardEvent::RIGHT_CONTROL:
            this->ctrldown = press;
            break;
        case SoKeyboardEvent::LEFT_SHIFT:
        case SoKeyboardEvent::RIGHT_SHIFT:
            this->shiftdown = press;
            break;
        case SoKeyboardEvent::LEFT_ALT:
        case SoKeyboardEvent::RIGHT_ALT:
            this->altdown = press;
            break;
        case SoKeyboardEvent::H:
            processed = true;
            viewer->saveHomePosition();
            break;
        case SoKeyboardEvent::S:
        case SoKeyboardEvent::HOME:
        case SoKeyboardEvent::LEFT_ARROW:
        case SoKeyboardEvent::UP_ARROW:
        case SoKeyboardEvent::RIGHT_ARROW:
        case SoKeyboardEvent::DOWN_ARROW:
            if (!this->isViewing())
                this->setViewing(true);
            break;
        default:
            break;
        }
    }

    // Mouse Button / Spaceball Button handling
    if (type.isDerivedFrom(SoMouseButtonEvent::getClassTypeId())) {
        const SoMouseButtonEvent * const event = (const SoMouseButtonEvent *) ev;
        const int button = event->getButton();
        const SbBool press = event->getState() == SoButtonEvent::DOWN ? true : false;

        //SoDebugError::postInfo("processSoEvent", "button = %d", button);
        switch (button) {
        case SoMouseButtonEvent::BUTTON1:
            this->lockrecenter = true;
            this->button1down = press;
            if (press && (this->currentmode == NavigationStyle::SEEK_WAIT_MODE)) {
                newmode = NavigationStyle::SEEK_MODE;
                this->seekToPoint(pos); // implicitly calls interactiveCountInc()
                processed = true;
            }
            //else if (press && (this->currentmode == NavigationStyle::IDLE)) {
            //    this->setViewing(true);
            //    processed = true;
            //}
            else if (press && (this->currentmode == NavigationStyle::PANNING ||
                               this->currentmode == NavigationStyle::ZOOMING)) {
                newmode = NavigationStyle::DRAGGING;
                saveCursorPosition(ev);
                this->centerTime = ev->getTime();
                processed = true;
            }
            else if (viewer->isEditing() && (this->currentmode == NavigationStyle::SPINNING)) {
                processed = true;
            }
            // issue #0002433: avoid to swallow the UP event if down the
            // scene graph somewhere a dialog gets opened
            else if (press) {
                SbTime tmp = (ev->getTime() - mouseDownConsumedEvent.getTime());
                float dci = (float)QApplication::doubleClickInterval()/1000.0f;
                // a double-click?
                if (tmp.getValue() < dci) {
                    mouseDownConsumedEvent = *event;
                    mouseDownConsumedEvent.setTime(ev->getTime());
                    processed = true;
                }
                else {
                    mouseDownConsumedEvent.setTime(ev->getTime());
                    // 'ANY' is used to mark that we don't know yet if it will
                    // be a double-click event.
                    mouseDownConsumedEvent.setButton(SoMouseButtonEvent::ANY);
                }
            }
            else if (!press) {
                if (mouseDownConsumedEvent.getButton() == SoMouseButtonEvent::BUTTON1) {
                    // now handle the postponed event
                    inherited::processSoEvent(&mouseDownConsumedEvent);
                    mouseDownConsumedEvent.setButton(SoMouseButtonEvent::ANY);
                }
            }
            break;
        case SoMouseButtonEvent::BUTTON2:
            // If we are in edit mode then simply ignore the RMB events
            // to pass the event to the base class.
            this->lockrecenter = true;
            if (!viewer->isEditing()) {
                // If we are in zoom or pan mode ignore RMB events otherwise
                // the canvas doesn't get any release events 
                if (this->currentmode != NavigationStyle::ZOOMING && 
                    this->currentmode != NavigationStyle::PANNING &&
                    this->currentmode != NavigationStyle::DRAGGING) {
                    if (this->isPopupMenuEnabled()) {
                        if (!press) { // release right mouse button
                            this->openPopupMenu(event->getPosition());
                        }
                    }
                }
            }
            // Alternative way of rotating & zooming
            if (press && (this->currentmode == NavigationStyle::PANNING ||
                          this->currentmode == NavigationStyle::ZOOMING)) {
                newmode = NavigationStyle::DRAGGING;
                saveCursorPosition(ev);
                this->centerTime = ev->getTime();
                processed = true;
            }
            this->button2down = press;
            break;
        case SoMouseButtonEvent::BUTTON3:
            if (press) {
                this->centerTime = ev->getTime();
                float ratio = vp.getViewportAspectRatio();
                SbViewVolume vv = viewer->getSoRenderManager()->getCamera()->getViewVolume(ratio);
                this->panningplane = vv.getPlane(viewer->getSoRenderManager()->getCamera()->focalDistance.getValue());
                this->lockrecenter = false;
            }
            else {
                SbTime tmp = (ev->getTime() - this->centerTime);
                float dci = (float)QApplication::doubleClickInterval()/1000.0f;
                // is it just a middle click?
                if (tmp.getValue() < dci && !this->lockrecenter) {
                    if (!this->lookAtPoint(pos)) {
                        panToCenter(panningplane, posn);
                        this->interactiveCountDec();
                    }
                    processed = true;
                }
            }
            this->button3down = press;
            break;
        case SoMouseButtonEvent::BUTTON4:
            doZoom(viewer->getSoRenderManager()->getCamera(), true, posn);
            processed = true;
            break;
        case SoMouseButtonEvent::BUTTON5:
            doZoom(viewer->getSoRenderManager()->getCamera(), false, posn);
            processed = true;
            break;
        default:
            break;
        }
    }

    // Mouse Movement handling
    if (type.isDerivedFrom(SoLocation2Event::getClassTypeId())) {
        this->lockrecenter = true;
        const SoLocation2Event * const event = (const SoLocation2Event *) ev;
        if (this->currentmode == NavigationStyle::ZOOMING) {
            this->zoomByCursor(posn, prevnormalized);
            processed = true;
        }
        else if (this->currentmode == NavigationStyle::PANNING) {
            float ratio = vp.getViewportAspectRatio();
            panCamera(viewer->getSoRenderManager()->getCamera(), ratio, this->panningplane, posn, prevnormalized);
            processed = true;
        }
        else if (this->currentmode == NavigationStyle::DRAGGING) {
            this->addToLog(event->getPosition(), event->getTime());
            this->spin(posn);
            moveCursorPosition();
            processed = true;
        }
    }

    // Spaceball & Joystick handling
    if (type.isDerivedFrom(SoMotion3Event::getClassTypeId())) {
        const SoMotion3Event * const event = static_cast<const SoMotion3Event * const>(ev);
        if (event)
            this->processMotionEvent(event);
        processed = true;
    }

    enum {
        BUTTON1DOWN = 1 << 0,
        BUTTON3DOWN = 1 << 1,
        CTRLDOWN =    1 << 2,
        SHIFTDOWN =   1 << 3,
        BUTTON2DOWN = 1 << 4
    };
    unsigned int combo =
        (this->button1down ? BUTTON1DOWN : 0) |
        (this->button2down ? BUTTON2DOWN : 0) |
        (this->button3down ? BUTTON3DOWN : 0) |
        (this->ctrldown ? CTRLDOWN : 0) |
        (this->shiftdown ? SHIFTDOWN : 0);

    switch (combo) {
    case 0:
        if (curmode == NavigationStyle::SPINNING) { break; }
        newmode = NavigationStyle::IDLE;
        // The left mouse button has been released right now but
        // we want to avoid that the event is procesed elsewhere
        if (this->lockButton1) {
            this->lockButton1 = false;
            processed = true;
        }

        //if (curmode == NavigationStyle::DRAGGING) {
        //    if (doSpin())
        //        newmode = NavigationStyle::SPINNING;
        //}
        break;
    case BUTTON1DOWN:
        // make sure not to change the selection when stopping spinning
        if (curmode == NavigationStyle::SPINNING || this->lockButton1)
            newmode = NavigationStyle::IDLE;
        else
            newmode = NavigationStyle::SELECTION;
        break;
    case BUTTON1DOWN|BUTTON2DOWN:
        newmode = NavigationStyle::PANNING;
        break;
    case BUTTON3DOWN:
        newmode = NavigationStyle::PANNING;
        break;
    case SHIFTDOWN|BUTTON3DOWN:
        if (newmode != NavigationStyle::DRAGGING) {
            saveCursorPosition(ev);
        }
        newmode = NavigationStyle::DRAGGING;
        break;
    //case BUTTON1DOWN|BUTTON2DOWN|BUTTON3DOWN:
    //    newmode = NavigationStyle::ZOOMING;
    //    break;
    case CTRLDOWN|SHIFTDOWN|BUTTON2DOWN:
        newmode = NavigationStyle::ZOOMING;
        break;

    default:
        break;
    }

    if (newmode != curmode) {
        this->setViewingMode(newmode);
    }

    // If for dragging the buttons 1 and 3 are pressed
    // but then button 3 is relaesed we shouldn't switch
    // into selection mode.
    if (this->button1down && this->button3down)
        this->lockButton1 = true;

    // If not handled in this class, pass on upwards in the inheritance
    // hierarchy.
    if (/*(curmode == NavigationStyle::SELECTION || viewer->isEditing()) && */!processed)
        processed = inherited::processSoEvent(ev);
    else
        return true;

    return processed;
}
User avatar
Eneen
Posts: 61
Joined: Wed Aug 12, 2015 9:06 pm

Re: [HELP] Maya navigation style

Post by Eneen »

a little, almost invisible bump
triplus
Veteran
Posts: 9471
Joined: Mon Dec 12, 2011 4:45 pm

Re: [HELP] Maya navigation style

Post by triplus »

Hi @Eneen.
Eneen wrote:Sure:
- MMB pans - best thing that it's consistent for both 3D and 2D view
- Alt or Shift +MMB orbits - disabled for 2D view
- turntable orbit style
Used in: 3dsmax, Rhino (Alt+MMB), AutoCAD, ArchiCAD, Revit (Shift+MMB)

- zoom by scrool wheel
Rhino also uses Ctrl+MMB to zoom, Shift+Alt+MMB to yaw and Ctrl+Alt+MMB to "game style" orbit (cool with interiors)
3dsmax also uses Ctrl+Alt+MMB to zoom.

If there are no conflicts IHMO best option is from Rhino.
Hope I helped a little.
We don't have things like "yaw" or "game style" orbit ATM. Turntable/Trackball orbit style is configurable. Therefore do you feel it makes sense to crate new navigation style just to add Ctrl+Alt+MMB to zoom? As other features are not available i feel that MayaGesture navigation style is closer to what you are after anyway?
User avatar
Eneen
Posts: 61
Joined: Wed Aug 12, 2015 9:06 pm

Re: [HELP] Maya navigation style

Post by Eneen »

triplus wrote:Hi @Eneen.
We don't have things like "yaw" or "game style" orbit ATM. Turntable/Trackball orbit style is configurable. Therefore do you feel it makes sense to crate new navigation style just to add Ctrl+Alt+MMB to zoom? As other features are not available i feel that MayaGesture navigation style is closer to what you are after anyway?
I just tried to compile all navigation styles I've ever used in one place, not wishing to implement this. I thought it may be useful in future - as FreeCAD is universal app.
What I'm looking for now is Alt+MMB to rotate and MMB to pan similar to rhino/3dsmax or Shift+MMB to rotate and MMB to pan similar to $cad/archicad/revit.
Maya style differs in that regard, closest one is blender (and now I use it), but shift+mmb and mmb is reversed, that's why I edited blender style and posted above.
triplus
Veteran
Posts: 9471
Joined: Mon Dec 12, 2011 4:45 pm

Re: [HELP] Maya navigation style

Post by triplus »

I don't plan to increase the number of projects i plan to finish when it comes to FreeCAD. But if somebody else takes one navigation style as a base (CAD/Blender) and makes CTRL/SHIFT/ALT/BUTTON1/BUTTON2/BUTTON3 combinations for Select/Zoom/Rotate/Pan configurable in a way the values can be fetched from the parameter editor... Then i can add GUI counterpart to the NavigationIndicator.
User avatar
Eneen
Posts: 61
Joined: Wed Aug 12, 2015 9:06 pm

Re: [HELP] Maya navigation style

Post by Eneen »

I've added RevitNavigation style but want to add RhinoNavigationStyle that contains ctrl, shift and alt combos. This will make FreeCAD most compatible regarding nav styles.
Currently Blender/Revit code contains this:

Code: Select all

    enum {
        BUTTON1DOWN = 1 << 0,
        BUTTON3DOWN = 1 << 1,
        CTRLDOWN =    1 << 2,
        SHIFTDOWN =   1 << 3,
        BUTTON2DOWN = 1 << 4
    };
    unsigned int combo =
        (this->button1down ? BUTTON1DOWN : 0) |
        (this->button2down ? BUTTON2DOWN : 0) |
        (this->button3down ? BUTTON3DOWN : 0) |
        (this->ctrldown ? CTRLDOWN : 0) |
        (this->shiftdown ? SHIFTDOWN : 0);
Inside gesture navigation I've found this:

Code: Select all

    enum {
        BUTTON1DOWN = 1 << 0,
        BUTTON2DOWN = 1 << 1,
        BUTTON3DOWN = 1 << 2,
        CTRLDOWN =    1 << 3,
        SHIFTDOWN =   1 << 4,
        ALTDOWN =     1 << 5,
        MASKBUTTONS = BUTTON1DOWN | BUTTON2DOWN | BUTTON3DOWN,
        MASKMODIFIERS = CTRLDOWN | SHIFTDOWN | ALTDOWN
    };
    unsigned int comboBefore = //before = state before this event
        (this->button1down ? BUTTON1DOWN : 0) |
        (this->button2down ? BUTTON2DOWN : 0) |
        (this->button3down ? BUTTON3DOWN : 0) |
        (this->ctrldown ? CTRLDOWN : 0) |
        (this->shiftdown ? SHIFTDOWN : 0) |
        (this->altdown ? ALTDOWN : 0);
How can I safely add ALT key to Blender/Revit code?
raptor3D
Posts: 1
Joined: Thu Sep 24, 2020 9:57 am

Re: [HELP] Maya navigation style

Post by raptor3D »

Hi, it's great to see Maya navigation inside FreeCAD! Thanks to everyone who made it possible.

Could you please add a support for zooming using a horizontal movement of the mouse/stylus (left <-> right)?
In fact Maya can do both vertical and horizontal zoom. I am used to the horizontal zoom and find it more comfortable/easier than the vertical one. You also usually have more space to perform the horizontal movement as opposed to the vertical (less repetitions of the movement).
The vertical zoom in FreeCAd Maya nav. messes with my muscle memory :(.

Maya supports both movements (vertical and horizontal) at the same time.
Some of the other 3D programs support it separately via a switch (vertical/horizontal).

Maya:
maya_nav_horizontal_dolly.gif
maya_nav_horizontal_dolly.gif (203.74 KiB) Viewed 1202 times

Blender and Modo:
blender_and_modo_nav_horizontal_dolly_screen.png
blender_and_modo_nav_horizontal_dolly_screen.png (75.88 KiB) Viewed 1202 times

I am a programming newbie (just started learning python recently) but I've looked at the MayaGestureNavigationStyle.cpp file. Unfortunately I could not find any part of the code where the zooming direction is defined.
Post Reply