Files
create/src/Gui/SoFCCSysDragger.cpp
Kacper Donat cf31f0c7ae Gui: Fix rendering order of transorm gizmo
Transform gizmo (dragger) used SoAnnotation which disables GL_DEPTH_TEST
resulting in rendering elements in wrong order. This PR introduces new
coin node: So3DAnnotation that delays rendering and clears depth buffer before
drawing which results in proper order of elements. As this destroys
information in the depth buffer it is crucial to run this action after
the scene is rendered.
2024-06-03 11:27:22 -05:00

1784 lines
63 KiB
C++

/***************************************************************************
* Copyright (c) 2015 Thomas Anderson <blobfish[at]gmx.com> *
* *
* 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 <cassert>
#include <Inventor/SbRotation.h>
#include <Inventor/actions/SoGLRenderAction.h>
#include <Inventor/engines/SoComposeVec3f.h>
#include <Inventor/elements/SoLazyElement.h>
#include <Inventor/nodes/SoLightModel.h>
#include <Inventor/nodes/SoDrawStyle.h>
#include <Inventor/nodes/SoAnnotation.h>
#include <Inventor/nodes/SoBaseColor.h>
#include <Inventor/nodes/SoCone.h>
#include <Inventor/nodes/SoCoordinate3.h>
#include <Inventor/nodes/SoCylinder.h>
#include <Inventor/nodes/SoCube.h>
#include <Inventor/nodes/SoGroup.h>
#include <Inventor/nodes/SoLineSet.h>
#include <Inventor/nodes/SoOrthographicCamera.h>
#include <Inventor/nodes/SoPerspectiveCamera.h>
#include <Inventor/nodes/SoPickStyle.h>
#include <Inventor/nodes/SoRotation.h>
#include <Inventor/nodes/SoSeparator.h>
#include <Inventor/nodes/SoSphere.h>
#include <Inventor/nodes/SoSwitch.h>
#include <Inventor/nodes/SoTranslation.h>
#endif
#include <Base/Quantity.h>
#include "SoFCCSysDragger.h"
#include "So3DAnnotation.h"
#include "MainWindow.h"
#include "SoFCDB.h"
/*
GENERAL NOTE ON COIN3D CUSTOM DRAGGERS
* You basically have two choices for creating custom dragger geometry.
* 1) create an .iv file and set environment variable to the file path. This
* comes with install headaches.
* 2) create an .iv file and run through a mock compiler that generates a header
* file to include in the project. I would have gone this way but after installing
* inventor-demo(ubuntu), the mock compiler tool was there only in source and make
* didn't do anything. Didn't want to put any time into something I didn't like anyway.
*
* static SbList <SoNode*> * defaultdraggerparts = NULL; is a global definition
* in SoInteractionKit that contains the geometry. There doesn't appear to be anyway
* to add to this other than readDefaultParts, that takes a file. So maybe a temp file?
*
* naming appears to be central to the core. It looks like as long as an object
* is alive SoNode::getByName() will find it. So maybe just create my own little
* container of objects to keep the default geometry alive....This appears to be
* working and I like this solution.
*
* SoInteractionKit warns about these
* names all being the same scope and do NOT have to be unique. Need to make names
* descriptive to avoid collisions.
* this is point of the SoGroup accessed from SoFCDB::getStorage().
*/
using namespace Gui;
SO_KIT_SOURCE(TDragger)
void TDragger::initClass()
{
SO_KIT_INIT_CLASS(TDragger, SoDragger, "Dragger");
}
TDragger::TDragger()
{
SO_KIT_CONSTRUCTOR(TDragger);
#if defined(Q_OS_MAC)
this->ref();
#endif
SO_KIT_ADD_CATALOG_ENTRY(translatorSwitch, SoSwitch, TRUE, geomSeparator, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(translator, SoSeparator, TRUE, translatorSwitch, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(translatorActive, SoSeparator, TRUE, translatorSwitch, "", TRUE);
if (SO_KIT_IS_FIRST_INSTANCE()) {
buildFirstInstance();
}
SO_KIT_ADD_FIELD(translation, (0.0, 0.0, 0.0));
SO_KIT_ADD_FIELD(translationIncrement, (1.0));
SO_KIT_ADD_FIELD(translationIncrementCount, (0));
SO_KIT_ADD_FIELD(autoScaleResult, (1.0));
SO_KIT_INIT_INSTANCE();
// initialize default parts.
// first is from 'SO_KIT_CATALOG_ENTRY_HEADER' macro
// second is unique name from buildFirstInstance().
this->setPartAsDefault("translator", "CSysDynamics_TDragger_Translator");
this->setPartAsDefault("translatorActive", "CSysDynamics_TDragger_TranslatorActive");
SoSwitch* sw = SO_GET_ANY_PART(this, "translatorSwitch", SoSwitch);
SoInteractionKit::setSwitchValue(sw, 0);
this->addStartCallback(&TDragger::startCB);
this->addMotionCallback(&TDragger::motionCB);
this->addFinishCallback(&TDragger::finishCB);
addValueChangedCallback(&TDragger::valueChangedCB);
fieldSensor.setFunction(&TDragger::fieldSensorCB);
fieldSensor.setData(this);
fieldSensor.setPriority(0);
this->setUpConnections(TRUE, TRUE);
}
TDragger::~TDragger()
{
fieldSensor.setData(nullptr);
fieldSensor.detach();
this->removeStartCallback(&TDragger::startCB);
this->removeMotionCallback(&TDragger::motionCB);
this->removeFinishCallback(&TDragger::finishCB);
removeValueChangedCallback(&TDragger::valueChangedCB);
}
void TDragger::buildFirstInstance()
{
SoGroup* geometryGroup = buildGeometry();
auto localTranslator = new SoSeparator();
localTranslator->setName("CSysDynamics_TDragger_Translator");
localTranslator->addChild(geometryGroup);
SoFCDB::getStorage()->addChild(localTranslator);
auto localTranslatorActive = new SoSeparator();
localTranslatorActive->setName("CSysDynamics_TDragger_TranslatorActive");
auto colorActive = new SoBaseColor();
colorActive->rgb.setValue(1.0, 1.0, 0.0);
localTranslatorActive->addChild(colorActive);
localTranslatorActive->addChild(geometryGroup);
SoFCDB::getStorage()->addChild(localTranslatorActive);
}
SoGroup* TDragger::buildGeometry()
{
// this builds one leg in the Y+ direction because of default done direction.
// the location anchor for shapes is the center of shape.
auto root = new SoGroup();
// cylinder
float cylinderHeight = 10.0;
float cylinderRadius = 0.1f;
auto cylinderSeparator = new SoSeparator();
root->addChild(cylinderSeparator);
auto cylinderLightModel = new SoLightModel();
cylinderLightModel->model = SoLightModel::BASE_COLOR;
cylinderSeparator->addChild(cylinderLightModel);
auto cylinderTranslation = new SoTranslation();
cylinderTranslation->translation.setValue(0.0, cylinderHeight / 2.0, 0.0);
cylinderSeparator->addChild(cylinderTranslation);
auto cylinder = new SoCylinder();
cylinder->radius.setValue(cylinderRadius);
cylinder->height.setValue(cylinderHeight);
cylinderSeparator->addChild(cylinder);
// cone
float coneBottomRadius = 0.8F;
float coneHeight = 2.5;
auto coneSeparator = new SoSeparator();
root->addChild(coneSeparator);
auto coneLightModel = new SoLightModel();
coneLightModel->model = SoLightModel::BASE_COLOR;
coneSeparator->addChild(coneLightModel);
auto pickStyle = new SoPickStyle();
pickStyle->style.setValue(SoPickStyle::SHAPE_ON_TOP);
pickStyle->setOverride(TRUE);
coneSeparator->addChild(pickStyle);
auto coneTranslation = new SoTranslation();
coneTranslation->translation.setValue(0.0, cylinderHeight + coneHeight / 2.0, 0.0);
coneSeparator->addChild(coneTranslation);
auto cone = new SoCone();
cone->bottomRadius.setValue(coneBottomRadius);
cone->height.setValue(coneHeight);
coneSeparator->addChild(cone);
return root;
}
void TDragger::startCB(void*, SoDragger* d)
{
auto sudoThis = static_cast<TDragger*>(d);
assert(sudoThis);
sudoThis->dragStart();
}
void TDragger::motionCB(void*, SoDragger* d)
{
auto sudoThis = static_cast<TDragger*>(d);
assert(sudoThis);
sudoThis->drag();
}
void TDragger::finishCB(void*, SoDragger* d)
{
auto sudoThis = static_cast<TDragger*>(d);
assert(sudoThis);
sudoThis->dragFinish();
}
void TDragger::fieldSensorCB(void* f, SoSensor*)
{
auto sudoThis = static_cast<TDragger*>(f);
if (!f) {
return;
}
SbMatrix matrix = sudoThis->getMotionMatrix(); // clazy:exclude=rule-of-two-soft
sudoThis->workFieldsIntoTransform(matrix);
sudoThis->setMotionMatrix(matrix);
}
void TDragger::valueChangedCB(void*, SoDragger* d)
{
auto sudoThis = dynamic_cast<TDragger*>(d);
assert(sudoThis);
SbMatrix matrix = sudoThis->getMotionMatrix(); // clazy:exclude=rule-of-two-soft
// all this just to get the translation?
SbVec3f trans, scaleDummy;
SbRotation rotationDummy, scaleOrientationDummy;
matrix.getTransform(trans, rotationDummy, scaleDummy, scaleOrientationDummy);
sudoThis->fieldSensor.detach();
if (sudoThis->translation.getValue() != trans) {
sudoThis->translation = trans;
}
sudoThis->fieldSensor.attach(&sudoThis->translation);
}
void TDragger::dragStart()
{
SoSwitch* sw;
sw = SO_GET_ANY_PART(this, "translatorSwitch", SoSwitch);
SoInteractionKit::setSwitchValue(sw, 1);
// do an initial projection to eliminate discrepancies
// in arrow head pick. we define the arrow in the y+ direction
// and we know local space will be relative to this. so y vector
// line projection will work.
projector.setViewVolume(this->getViewVolume());
projector.setWorkingSpace(this->getLocalToWorldMatrix());
projector.setLine(SbLine(SbVec3f(0.0, 0.0, 0.0), SbVec3f(0.0, 1.0, 0.0)));
SbVec3f hitPoint = projector.project(getNormalizedLocaterPosition());
projector.setLine(SbLine(SbVec3f(0.0, 0.0, 0.0), hitPoint));
SbMatrix localToWorld = getLocalToWorldMatrix();
localToWorld.multVecMatrix(hitPoint, hitPoint);
setStartingPoint((hitPoint));
translationIncrementCount.setValue(0);
}
void TDragger::drag()
{
projector.setViewVolume(this->getViewVolume());
projector.setWorkingSpace(this->getLocalToWorldMatrix());
SbVec3f hitPoint = projector.project(getNormalizedLocaterPosition());
SbVec3f startingPoint = getLocalStartingPoint();
SbVec3f localMovement = hitPoint - startingPoint;
// scale the increment to match local space.
float scaledIncrement =
static_cast<float>(translationIncrement.getValue()) / autoScaleResult.getValue();
localMovement = roundTranslation(localMovement, scaledIncrement);
// when the movement vector is null either the appendTranslation or
// the setMotionMatrix doesn't work. either way it stops translating
// back to its initial starting point.
if (localMovement.equals(SbVec3f(0.0, 0.0, 0.0), 0.00001f)) {
setMotionMatrix(getStartMotionMatrix());
// don't know why I need the following but if I don't have it
// it won't return to original position.
this->valueChanged();
}
else {
setMotionMatrix(appendTranslation(getStartMotionMatrix(), localMovement));
}
Base::Quantity quantity(static_cast<double>(translationIncrementCount.getValue())
* translationIncrement.getValue(),
Base::Unit::Length);
QString message =
QString::fromLatin1("%1 %2").arg(QObject::tr("Translation:"), quantity.getUserString());
getMainWindow()->showMessage(message, 3000);
}
void TDragger::dragFinish()
{
SoSwitch* sw;
sw = SO_GET_ANY_PART(this, "translatorSwitch", SoSwitch);
SoInteractionKit::setSwitchValue(sw, 0);
}
SbBool TDragger::setUpConnections(SbBool onoff, SbBool doitalways)
{
if (!doitalways && this->connectionsSetUp == onoff) {
return onoff;
}
SbBool oldval = this->connectionsSetUp;
if (onoff) {
inherited::setUpConnections(onoff, doitalways);
TDragger::fieldSensorCB(this, nullptr);
if (this->fieldSensor.getAttachedField() != &this->translation) {
this->fieldSensor.attach(&this->translation);
}
}
else {
if (this->fieldSensor.getAttachedField()) {
this->fieldSensor.detach();
}
inherited::setUpConnections(onoff, doitalways);
}
this->connectionsSetUp = onoff;
return oldval;
}
SbVec3f TDragger::roundTranslation(const SbVec3f& vecIn, float incrementIn)
{
// everything is transformed into local space. That means we only have
// worry about the y-value.
int yCount = 0;
float yValue = vecIn[1];
if (fabs(yValue) > (incrementIn / 2.0)) {
yCount = static_cast<int>(yValue / incrementIn);
float remainder = fmod(yValue, incrementIn);
if (remainder >= (incrementIn / 2.0)) {
yCount++;
}
}
translationIncrementCount.setValue(yCount);
SbVec3f out;
out[0] = 0;
out[1] = static_cast<float>(yCount) * incrementIn;
out[2] = 0.0;
return out;
}
SO_KIT_SOURCE(TPlanarDragger)
void TPlanarDragger::initClass()
{
SO_KIT_INIT_CLASS(TPlanarDragger, SoDragger, "Dragger");
}
TPlanarDragger::TPlanarDragger()
{
SO_KIT_CONSTRUCTOR(TPlanarDragger);
#if defined(Q_OS_MAC)
this->ref();
#endif
SO_KIT_ADD_CATALOG_ENTRY(planarTranslatorSwitch, SoSwitch, TRUE, geomSeparator, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(planarTranslator, SoSeparator, TRUE, planarTranslatorSwitch, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(planarTranslatorActive,
SoSeparator,
TRUE,
planarTranslatorSwitch,
"",
TRUE);
if (SO_KIT_IS_FIRST_INSTANCE()) {
buildFirstInstance();
}
SO_KIT_ADD_FIELD(translation, (0.0, 0.0, 0.0));
SO_KIT_ADD_FIELD(translationIncrement, (1.0));
SO_KIT_ADD_FIELD(translationIncrementXCount, (0));
SO_KIT_ADD_FIELD(translationIncrementYCount, (0));
SO_KIT_ADD_FIELD(autoScaleResult, (1.0));
SO_KIT_INIT_INSTANCE();
// initialize default parts.
// first is from 'SO_KIT_CATALOG_ENTRY_HEADER' macro
// second is unique name from buildFirstInstance().
this->setPartAsDefault("planarTranslator", "CSysDynamics_TPlanarDragger_Translator");
this->setPartAsDefault("planarTranslatorActive",
"CSysDynamics_TPlanarDragger_TranslatorActive");
SoSwitch* sw = SO_GET_ANY_PART(this, "planarTranslatorSwitch", SoSwitch);
SoInteractionKit::setSwitchValue(sw, 0);
this->addStartCallback(&TPlanarDragger::startCB);
this->addMotionCallback(&TPlanarDragger::motionCB);
this->addFinishCallback(&TPlanarDragger::finishCB);
addValueChangedCallback(&TPlanarDragger::valueChangedCB);
fieldSensor.setFunction(&TPlanarDragger::fieldSensorCB);
fieldSensor.setData(this);
fieldSensor.setPriority(0);
this->setUpConnections(TRUE, TRUE);
}
TPlanarDragger::~TPlanarDragger()
{
fieldSensor.setData(nullptr);
fieldSensor.detach();
this->removeStartCallback(&TPlanarDragger::startCB);
this->removeMotionCallback(&TPlanarDragger::motionCB);
this->removeFinishCallback(&TPlanarDragger::finishCB);
removeValueChangedCallback(&TPlanarDragger::valueChangedCB);
}
void TPlanarDragger::buildFirstInstance()
{
SoGroup* geometryGroup = buildGeometry();
auto localTranslator = new SoSeparator();
localTranslator->setName("CSysDynamics_TPlanarDragger_Translator");
localTranslator->addChild(geometryGroup);
SoFCDB::getStorage()->addChild(localTranslator);
auto localTranslatorActive = new SoSeparator();
localTranslatorActive->setName("CSysDynamics_TPlanarDragger_TranslatorActive");
auto colorActive = new SoBaseColor();
colorActive->rgb.setValue(1.0, 1.0, 0.0);
localTranslatorActive->addChild(colorActive);
localTranslatorActive->addChild(geometryGroup);
SoFCDB::getStorage()->addChild(localTranslatorActive);
}
SoGroup* TPlanarDragger::buildGeometry()
{
auto root = new SoGroup();
float cubeWidthHeight = 2.0;
float cubeDepth = 0.1f;
auto translation = new SoTranslation();
translation->translation.setValue(cubeWidthHeight + 0.15, cubeWidthHeight + 0.15, 0.0);
root->addChild(translation);
auto pickStyle = new SoPickStyle();
pickStyle->style.setValue(SoPickStyle::SHAPE_ON_TOP);
pickStyle->setOverride(TRUE);
root->addChild(pickStyle);
auto lightModel = new SoLightModel();
lightModel->model = SoLightModel::BASE_COLOR;
root->addChild(lightModel);
auto cube = new SoCube();
cube->width.setValue(cubeWidthHeight);
cube->height.setValue(cubeWidthHeight);
cube->depth.setValue(cubeDepth);
root->addChild(cube);
return root;
}
void TPlanarDragger::startCB(void*, SoDragger* d)
{
auto sudoThis = static_cast<TPlanarDragger*>(d);
assert(sudoThis);
sudoThis->dragStart();
}
void TPlanarDragger::motionCB(void*, SoDragger* d)
{
auto sudoThis = static_cast<TPlanarDragger*>(d);
assert(sudoThis);
sudoThis->drag();
}
void TPlanarDragger::finishCB(void*, SoDragger* d)
{
auto sudoThis = static_cast<TPlanarDragger*>(d);
assert(sudoThis);
sudoThis->dragFinish();
}
void TPlanarDragger::fieldSensorCB(void* f, SoSensor*)
{
auto sudoThis = static_cast<TPlanarDragger*>(f);
if (!f) {
return;
}
SbMatrix matrix = sudoThis->getMotionMatrix(); // clazy:exclude=rule-of-two-soft
sudoThis->workFieldsIntoTransform(matrix);
sudoThis->setMotionMatrix(matrix);
}
void TPlanarDragger::valueChangedCB(void*, SoDragger* d)
{
auto sudoThis = dynamic_cast<TPlanarDragger*>(d);
assert(sudoThis);
SbMatrix matrix = sudoThis->getMotionMatrix(); // clazy:exclude=rule-of-two-soft
// all this just to get the translation?
SbVec3f trans, scaleDummy;
SbRotation rotationDummy, scaleOrientationDummy;
matrix.getTransform(trans, rotationDummy, scaleDummy, scaleOrientationDummy);
sudoThis->fieldSensor.detach();
if (sudoThis->translation.getValue() != trans) {
sudoThis->translation = trans;
}
sudoThis->fieldSensor.attach(&sudoThis->translation);
}
void TPlanarDragger::dragStart()
{
SoSwitch* sw;
sw = SO_GET_ANY_PART(this, "planarTranslatorSwitch", SoSwitch);
SoInteractionKit::setSwitchValue(sw, 1);
projector.setViewVolume(this->getViewVolume());
projector.setWorkingSpace(this->getLocalToWorldMatrix());
projector.setPlane(
SbPlane(SbVec3f(0.0, 0.0, 0.0), SbVec3f(1.0, 0.0, 0.0), SbVec3f(0.0, 1.0, 0.0)));
SbVec3f hitPoint = projector.project(getNormalizedLocaterPosition());
SbMatrix localToWorld = getLocalToWorldMatrix();
localToWorld.multVecMatrix(hitPoint, hitPoint);
setStartingPoint((hitPoint));
translationIncrementXCount.setValue(0);
translationIncrementYCount.setValue(0);
}
void TPlanarDragger::drag()
{
projector.setViewVolume(this->getViewVolume());
projector.setWorkingSpace(this->getLocalToWorldMatrix());
SbVec3f hitPoint = projector.project(getNormalizedLocaterPosition());
SbVec3f startingPoint = getLocalStartingPoint();
SbVec3f localMovement = hitPoint - startingPoint;
// scale the increment to match local space.
float scaledIncrement =
static_cast<float>(translationIncrement.getValue()) / autoScaleResult.getValue();
localMovement = roundTranslation(localMovement, scaledIncrement);
// when the movement vector is null either the appendTranslation or
// the setMotionMatrix doesn't work. either way it stops translating
// back to its initial starting point.
if (localMovement.equals(SbVec3f(0.0, 0.0, 0.0), 0.00001f)) {
setMotionMatrix(getStartMotionMatrix());
// don't know why I need the following but if I don't have it
// it won't return to original position.
this->valueChanged();
}
else {
setMotionMatrix(appendTranslation(getStartMotionMatrix(), localMovement));
}
Base::Quantity quantityX(static_cast<double>(translationIncrementXCount.getValue())
* translationIncrement.getValue(),
Base::Unit::Length);
Base::Quantity quantityY(static_cast<double>(translationIncrementYCount.getValue())
* translationIncrement.getValue(),
Base::Unit::Length);
QString message = QString::fromLatin1("%1 %2, %3")
.arg(QObject::tr("Translation XY:"),
quantityX.getUserString(),
quantityY.getUserString());
getMainWindow()->showMessage(message, 3000);
}
void TPlanarDragger::dragFinish()
{
SoSwitch* sw;
sw = SO_GET_ANY_PART(this, "planarTranslatorSwitch", SoSwitch);
SoInteractionKit::setSwitchValue(sw, 0);
}
SbBool TPlanarDragger::setUpConnections(SbBool onoff, SbBool doitalways)
{
if (!doitalways && this->connectionsSetUp == onoff) {
return onoff;
}
SbBool oldval = this->connectionsSetUp;
if (onoff) {
inherited::setUpConnections(onoff, doitalways);
TPlanarDragger::fieldSensorCB(this, nullptr);
if (this->fieldSensor.getAttachedField() != &this->translation) {
this->fieldSensor.attach(&this->translation);
}
}
else {
if (this->fieldSensor.getAttachedField()) {
this->fieldSensor.detach();
}
inherited::setUpConnections(onoff, doitalways);
}
this->connectionsSetUp = onoff;
return oldval;
}
SbVec3f TPlanarDragger::roundTranslation(const SbVec3f& vecIn, float incrementIn)
{
int xCount = 0;
float xValue = vecIn[0];
if (fabs(xValue) > (incrementIn / 2.0)) {
xCount = static_cast<int>(xValue / incrementIn);
float remainder = fmod(xValue, incrementIn);
if (remainder >= (incrementIn / 2.0)) {
xCount++;
}
}
translationIncrementXCount.setValue(xCount);
int yCount = 0;
float yValue = vecIn[1];
if (fabs(yValue) > (incrementIn / 2.0)) {
yCount = static_cast<int>(yValue / incrementIn);
float remainder = fmod(yValue, incrementIn);
if (remainder >= (incrementIn / 2.0)) {
yCount++;
}
}
translationIncrementYCount.setValue(yCount);
SbVec3f out;
out[0] = static_cast<float>(xCount) * incrementIn;
out[1] = static_cast<float>(yCount) * incrementIn;
out[2] = 0.0;
return out;
}
SO_KIT_SOURCE(RDragger)
void RDragger::initClass()
{
SO_KIT_INIT_CLASS(RDragger, SoDragger, "Dragger");
}
RDragger::RDragger()
{
SO_KIT_CONSTRUCTOR(RDragger);
#if defined(Q_OS_MAC)
this->ref();
#endif
SO_KIT_ADD_CATALOG_ENTRY(rotatorSwitch, SoSwitch, TRUE, geomSeparator, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(rotator, SoSeparator, TRUE, rotatorSwitch, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(rotatorActive, SoSeparator, TRUE, rotatorSwitch, "", TRUE);
arcRadius = 8.0;
if (SO_KIT_IS_FIRST_INSTANCE()) {
buildFirstInstance();
}
SO_KIT_ADD_FIELD(rotation, (SbVec3f(0.0, 0.0, 1.0), 0.0));
SO_KIT_ADD_FIELD(rotationIncrement, (M_PI / 8.0));
SO_KIT_ADD_FIELD(rotationIncrementCount, (0));
SO_KIT_INIT_INSTANCE();
// initialize default parts.
// first is from 'SO_KIT_CATALOG_ENTRY_HEADER' macro
// second is unique name from buildFirstInstance().
this->setPartAsDefault("rotator", "CSysDynamics_RDragger_Rotator");
this->setPartAsDefault("rotatorActive", "CSysDynamics_RDragger_RotatorActive");
SoSwitch* sw = SO_GET_ANY_PART(this, "rotatorSwitch", SoSwitch);
SoInteractionKit::setSwitchValue(sw, 0);
this->addStartCallback(&RDragger::startCB);
this->addMotionCallback(&RDragger::motionCB);
this->addFinishCallback(&RDragger::finishCB);
addValueChangedCallback(&RDragger::valueChangedCB);
fieldSensor.setFunction(&RDragger::fieldSensorCB);
fieldSensor.setData(this);
fieldSensor.setPriority(0);
this->setUpConnections(TRUE, TRUE);
}
RDragger::~RDragger()
{
fieldSensor.setData(nullptr);
fieldSensor.detach();
this->removeStartCallback(&RDragger::startCB);
this->removeMotionCallback(&RDragger::motionCB);
this->removeFinishCallback(&RDragger::finishCB);
removeValueChangedCallback(&RDragger::valueChangedCB);
}
void RDragger::buildFirstInstance()
{
SoGroup* geometryGroup = buildGeometry();
auto localRotator = new SoSeparator();
localRotator->setName("CSysDynamics_RDragger_Rotator");
localRotator->addChild(geometryGroup);
SoFCDB::getStorage()->addChild(localRotator);
auto localRotatorActive = new SoSeparator();
localRotatorActive->setName("CSysDynamics_RDragger_RotatorActive");
auto colorActive = new SoBaseColor();
colorActive->rgb.setValue(1.0, 1.0, 0.0);
localRotatorActive->addChild(colorActive);
localRotatorActive->addChild(geometryGroup);
SoFCDB::getStorage()->addChild(localRotatorActive);
}
SoGroup* RDragger::buildGeometry()
{
auto root = new SoGroup();
// arc
auto coordinates = new SoCoordinate3();
unsigned int segments = 15;
float angleIncrement = static_cast<float>(M_PI / 2.0) / static_cast<float>(segments);
SbRotation rotation(SbVec3f(0.0, 0.0, 1.0), angleIncrement);
SbVec3f point(arcRadius, 0.0, 0.0);
for (unsigned int index = 0; index <= segments; ++index) {
coordinates->point.set1Value(index, point);
rotation.multVec(point, point);
}
root->addChild(coordinates);
auto drawStyle = new SoDrawStyle();
drawStyle->lineWidth = 4.0;
root->addChild(drawStyle);
auto lightModel = new SoLightModel();
lightModel->model = SoLightModel::BASE_COLOR;
root->addChild(lightModel);
auto lineSet = new SoLineSet();
lineSet->numVertices.setValue(segments + 1);
root->addChild(lineSet);
auto pickStyle = new SoPickStyle();
pickStyle->style.setValue(SoPickStyle::SHAPE_ON_TOP);
pickStyle->setOverride(TRUE);
root->addChild(pickStyle);
// sphere.
SbVec3f origin(1.0, 1.0, 0.0);
origin.normalize();
origin *= arcRadius;
auto sphereTranslation = new SoTranslation();
sphereTranslation->translation.setValue(origin);
root->addChild(sphereTranslation);
auto sphere = new SoSphere();
sphere->radius.setValue(0.8F);
root->addChild(sphere);
return root;
}
void RDragger::startCB(void*, SoDragger* d)
{
auto sudoThis = static_cast<RDragger*>(d);
assert(sudoThis);
sudoThis->dragStart();
}
void RDragger::motionCB(void*, SoDragger* d)
{
auto sudoThis = static_cast<RDragger*>(d);
assert(sudoThis);
sudoThis->drag();
}
void RDragger::finishCB(void*, SoDragger* d)
{
auto sudoThis = static_cast<RDragger*>(d);
assert(sudoThis);
sudoThis->dragFinish();
}
void RDragger::fieldSensorCB(void* f, SoSensor*)
{
auto sudoThis = static_cast<RDragger*>(f);
if (!f) {
return;
}
SbMatrix matrix = sudoThis->getMotionMatrix(); // clazy:exclude=rule-of-two-soft
sudoThis->workFieldsIntoTransform(matrix);
sudoThis->setMotionMatrix(matrix);
}
void RDragger::valueChangedCB(void*, SoDragger* d)
{
auto sudoThis = dynamic_cast<RDragger*>(d);
assert(sudoThis);
SbMatrix matrix = sudoThis->getMotionMatrix(); // clazy:exclude=rule-of-two-soft
// all this just to get the translation?
SbVec3f translationDummy, scaleDummy;
SbRotation localRotation, scaleOrientationDummy;
matrix.getTransform(translationDummy, localRotation, scaleDummy, scaleOrientationDummy);
sudoThis->fieldSensor.detach();
if (sudoThis->rotation.getValue() != localRotation) {
sudoThis->rotation = localRotation;
}
sudoThis->fieldSensor.attach(&sudoThis->rotation);
}
void RDragger::dragStart()
{
SoSwitch* sw;
sw = SO_GET_ANY_PART(this, "rotatorSwitch", SoSwitch);
SoInteractionKit::setSwitchValue(sw, 1);
projector.setViewVolume(this->getViewVolume());
projector.setWorkingSpace(this->getLocalToWorldMatrix());
projector.setPlane(SbPlane(SbVec3f(0.0, 0.0, 1.0), 0.0));
SbVec3f hitPoint;
if (!projector.tryProject(getNormalizedLocaterPosition(), 0.0, hitPoint)) {
return;
}
hitPoint.normalize();
SbMatrix localToWorld = getLocalToWorldMatrix();
localToWorld.multVecMatrix(hitPoint, hitPoint);
setStartingPoint((hitPoint));
rotationIncrementCount.setValue(0);
}
void RDragger::drag()
{
projector.setViewVolume(this->getViewVolume());
projector.setWorkingSpace(this->getLocalToWorldMatrix());
SbVec3f hitPoint;
if (!projector.tryProject(getNormalizedLocaterPosition(), 0.0, hitPoint)) {
return;
}
hitPoint.normalize();
SbVec3f startingPoint = getLocalStartingPoint();
startingPoint.normalize();
SbRotation localRotation(startingPoint, hitPoint);
// getting some slop from this. grab vector and put it absolute.
SbVec3f tempVec;
float tempRadians;
localRotation.getValue(tempVec, tempRadians);
tempVec[0] = 0.0;
tempVec[1] = 0.0;
tempVec.normalize();
if (tempVec[2] < 0.0) {
tempRadians *= -1.0;
tempVec.negate();
}
int incrementCount = roundIncrement(tempRadians);
rotationIncrementCount.setValue(incrementCount);
localRotation =
SbRotation(tempVec, incrementCount * static_cast<float>(rotationIncrement.getValue()));
// same problem as described in tDragger::drag.
if (localRotation.equals(SbRotation(SbVec3f(0.0, 0.0, 1.0), 0.0), 0.00001f)) {
setMotionMatrix(getStartMotionMatrix());
this->valueChanged();
}
else {
setMotionMatrix(
appendRotation(getStartMotionMatrix(), localRotation, SbVec3f(0.0, 0.0, 0.0)));
}
Base::Quantity quantity(static_cast<double>(rotationIncrementCount.getValue()) * (180.0 / M_PI)
* rotationIncrement.getValue(),
Base::Unit::Angle);
QString message =
QString::fromLatin1("%1 %2").arg(QObject::tr("Rotation:"), quantity.getUserString());
getMainWindow()->showMessage(message, 3000);
}
void RDragger::dragFinish()
{
SoSwitch* sw;
sw = SO_GET_ANY_PART(this, "rotatorSwitch", SoSwitch);
SoInteractionKit::setSwitchValue(sw, 0);
}
SbBool RDragger::setUpConnections(SbBool onoff, SbBool doitalways)
{
if (!doitalways && this->connectionsSetUp == onoff) {
return onoff;
}
SbBool oldval = this->connectionsSetUp;
if (onoff) {
inherited::setUpConnections(onoff, doitalways);
RDragger::fieldSensorCB(this, nullptr);
if (this->fieldSensor.getAttachedField() != &this->rotation) {
this->fieldSensor.attach(&this->rotation);
}
}
else {
if (this->fieldSensor.getAttachedField()) {
this->fieldSensor.detach();
}
inherited::setUpConnections(onoff, doitalways);
}
this->connectionsSetUp = onoff;
return oldval;
}
int RDragger::roundIncrement(const float& radiansIn)
{
int rCount = 0;
auto increment = static_cast<float>(rotationIncrement.getValue());
if (fabs(radiansIn) > (increment / 2.0)) {
rCount = static_cast<int>(radiansIn / increment);
float remainder = fmod(radiansIn, increment);
if (remainder >= (increment / 2.0)) {
rCount++;
}
}
return rCount;
}
SO_KIT_SOURCE(SoFCCSysDragger)
void SoFCCSysDragger::initClass()
{
TDragger::initClass();
TPlanarDragger::initClass();
RDragger::initClass();
SO_KIT_INIT_CLASS(SoFCCSysDragger, SoDragger, "Dragger");
}
SoFCCSysDragger::SoFCCSysDragger()
: axisScale(1.0f, 1.0f, 1.0f)
{
SO_KIT_CONSTRUCTOR(SoFCCSysDragger);
SO_KIT_ADD_CATALOG_ENTRY(annotation, So3DAnnotation, TRUE, geomSeparator, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(scaleNode, SoScale, TRUE, annotation, "", TRUE);
// Translator
SO_KIT_ADD_CATALOG_ENTRY(xTranslatorSwitch, SoSwitch, TRUE, annotation, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(yTranslatorSwitch, SoSwitch, TRUE, annotation, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(zTranslatorSwitch, SoSwitch, TRUE, annotation, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(xTranslatorSeparator, SoSeparator, TRUE, xTranslatorSwitch, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(yTranslatorSeparator, SoSeparator, TRUE, yTranslatorSwitch, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(zTranslatorSeparator, SoSeparator, TRUE, zTranslatorSwitch, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(xTranslatorColor, SoBaseColor, TRUE, xTranslatorSeparator, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(yTranslatorColor, SoBaseColor, TRUE, yTranslatorSeparator, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(zTranslatorColor, SoBaseColor, TRUE, zTranslatorSeparator, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(xTranslatorRotation, SoRotation, TRUE, xTranslatorSeparator, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(yTranslatorRotation, SoRotation, TRUE, yTranslatorSeparator, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(zTranslatorRotation, SoRotation, TRUE, zTranslatorSeparator, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(xTranslatorDragger, TDragger, TRUE, xTranslatorSeparator, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(yTranslatorDragger, TDragger, TRUE, yTranslatorSeparator, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(zTranslatorDragger, TDragger, TRUE, zTranslatorSeparator, "", TRUE);
// Planar Translator
SO_KIT_ADD_CATALOG_ENTRY(xyPlanarTranslatorSwitch, SoSwitch, TRUE, annotation, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(yzPlanarTranslatorSwitch, SoSwitch, TRUE, annotation, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(zxPlanarTranslatorSwitch, SoSwitch, TRUE, annotation, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(xyPlanarTranslatorSeparator,
SoSeparator,
TRUE,
xyPlanarTranslatorSwitch,
"",
TRUE);
SO_KIT_ADD_CATALOG_ENTRY(yzPlanarTranslatorSeparator,
SoSeparator,
TRUE,
yzPlanarTranslatorSwitch,
"",
TRUE);
SO_KIT_ADD_CATALOG_ENTRY(zxPlanarTranslatorSeparator,
SoSeparator,
TRUE,
zxPlanarTranslatorSwitch,
"",
TRUE);
SO_KIT_ADD_CATALOG_ENTRY(xyPlanarTranslatorColor,
SoBaseColor,
TRUE,
xyPlanarTranslatorSeparator,
"",
TRUE);
SO_KIT_ADD_CATALOG_ENTRY(yzPlanarTranslatorColor,
SoBaseColor,
TRUE,
yzPlanarTranslatorSeparator,
"",
TRUE);
SO_KIT_ADD_CATALOG_ENTRY(zxPlanarTranslatorColor,
SoBaseColor,
TRUE,
zxPlanarTranslatorSeparator,
"",
TRUE);
SO_KIT_ADD_CATALOG_ENTRY(xyPlanarTranslatorRotation,
SoRotation,
TRUE,
xyPlanarTranslatorSeparator,
"",
TRUE);
SO_KIT_ADD_CATALOG_ENTRY(yzPlanarTranslatorRotation,
SoRotation,
TRUE,
yzPlanarTranslatorSeparator,
"",
TRUE);
SO_KIT_ADD_CATALOG_ENTRY(zxPlanarTranslatorRotation,
SoRotation,
TRUE,
zxPlanarTranslatorSeparator,
"",
TRUE);
SO_KIT_ADD_CATALOG_ENTRY(xyPlanarTranslatorDragger,
TPlanarDragger,
TRUE,
xyPlanarTranslatorSeparator,
"",
TRUE);
SO_KIT_ADD_CATALOG_ENTRY(yzPlanarTranslatorDragger,
TPlanarDragger,
TRUE,
yzPlanarTranslatorSeparator,
"",
TRUE);
SO_KIT_ADD_CATALOG_ENTRY(zxPlanarTranslatorDragger,
TPlanarDragger,
TRUE,
zxPlanarTranslatorSeparator,
"",
TRUE);
// Rotator
SO_KIT_ADD_CATALOG_ENTRY(xRotatorSwitch, SoSwitch, TRUE, annotation, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(yRotatorSwitch, SoSwitch, TRUE, annotation, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(zRotatorSwitch, SoSwitch, TRUE, annotation, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(xRotatorSeparator, SoSeparator, TRUE, xRotatorSwitch, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(yRotatorSeparator, SoSeparator, TRUE, yRotatorSwitch, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(zRotatorSeparator, SoSeparator, TRUE, zRotatorSwitch, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(xRotatorColor, SoBaseColor, TRUE, xRotatorSeparator, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(yRotatorColor, SoBaseColor, TRUE, yRotatorSeparator, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(zRotatorColor, SoBaseColor, TRUE, zRotatorSeparator, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(xRotatorRotation, SoRotation, TRUE, xRotatorSeparator, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(yRotatorRotation, SoRotation, TRUE, yRotatorSeparator, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(zRotatorRotation, SoRotation, TRUE, zRotatorSeparator, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(xRotatorDragger, RDragger, TRUE, xRotatorSeparator, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(yRotatorDragger, RDragger, TRUE, yRotatorSeparator, "", TRUE);
SO_KIT_ADD_CATALOG_ENTRY(zRotatorDragger, RDragger, TRUE, zRotatorSeparator, "", TRUE);
// Other
SO_KIT_ADD_FIELD(translation, (0.0, 0.0, 0.0));
SO_KIT_ADD_FIELD(translationIncrement, (1.0));
SO_KIT_ADD_FIELD(translationIncrementCountX, (0));
SO_KIT_ADD_FIELD(translationIncrementCountY, (0));
SO_KIT_ADD_FIELD(translationIncrementCountZ, (0));
SO_KIT_ADD_FIELD(rotation, (SbVec3f(0.0, 0.0, 1.0), 0.0));
SO_KIT_ADD_FIELD(rotationIncrement, (M_PI / 8.0));
SO_KIT_ADD_FIELD(rotationIncrementCountX, (0));
SO_KIT_ADD_FIELD(rotationIncrementCountY, (0));
SO_KIT_ADD_FIELD(rotationIncrementCountZ, (0));
SO_KIT_ADD_FIELD(draggerSize, (1.0));
SO_KIT_ADD_FIELD(autoScaleResult, (1.0));
SO_KIT_INIT_INSTANCE();
// Colors
setAxisColors(SbColor(1.0, 0, 0).getPackedValue(0.0f),
SbColor(0, 1.0, 0).getPackedValue(0.0f),
SbColor(0, 0, 1.0).getPackedValue(0.0f));
// Increments
// Translator
TDragger* tDragger;
tDragger = SO_GET_ANY_PART(this, "xTranslatorDragger", TDragger);
tDragger->translationIncrement.connectFrom(&this->translationIncrement);
tDragger->autoScaleResult.connectFrom(&this->autoScaleResult);
translationIncrementCountX.connectFrom(&tDragger->translationIncrementCount);
tDragger = SO_GET_ANY_PART(this, "yTranslatorDragger", TDragger);
tDragger->translationIncrement.connectFrom(&this->translationIncrement);
tDragger->autoScaleResult.connectFrom(&this->autoScaleResult);
translationIncrementCountY.connectFrom(&tDragger->translationIncrementCount);
tDragger = SO_GET_ANY_PART(this, "zTranslatorDragger", TDragger);
tDragger->translationIncrement.connectFrom(&this->translationIncrement);
tDragger->autoScaleResult.connectFrom(&this->autoScaleResult);
translationIncrementCountZ.connectFrom(&tDragger->translationIncrementCount);
// Planar Translator
TPlanarDragger* tPlanarDragger;
tPlanarDragger = SO_GET_ANY_PART(this, "xyPlanarTranslatorDragger", TPlanarDragger);
tPlanarDragger->translationIncrement.connectFrom(&this->translationIncrement);
tPlanarDragger->autoScaleResult.connectFrom(&this->autoScaleResult);
translationIncrementCountX.appendConnection(&tPlanarDragger->translationIncrementXCount);
translationIncrementCountY.appendConnection(&tPlanarDragger->translationIncrementYCount);
tPlanarDragger = SO_GET_ANY_PART(this, "yzPlanarTranslatorDragger", TPlanarDragger);
tPlanarDragger->translationIncrement.connectFrom(&this->translationIncrement);
tPlanarDragger->autoScaleResult.connectFrom(&this->autoScaleResult);
translationIncrementCountZ.appendConnection(&tPlanarDragger->translationIncrementXCount);
translationIncrementCountY.appendConnection(&tPlanarDragger->translationIncrementYCount);
tPlanarDragger = SO_GET_ANY_PART(this, "zxPlanarTranslatorDragger", TPlanarDragger);
tPlanarDragger->translationIncrement.connectFrom(&this->translationIncrement);
tPlanarDragger->autoScaleResult.connectFrom(&this->autoScaleResult);
translationIncrementCountX.appendConnection(&tPlanarDragger->translationIncrementXCount);
translationIncrementCountZ.appendConnection(&tPlanarDragger->translationIncrementYCount);
// Rotator
RDragger* rDragger;
rDragger = SO_GET_ANY_PART(this, "xRotatorDragger", RDragger);
rDragger->rotationIncrement.connectFrom(&this->rotationIncrement);
rotationIncrementCountX.connectFrom(&rDragger->rotationIncrementCount);
rDragger = SO_GET_ANY_PART(this, "yRotatorDragger", RDragger);
rDragger->rotationIncrement.connectFrom(&this->rotationIncrement);
rotationIncrementCountY.connectFrom(&rDragger->rotationIncrementCount);
rDragger = SO_GET_ANY_PART(this, "zRotatorDragger", RDragger);
rDragger->rotationIncrement.connectFrom(&this->rotationIncrement);
rotationIncrementCountZ.connectFrom(&rDragger->rotationIncrementCount);
// Switches
// Translator
SoSwitch* sw = SO_GET_ANY_PART(this, "xTranslatorSwitch", SoSwitch);
SoInteractionKit::setSwitchValue(sw, SO_SWITCH_ALL);
sw = SO_GET_ANY_PART(this, "yTranslatorSwitch", SoSwitch);
SoInteractionKit::setSwitchValue(sw, SO_SWITCH_ALL);
sw = SO_GET_ANY_PART(this, "zTranslatorSwitch", SoSwitch);
SoInteractionKit::setSwitchValue(sw, SO_SWITCH_ALL);
// Planar Translator
sw = SO_GET_ANY_PART(this, "xyPlanarTranslatorSwitch", SoSwitch);
SoInteractionKit::setSwitchValue(sw, SO_SWITCH_ALL);
sw = SO_GET_ANY_PART(this, "yzPlanarTranslatorSwitch", SoSwitch);
SoInteractionKit::setSwitchValue(sw, SO_SWITCH_ALL);
sw = SO_GET_ANY_PART(this, "zxPlanarTranslatorSwitch", SoSwitch);
SoInteractionKit::setSwitchValue(sw, SO_SWITCH_ALL);
// Rotator
sw = SO_GET_ANY_PART(this, "xRotatorSwitch", SoSwitch);
SoInteractionKit::setSwitchValue(sw, SO_SWITCH_ALL);
sw = SO_GET_ANY_PART(this, "yRotatorSwitch", SoSwitch);
SoInteractionKit::setSwitchValue(sw, SO_SWITCH_ALL);
sw = SO_GET_ANY_PART(this, "zRotatorSwitch", SoSwitch);
SoInteractionKit::setSwitchValue(sw, SO_SWITCH_ALL);
// Rotations
SoRotation* localRotation;
SbRotation tempRotation;
auto angle = static_cast<float>(M_PI / 2.0);
// Translator
localRotation = SO_GET_ANY_PART(this, "xTranslatorRotation", SoRotation);
localRotation->rotation.setValue(SbVec3f(0.0, 0.0, -1.0), angle);
localRotation = SO_GET_ANY_PART(this, "yTranslatorRotation", SoRotation);
localRotation->rotation.setValue(SbRotation::identity());
localRotation = SO_GET_ANY_PART(this, "zTranslatorRotation", SoRotation);
localRotation->rotation.setValue(SbVec3f(1.0, 0.0, 0.0), angle);
// Planar Translator
localRotation = SO_GET_ANY_PART(this, "xyPlanarTranslatorRotation", SoRotation);
localRotation->rotation.setValue(SbRotation::identity());
localRotation = SO_GET_ANY_PART(this, "yzPlanarTranslatorRotation", SoRotation);
localRotation->rotation.setValue(SbVec3f(0.0, -1.0, 0.0), angle);
localRotation = SO_GET_ANY_PART(this, "zxPlanarTranslatorRotation", SoRotation);
localRotation->rotation.setValue(SbVec3f(1.0, 0.0, 0.0), angle);
// Rotator
localRotation = SO_GET_ANY_PART(this, "xRotatorRotation", SoRotation);
tempRotation = SbRotation(SbVec3f(1.0, 0.0, 0.0), angle);
tempRotation *= SbRotation(SbVec3f(0.0, 0.0, 1.0), angle);
localRotation->rotation.setValue(tempRotation);
localRotation = SO_GET_ANY_PART(this, "yRotatorRotation", SoRotation);
tempRotation = SbRotation(SbVec3f(0.0, -1.0, 0.0), angle);
tempRotation *= SbRotation(SbVec3f(0.0, 0.0, -1.0), angle);
localRotation->rotation.setValue(tempRotation);
localRotation = SO_GET_ANY_PART(this, "zRotatorRotation", SoRotation);
localRotation->rotation.setValue(SbRotation::identity());
// this is for non-autoscale mode. this will be disconnected for autoscale
// and won't be used. see setUpAutoScale.
auto scaleEngine = new SoComposeVec3f(); // uses coin ref scheme.
scaleEngine->x.connectFrom(&draggerSize);
scaleEngine->y.connectFrom(&draggerSize);
scaleEngine->z.connectFrom(&draggerSize);
SoScale* localScaleNode = SO_GET_ANY_PART(this, "scaleNode", SoScale);
localScaleNode->scaleFactor.connectFrom(&scaleEngine->vector);
autoScaleResult.connectFrom(&draggerSize);
addValueChangedCallback(&SoFCCSysDragger::valueChangedCB);
translationSensor.setFunction(&SoFCCSysDragger::translationSensorCB);
translationSensor.setData(this);
translationSensor.setPriority(0);
rotationSensor.setFunction(&SoFCCSysDragger::rotationSensorCB);
rotationSensor.setData(this);
rotationSensor.setPriority(0);
cameraSensor.setFunction(&SoFCCSysDragger::cameraCB);
cameraSensor.setData(this);
idleSensor.setFunction(&SoFCCSysDragger::idleCB);
idleSensor.setData(this);
this->addFinishCallback(&SoFCCSysDragger::finishDragCB, this);
this->setUpConnections(TRUE, TRUE);
}
SoFCCSysDragger::~SoFCCSysDragger()
{
translationSensor.setData(nullptr);
translationSensor.detach();
rotationSensor.setData(nullptr);
rotationSensor.detach();
cameraSensor.setData(nullptr);
cameraSensor.detach();
idleSensor.setData(nullptr);
idleSensor.unschedule();
removeValueChangedCallback(&SoFCCSysDragger::valueChangedCB);
removeFinishCallback(&SoFCCSysDragger::finishDragCB, this);
}
SbBool SoFCCSysDragger::setUpConnections(SbBool onoff, SbBool doitalways)
{
if (!doitalways && (connectionsSetUp == onoff)) {
return onoff;
}
TDragger* tDraggerX = SO_GET_ANY_PART(this, "xTranslatorDragger", TDragger);
TDragger* tDraggerY = SO_GET_ANY_PART(this, "yTranslatorDragger", TDragger);
TDragger* tDraggerZ = SO_GET_ANY_PART(this, "zTranslatorDragger", TDragger);
TPlanarDragger* tPlanarDraggerXZ =
SO_GET_ANY_PART(this, "xyPlanarTranslatorDragger", TPlanarDragger);
TPlanarDragger* tPlanarDraggerYZ =
SO_GET_ANY_PART(this, "yzPlanarTranslatorDragger", TPlanarDragger);
TPlanarDragger* tPlanarDraggerZX =
SO_GET_ANY_PART(this, "zxPlanarTranslatorDragger", TPlanarDragger);
RDragger* rDraggerX = SO_GET_ANY_PART(this, "xRotatorDragger", RDragger);
RDragger* rDraggerY = SO_GET_ANY_PART(this, "yRotatorDragger", RDragger);
RDragger* rDraggerZ = SO_GET_ANY_PART(this, "zRotatorDragger", RDragger);
if (onoff) {
inherited::setUpConnections(onoff, doitalways);
registerChildDragger(tDraggerX);
registerChildDragger(tDraggerY);
registerChildDragger(tDraggerZ);
registerChildDragger(tPlanarDraggerXZ);
registerChildDragger(tPlanarDraggerYZ);
registerChildDragger(tPlanarDraggerZX);
registerChildDragger(rDraggerX);
registerChildDragger(rDraggerY);
registerChildDragger(rDraggerZ);
translationSensorCB(this, nullptr);
if (this->translationSensor.getAttachedField() != &this->translation) {
this->translationSensor.attach(&this->translation);
}
rotationSensorCB(this, nullptr);
if (this->rotationSensor.getAttachedField() != &this->rotation) {
this->rotationSensor.attach(&this->rotation);
}
}
else {
unregisterChildDragger(tDraggerX);
unregisterChildDragger(tDraggerY);
unregisterChildDragger(tDraggerZ);
unregisterChildDragger(tPlanarDraggerXZ);
unregisterChildDragger(tPlanarDraggerYZ);
unregisterChildDragger(tPlanarDraggerZX);
unregisterChildDragger(rDraggerX);
unregisterChildDragger(rDraggerY);
unregisterChildDragger(rDraggerZ);
inherited::setUpConnections(onoff, doitalways);
if (this->translationSensor.getAttachedField()) {
this->translationSensor.detach();
}
if (this->rotationSensor.getAttachedField()) {
this->rotationSensor.detach();
}
}
return !(this->connectionsSetUp = onoff);
}
void SoFCCSysDragger::translationSensorCB(void* f, SoSensor*)
{
auto sudoThis = static_cast<SoFCCSysDragger*>(f);
if (!f) {
return;
}
SbMatrix matrix = sudoThis->getMotionMatrix(); // clazy:exclude=rule-of-two-soft
sudoThis->workFieldsIntoTransform(matrix);
sudoThis->setMotionMatrix(matrix);
}
void SoFCCSysDragger::rotationSensorCB(void* f, SoSensor*)
{
auto sudoThis = static_cast<SoFCCSysDragger*>(f);
if (!f) {
return;
}
SbMatrix matrix = sudoThis->getMotionMatrix(); // clazy:exclude=rule-of-two-soft
sudoThis->workFieldsIntoTransform(matrix);
sudoThis->setMotionMatrix(matrix);
}
void SoFCCSysDragger::valueChangedCB(void*, SoDragger* d)
{
auto sudoThis = dynamic_cast<SoFCCSysDragger*>(d);
assert(sudoThis);
SbMatrix matrix = sudoThis->getMotionMatrix(); // clazy:exclude=rule-of-two-soft
// all this just to get the translation?
SbVec3f localTranslation, scaleDummy;
SbRotation localRotation, scaleOrientationDummy;
matrix.getTransform(localTranslation, localRotation, scaleDummy, scaleOrientationDummy);
sudoThis->translationSensor.detach();
if (sudoThis->translation.getValue() != localTranslation) {
sudoThis->translation = localTranslation;
}
sudoThis->translationSensor.attach(&sudoThis->translation);
sudoThis->rotationSensor.detach();
if (sudoThis->rotation.getValue() != localRotation) {
sudoThis->rotation = localRotation;
}
sudoThis->rotationSensor.attach(&sudoThis->rotation);
}
void SoFCCSysDragger::setUpAutoScale(SoCamera* cameraIn)
{
// note: sofieldsensor checks if the current sensor is already attached
// and takes appropriate action. So it is safe to attach to a field without
// checking current attachment state.
if (cameraIn->getTypeId() == SoOrthographicCamera::getClassTypeId()) {
auto localCamera = dynamic_cast<SoOrthographicCamera*>(cameraIn);
assert(localCamera);
cameraSensor.attach(&localCamera->height);
SoScale* localScaleNode = SO_GET_ANY_PART(this, "scaleNode", SoScale);
localScaleNode->scaleFactor.disconnect();
autoScaleResult.disconnect(&draggerSize);
cameraCB(this, nullptr);
}
else if (cameraIn->getTypeId() == SoPerspectiveCamera::getClassTypeId()) {
auto localCamera = dynamic_cast<SoPerspectiveCamera*>(cameraIn);
assert(localCamera);
cameraSensor.attach(&localCamera->position);
SoScale* localScaleNode = SO_GET_ANY_PART(this, "scaleNode", SoScale);
localScaleNode->scaleFactor.disconnect();
autoScaleResult.disconnect(&draggerSize);
cameraCB(this, nullptr);
}
}
void SoFCCSysDragger::cameraCB(void* data, SoSensor*)
{
auto sudoThis = static_cast<SoFCCSysDragger*>(data);
if (!sudoThis) {
return;
}
if (!sudoThis->idleSensor.isScheduled()) {
sudoThis->idleSensor.schedule();
}
}
void SoFCCSysDragger::GLRender(SoGLRenderAction* action)
{
if (!scaleInited) {
scaleInited = true;
updateDraggerCache(action->getCurPath());
updateAxisScale();
}
inherited::GLRender(action);
}
void SoFCCSysDragger::updateAxisScale()
{
SbMatrix localToWorld = getLocalToWorldMatrix();
SbVec3f origin;
localToWorld.multVecMatrix(SbVec3f(0.0, 0.0, 0.0), origin);
SbVec3f vx, vy, vz;
localToWorld.multVecMatrix(SbVec3f(1.0f, 0.0f, 0.0f), vx);
localToWorld.multVecMatrix(SbVec3f(0.0f, 1.0f, 0.0f), vy);
localToWorld.multVecMatrix(SbVec3f(0.0f, 0.0f, 1.0f), vz);
float x = std::max((vx - origin).length(), 1e-7f);
float y = std::max((vy - origin).length(), 1e-7f);
float z = std::max((vz - origin).length(), 1e-7f);
if (!axisScale.equals(SbVec3f(x, y, z), 1e-7f)) {
axisScale.setValue(x, y, z);
idleCB(this, &idleSensor);
}
}
void SoFCCSysDragger::handleEvent(SoHandleEventAction* action)
{
this->ref();
inherited::handleEvent(action);
updateAxisScale();
this->unref();
}
void SoFCCSysDragger::idleCB(void* data, SoSensor*)
{
auto sudoThis = static_cast<SoFCCSysDragger*>(data);
if (!data) {
return;
}
SoField* field = sudoThis->cameraSensor.getAttachedField();
if (field) {
auto camera = static_cast<SoCamera*>(field->getContainer());
SbMatrix localToWorld = sudoThis->getLocalToWorldMatrix();
SbVec3f origin;
localToWorld.multVecMatrix(SbVec3f(0.0, 0.0, 0.0), origin);
SbViewVolume viewVolume = camera->getViewVolume();
float radius = sudoThis->draggerSize.getValue() / 2.0;
float localScale = viewVolume.getWorldToScreenScale(origin, radius);
float sx, sy, sz;
sudoThis->axisScale.getValue(sx, sy, sz);
SbVec3f scaleVector(localScale / sx, localScale / sy, localScale / sz);
SoScale* localScaleNode = SO_GET_ANY_PART(sudoThis, "scaleNode", SoScale);
localScaleNode->scaleFactor.setValue(scaleVector);
sudoThis->autoScaleResult.setValue(localScale);
}
}
void SoFCCSysDragger::finishDragCB(void* data, SoDragger*)
{
auto sudoThis = static_cast<SoFCCSysDragger*>(data);
assert(sudoThis);
// note: when creating a second view of the document and then closing
// the first viewer it deletes the camera. However, the attached field
// of the cameraSensor will be detached automatically.
SoField* field = sudoThis->cameraSensor.getAttachedField();
if (field) {
auto camera = static_cast<SoCamera*>(field->getContainer());
if (camera->getTypeId() == SoPerspectiveCamera::getClassTypeId()) {
cameraCB(sudoThis, nullptr);
}
}
}
void SoFCCSysDragger::clearIncrementCounts()
{
translationIncrementCountX.setValue(0);
translationIncrementCountY.setValue(0);
translationIncrementCountZ.setValue(0);
rotationIncrementCountX.setValue(0);
rotationIncrementCountY.setValue(0);
rotationIncrementCountZ.setValue(0);
}
void SoFCCSysDragger::setAxisColors(unsigned long x, unsigned long y, unsigned long z)
{
SbColor colorX;
SbColor colorY;
SbColor colorZ;
float t = 0.0f;
colorX.setPackedValue(x, t);
colorY.setPackedValue(y, t);
colorZ.setPackedValue(z, t);
SoBaseColor* color;
// Translator
color = SO_GET_ANY_PART(this, "xTranslatorColor", SoBaseColor);
color->rgb.setValue(colorX[0], colorX[1], colorX[2]);
color = SO_GET_ANY_PART(this, "yTranslatorColor", SoBaseColor);
color->rgb.setValue(colorY[0], colorY[1], colorY[2]);
color = SO_GET_ANY_PART(this, "zTranslatorColor", SoBaseColor);
color->rgb.setValue(colorZ[0], colorZ[1], colorZ[2]);
// Planar Translator
color = SO_GET_ANY_PART(this, "xyPlanarTranslatorColor", SoBaseColor);
color->rgb.setValue(colorZ[0], colorZ[1], colorZ[2]);
color = SO_GET_ANY_PART(this, "yzPlanarTranslatorColor", SoBaseColor);
color->rgb.setValue(colorX[0], colorX[1], colorX[2]);
color = SO_GET_ANY_PART(this, "zxPlanarTranslatorColor", SoBaseColor);
color->rgb.setValue(colorY[0], colorY[1], colorY[2]);
// Rotator
color = SO_GET_ANY_PART(this, "xRotatorColor", SoBaseColor);
color->rgb.setValue(colorX[0], colorX[1], colorX[2]);
color = SO_GET_ANY_PART(this, "yRotatorColor", SoBaseColor);
color->rgb.setValue(colorY[0], colorY[1], colorY[2]);
color = SO_GET_ANY_PART(this, "zRotatorColor", SoBaseColor);
color->rgb.setValue(colorZ[0], colorZ[1], colorZ[2]);
}
// Visibility API Functions
// Translator
void SoFCCSysDragger::showTranslationX()
{
SoSwitch* sw = SO_GET_ANY_PART(this, "xTranslatorSwitch", SoSwitch);
SoInteractionKit::setSwitchValue(sw, SO_SWITCH_ALL);
}
void SoFCCSysDragger::showTranslationY()
{
SoSwitch* sw = SO_GET_ANY_PART(this, "yTranslatorSwitch", SoSwitch);
SoInteractionKit::setSwitchValue(sw, SO_SWITCH_ALL);
}
void SoFCCSysDragger::showTranslationZ()
{
SoSwitch* sw = SO_GET_ANY_PART(this, "zTranslatorSwitch", SoSwitch);
SoInteractionKit::setSwitchValue(sw, SO_SWITCH_ALL);
}
void SoFCCSysDragger::hideTranslationX()
{
SoSwitch* sw = SO_GET_ANY_PART(this, "xTranslatorSwitch", SoSwitch);
SoInteractionKit::setSwitchValue(sw, SO_SWITCH_NONE);
}
void SoFCCSysDragger::hideTranslationY()
{
SoSwitch* sw = SO_GET_ANY_PART(this, "yTranslatorSwitch", SoSwitch);
SoInteractionKit::setSwitchValue(sw, SO_SWITCH_NONE);
}
void SoFCCSysDragger::hideTranslationZ()
{
SoSwitch* sw = SO_GET_ANY_PART(this, "zTranslatorSwitch", SoSwitch);
SoInteractionKit::setSwitchValue(sw, SO_SWITCH_NONE);
}
bool SoFCCSysDragger::isShownTranslationX()
{
SoSwitch* sw = SO_GET_ANY_PART(this, "xTranslatorSwitch", SoSwitch);
return (sw->whichChild.getValue() == SO_SWITCH_ALL);
}
bool SoFCCSysDragger::isShownTranslationY()
{
SoSwitch* sw = SO_GET_ANY_PART(this, "yTranslatorSwitch", SoSwitch);
return (sw->whichChild.getValue() == SO_SWITCH_ALL);
}
bool SoFCCSysDragger::isShownTranslationZ()
{
SoSwitch* sw = SO_GET_ANY_PART(this, "zTranslatorSwitch", SoSwitch);
return (sw->whichChild.getValue() == SO_SWITCH_ALL);
}
bool SoFCCSysDragger::isHiddenTranslationX()
{
SoSwitch* sw = SO_GET_ANY_PART(this, "xTranslatorSwitch", SoSwitch);
return (sw->whichChild.getValue() == SO_SWITCH_NONE);
}
bool SoFCCSysDragger::isHiddenTranslationY()
{
SoSwitch* sw = SO_GET_ANY_PART(this, "yTranslatorSwitch", SoSwitch);
return (sw->whichChild.getValue() == SO_SWITCH_NONE);
}
bool SoFCCSysDragger::isHiddenTranslationZ()
{
SoSwitch* sw = SO_GET_ANY_PART(this, "zTranslatorSwitch", SoSwitch);
return (sw->whichChild.getValue() == SO_SWITCH_NONE);
}
// Planar Translator
void SoFCCSysDragger::showPlanarTranslationXY()
{
SoSwitch* sw = SO_GET_ANY_PART(this, "xyPlanarTranslatorSwitch", SoSwitch);
SoInteractionKit::setSwitchValue(sw, SO_SWITCH_ALL);
}
void SoFCCSysDragger::showPlanarTranslationYZ()
{
SoSwitch* sw = SO_GET_ANY_PART(this, "yzPlanarTranslatorSwitch", SoSwitch);
SoInteractionKit::setSwitchValue(sw, SO_SWITCH_ALL);
}
void SoFCCSysDragger::showPlanarTranslationZX()
{
SoSwitch* sw = SO_GET_ANY_PART(this, "zxPlanarTranslatorSwitch", SoSwitch);
SoInteractionKit::setSwitchValue(sw, SO_SWITCH_ALL);
}
void SoFCCSysDragger::hidePlanarTranslationXY()
{
SoSwitch* sw = SO_GET_ANY_PART(this, "xyPlanarTranslatorSwitch", SoSwitch);
SoInteractionKit::setSwitchValue(sw, SO_SWITCH_NONE);
}
void SoFCCSysDragger::hidePlanarTranslationYZ()
{
SoSwitch* sw = SO_GET_ANY_PART(this, "yzPlanarTranslatorSwitch", SoSwitch);
SoInteractionKit::setSwitchValue(sw, SO_SWITCH_NONE);
}
void SoFCCSysDragger::hidePlanarTranslationZX()
{
SoSwitch* sw = SO_GET_ANY_PART(this, "zxPlanarTranslatorSwitch", SoSwitch);
SoInteractionKit::setSwitchValue(sw, SO_SWITCH_NONE);
}
bool SoFCCSysDragger::isShownPlanarTranslationXY()
{
SoSwitch* sw = SO_GET_ANY_PART(this, "xyPlanarTranslatorSwitch", SoSwitch);
return (sw->whichChild.getValue() == SO_SWITCH_ALL);
}
bool SoFCCSysDragger::isShownPlanarTranslationYZ()
{
SoSwitch* sw = SO_GET_ANY_PART(this, "yzPlanarTranslatorSwitch", SoSwitch);
return (sw->whichChild.getValue() == SO_SWITCH_ALL);
}
bool SoFCCSysDragger::isShownPlanarTranslationZX()
{
SoSwitch* sw = SO_GET_ANY_PART(this, "zxPlanarTranslatorSwitch", SoSwitch);
return (sw->whichChild.getValue() == SO_SWITCH_ALL);
}
bool SoFCCSysDragger::isHiddenPlanarTranslationXY()
{
SoSwitch* sw = SO_GET_ANY_PART(this, "xyPlanarTranslatorSwitch", SoSwitch);
return (sw->whichChild.getValue() == SO_SWITCH_NONE);
}
bool SoFCCSysDragger::isHiddenPlanarTranslationYZ()
{
SoSwitch* sw = SO_GET_ANY_PART(this, "yzPlanarTranslatorSwitch", SoSwitch);
return (sw->whichChild.getValue() == SO_SWITCH_NONE);
}
bool SoFCCSysDragger::isHiddenPlanarTranslationZX()
{
SoSwitch* sw = SO_GET_ANY_PART(this, "zxPlanarTranslatorSwitch", SoSwitch);
return (sw->whichChild.getValue() == SO_SWITCH_NONE);
}
// Rotator
void SoFCCSysDragger::showRotationX()
{
SoSwitch* sw = SO_GET_ANY_PART(this, "xRotatorSwitch", SoSwitch);
SoInteractionKit::setSwitchValue(sw, SO_SWITCH_ALL);
}
void SoFCCSysDragger::showRotationY()
{
SoSwitch* sw = SO_GET_ANY_PART(this, "yRotatorSwitch", SoSwitch);
SoInteractionKit::setSwitchValue(sw, SO_SWITCH_ALL);
}
void SoFCCSysDragger::showRotationZ()
{
SoSwitch* sw = SO_GET_ANY_PART(this, "zRotatorSwitch", SoSwitch);
SoInteractionKit::setSwitchValue(sw, SO_SWITCH_ALL);
}
void SoFCCSysDragger::hideRotationX()
{
SoSwitch* sw = SO_GET_ANY_PART(this, "xRotatorSwitch", SoSwitch);
SoInteractionKit::setSwitchValue(sw, SO_SWITCH_NONE);
}
void SoFCCSysDragger::hideRotationY()
{
SoSwitch* sw = SO_GET_ANY_PART(this, "yRotatorSwitch", SoSwitch);
SoInteractionKit::setSwitchValue(sw, SO_SWITCH_NONE);
}
void SoFCCSysDragger::hideRotationZ()
{
SoSwitch* sw = SO_GET_ANY_PART(this, "zRotatorSwitch", SoSwitch);
SoInteractionKit::setSwitchValue(sw, SO_SWITCH_NONE);
}
bool SoFCCSysDragger::isShownRotationX()
{
SoSwitch* sw = SO_GET_ANY_PART(this, "xRotatorSwitch", SoSwitch);
return (sw->whichChild.getValue() == SO_SWITCH_ALL);
}
bool SoFCCSysDragger::isShownRotationY()
{
SoSwitch* sw = SO_GET_ANY_PART(this, "yRotatorSwitch", SoSwitch);
return (sw->whichChild.getValue() == SO_SWITCH_ALL);
}
bool SoFCCSysDragger::isShownRotationZ()
{
SoSwitch* sw = SO_GET_ANY_PART(this, "zRotatorSwitch", SoSwitch);
return (sw->whichChild.getValue() == SO_SWITCH_ALL);
}
bool SoFCCSysDragger::isHiddenRotationX()
{
SoSwitch* sw = SO_GET_ANY_PART(this, "xRotatorSwitch", SoSwitch);
return (sw->whichChild.getValue() == SO_SWITCH_NONE);
}
bool SoFCCSysDragger::isHiddenRotationY()
{
SoSwitch* sw = SO_GET_ANY_PART(this, "yRotatorSwitch", SoSwitch);
return (sw->whichChild.getValue() == SO_SWITCH_NONE);
}
bool SoFCCSysDragger::isHiddenRotationZ()
{
SoSwitch* sw = SO_GET_ANY_PART(this, "zRotatorSwitch", SoSwitch);
return (sw->whichChild.getValue() == SO_SWITCH_NONE);
}