Files
create/src/Gui/Inventor/Draggers/SoPlanarDragger.cpp
2025-12-24 22:05:07 -06:00

356 lines
12 KiB
C++

// SPDX-License-Identifier: LGPL-2.1-or-later
/***************************************************************************
* 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 <cassert>
#include <numbers>
#include <Inventor/SbRotation.h>
#include <Inventor/actions/SoGLRenderAction.h>
#include <Inventor/engines/SoComposeVec3f.h>
#include <Inventor/nodes/SoLightModel.h>
#include <Inventor/nodes/SoDrawStyle.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>
#include <Inventor/nodes/SoText2.h>
#include <Inventor/nodes/SoAnnotation.h>
#include <Inventor/nodes/SoFontStyle.h>
#include <Base/Quantity.h>
#include "SoPlanarDragger.h"
#include "MainWindow.h"
#include "SoFCDB.h"
#include <SoTextLabel.h>
using namespace Gui;
SO_KIT_SOURCE(SoPlanarDragger)
void SoPlanarDragger::initClass()
{
SO_KIT_INIT_CLASS(SoPlanarDragger, SoDragger, "Dragger");
}
SoPlanarDragger::SoPlanarDragger()
{
SO_KIT_CONSTRUCTOR(SoPlanarDragger);
#if defined(Q_OS_MACOS) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
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(&SoPlanarDragger::startCB);
this->addMotionCallback(&SoPlanarDragger::motionCB);
this->addFinishCallback(&SoPlanarDragger::finishCB);
addValueChangedCallback(&SoPlanarDragger::valueChangedCB);
fieldSensor.setFunction(&SoPlanarDragger::fieldSensorCB);
fieldSensor.setData(this);
fieldSensor.setPriority(0);
this->setUpConnections(TRUE, TRUE);
}
SoPlanarDragger::~SoPlanarDragger()
{
fieldSensor.setData(nullptr);
fieldSensor.detach();
this->removeStartCallback(&SoPlanarDragger::startCB);
this->removeMotionCallback(&SoPlanarDragger::motionCB);
this->removeFinishCallback(&SoPlanarDragger::finishCB);
removeValueChangedCallback(&SoPlanarDragger::valueChangedCB);
}
void SoPlanarDragger::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* SoPlanarDragger::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 SoPlanarDragger::startCB(void*, SoDragger* d)
{
auto sudoThis = static_cast<SoPlanarDragger*>(d);
assert(sudoThis);
sudoThis->dragStart();
}
void SoPlanarDragger::motionCB(void*, SoDragger* d)
{
auto sudoThis = static_cast<SoPlanarDragger*>(d);
assert(sudoThis);
sudoThis->drag();
}
void SoPlanarDragger::finishCB(void*, SoDragger* d)
{
auto sudoThis = static_cast<SoPlanarDragger*>(d);
assert(sudoThis);
sudoThis->dragFinish();
}
void SoPlanarDragger::fieldSensorCB(void* f, SoSensor*)
{
auto sudoThis = static_cast<SoPlanarDragger*>(f);
if (!f) {
return;
}
SbMatrix matrix = sudoThis->getMotionMatrix(); // clazy:exclude=rule-of-two-soft
sudoThis->workFieldsIntoTransform(matrix);
sudoThis->setMotionMatrix(matrix);
}
void SoPlanarDragger::valueChangedCB(void*, SoDragger* d)
{
auto sudoThis = dynamic_cast<SoPlanarDragger*>(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 SoPlanarDragger::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 SoPlanarDragger::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 = QStringLiteral("%1 %2, %3")
.arg(
QObject::tr("Translation XY:"),
QString::fromStdString(quantityX.getUserString()),
QString::fromStdString(quantityY.getUserString())
);
getMainWindow()->showMessage(message, 3000);
}
void SoPlanarDragger::dragFinish()
{
SoSwitch* sw;
sw = SO_GET_ANY_PART(this, "planarTranslatorSwitch", SoSwitch);
SoInteractionKit::setSwitchValue(sw, 0);
}
SbBool SoPlanarDragger::setUpConnections(SbBool onoff, SbBool doitalways)
{
if (!doitalways && this->connectionsSetUp == onoff) {
return onoff;
}
SbBool oldval = this->connectionsSetUp;
if (onoff) {
inherited::setUpConnections(onoff, doitalways);
SoPlanarDragger::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 SoPlanarDragger::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;
}