FreeCAD / src /Gui /Inventor /SoFCPlacementIndicatorKit.cpp
AbdulElahGwaith's picture
Upload folder using huggingface_hub
985c397 verified
// SPDX-License-Identifier: LGPL-2.1-or-later
/****************************************************************************
* *
* Copyright (c) 2024 Kacper Donat <kacper@kadet.net> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD 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 *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
***************************************************************************/
#include <sstream>
#include <Inventor/nodes/SoBaseColor.h>
#include <Inventor/nodes/SoComplexity.h>
#include <Inventor/nodes/SoCone.h>
#include <Inventor/nodes/SoCylinder.h>
#include <Inventor/nodes/SoDrawStyle.h>
#include <Inventor/nodes/SoLightModel.h>
#include <Inventor/nodes/SoMaterial.h>
#include <Inventor/nodes/SoRotation.h>
#include <Inventor/nodes/SoSeparator.h>
#include <Inventor/nodes/SoSphere.h>
#include <Inventor/nodes/SoTransform.h>
#include <Inventor/nodes/SoTranslation.h>
#include <Inventor/nodes/SoFontStyle.h>
#include "SoFCPlacementIndicatorKit.h"
#include "So3DAnnotation.h"
#include "SoAxisCrossKit.h"
#include <SoTextLabel.h>
#include <Utilities.h>
#include <ViewParams.h>
#include <ViewProvider.h>
#include <Base/Color.h>
#include <Base/Placement.h>
#include <Base/Vector3D.h>
namespace Gui
{
SO_KIT_SOURCE(SoFCPlacementIndicatorKit);
SoFCPlacementIndicatorKit::SoFCPlacementIndicatorKit()
{
SO_KIT_CONSTRUCTOR(SoFCPlacementIndicatorKit);
SO_KIT_ADD_CATALOG_ENTRY(root, SoShapeScale, false, this, "", true);
SO_KIT_INIT_INSTANCE();
SO_NODE_ADD_FIELD(coloredAxis, (true));
SO_NODE_ADD_FIELD(scaleFactor, (ViewParams::instance()->getPlacementIndicatorScale()));
SO_NODE_ADD_FIELD(axisLength, (axisLengthDefault));
SO_NODE_ADD_FIELD(parts, (AxisCross));
SO_NODE_ADD_FIELD(axes, (AllAxes));
SO_NODE_DEFINE_ENUM_VALUE(Part, Axes);
SO_NODE_DEFINE_ENUM_VALUE(Part, ArrowHeads);
SO_NODE_DEFINE_ENUM_VALUE(Part, Labels);
SO_NODE_DEFINE_ENUM_VALUE(Part, AxisCross);
SO_NODE_DEFINE_ENUM_VALUE(Part, PlaneIndicator);
SO_NODE_DEFINE_ENUM_VALUE(Part, OriginIndicator);
SO_NODE_DEFINE_ENUM_VALUE(Part, AllParts);
SO_NODE_SET_SF_ENUM_TYPE(parts, Part);
SO_NODE_DEFINE_ENUM_VALUE(Axes, X);
SO_NODE_DEFINE_ENUM_VALUE(Axes, Y);
SO_NODE_DEFINE_ENUM_VALUE(Axes, Z);
SO_NODE_DEFINE_ENUM_VALUE(Axes, AllAxes);
SO_NODE_SET_SF_ENUM_TYPE(axes, Axes);
auto root = SO_GET_ANY_PART(this, "root", SoShapeScale);
root->scaleFactor.connectFrom(&scaleFactor);
recomputeGeometry();
}
void SoFCPlacementIndicatorKit::initClass()
{
SO_KIT_INIT_CLASS(SoFCPlacementIndicatorKit, SoBaseKit, "BaseKit");
}
void SoFCPlacementIndicatorKit::notify(SoNotList* l)
{
SoField* field = l->getLastField();
if (field == &parts || field == &axes || field == &axisLength) {
// certainly this is not the fastest way to recompute the geometry as it does recreate
// everything from the scratch. It is however very easy to implement and this node should
// not really change too often so the performance penalty is better than making code that
// is harder to maintain.
recomputeGeometry();
return;
}
SoBaseKit::notify(l);
}
void SoFCPlacementIndicatorKit::recomputeGeometry()
{
auto root = SO_GET_ANY_PART(this, "root", SoShapeScale);
root->setPart("shape", createGeometry());
}
SoSeparator* SoFCPlacementIndicatorKit::createOriginIndicator()
{
const uint32_t originColor = ViewParams::instance()->getOriginColor();
auto sep = new SoSeparator();
auto pcBaseColor = new SoBaseColor();
pcBaseColor->rgb.setValue(Base::Color::fromPackedRGBA<SbColor>(originColor));
auto pcSphere = new SoSphere();
// the factor aligns radius of sphere and arrow head visually, without it, it looks too small
constexpr float visualAdjustmentFactor = 1.2F;
pcSphere->radius = arrowHeadRadius * visualAdjustmentFactor;
sep->addChild(pcBaseColor);
sep->addChild(pcSphere);
return sep;
}
SoSeparator* SoFCPlacementIndicatorKit::createGeometry()
{
const uint32_t neutralColor = ViewParams::instance()->getNeutralColor();
auto sep = new SoSeparator();
auto pcBaseColor = new SoBaseColor();
pcBaseColor->rgb.setValue(Base::Color::fromPackedRGBA<SbColor>(neutralColor));
auto pcLightModel = new SoLightModel();
pcLightModel->model = SoLightModel::BASE_COLOR;
sep->addChild(pcBaseColor);
sep->addChild(pcLightModel);
if (parts.getValue() & PlaneIndicator) {
sep->addChild(createPlaneIndicator());
}
if (parts.getValue() & OriginIndicator) {
sep->addChild(createOriginIndicator());
}
if (parts.getValue() & Axes) {
sep->addChild(createAxes());
}
return sep;
}
SoSeparator* SoFCPlacementIndicatorKit::createAxes()
{
const auto cylinderOffset = axisLength.getValue() / 2.F;
const auto createAxis = [&](const char* label,
Base::Vector3d axis,
uint32_t packedColor,
const double offset) {
Base::Color axisColor(packedColor);
Base::Rotation rotation(Base::Vector3d::UnitY, axis);
auto sep = new SoSeparator;
auto pcTranslate = new SoTransform();
pcTranslate->translation.setValue(Base::convertTo<SbVec3f>((cylinderOffset + offset) * axis));
pcTranslate->rotation.setValue(Base::convertTo<SbRotation>(rotation));
auto pcArrowShaft = new SoCylinder();
pcArrowShaft->radius = axisThickness / 2.F;
pcArrowShaft->height = axisLength;
if (coloredAxis.getValue()) {
auto pcBaseColor = new SoBaseColor();
pcBaseColor->rgb.setValue(Base::convertTo<SbColor>(axisColor));
sep->addChild(pcBaseColor);
}
sep->addChild(pcTranslate);
sep->addChild(pcArrowShaft);
if (parts.getValue() & ArrowHeads) {
auto pcArrowHeadTranslation = new SoTranslation();
pcArrowHeadTranslation->translation.setValue(0.0, cylinderOffset, 0.0);
auto pcArrowHead = new SoCone();
pcArrowHead->bottomRadius = arrowHeadRadius;
pcArrowHead->height = arrowHeadHeight;
auto pcArrowHeadSeparator = new SoSeparator();
pcArrowHeadSeparator->addChild(pcArrowHeadTranslation);
pcArrowHeadSeparator->addChild(pcArrowHead);
sep->addChild(pcArrowHeadSeparator);
}
if (parts.getValue() & Labels) {
auto pcLabelSeparator = new SoSeparator();
auto pcLabelTranslation = new SoTranslation();
pcLabelTranslation->translation.setValue(0.0, cylinderOffset + labelOffset, 0.0);
pcLabelSeparator->addChild(pcLabelTranslation);
auto pcAxisLabel = new SoFrameLabel();
pcAxisLabel->string.setValue(label);
pcAxisLabel->textColor.setValue(1.0, 1.0, 1.0);
pcAxisLabel->horAlignment = SoImage::CENTER;
pcAxisLabel->vertAlignment = SoImage::HALF;
pcAxisLabel->border = false;
pcAxisLabel->frame = false;
pcAxisLabel->textUseBaseColor = true;
pcAxisLabel->size = labelFontSize;
pcLabelSeparator->addChild(pcAxisLabel);
sep->addChild(pcLabelSeparator);
}
return sep;
};
double additionalAxisMargin = (parts.getValue() & OriginIndicator) ? axisThickness * 4 : 0;
double xyOffset = (parts.getValue() & PlaneIndicator)
? planeIndicatorRadius + planeIndicatorMargin
: axisMargin + additionalAxisMargin;
auto sep = new SoSeparator;
if (axes.getValue() & X) {
sep->addChild(
createAxis("X", Base::Vector3d::UnitX, ViewParams::instance()->getAxisXColor(), xyOffset)
);
}
if (axes.getValue() & Y) {
sep->addChild(
createAxis("Y", Base::Vector3d::UnitY, ViewParams::instance()->getAxisYColor(), xyOffset)
);
}
if (axes.getValue() & Z) {
double zOffset = (parts.getValue() & PlaneIndicator) ? planeIndicatorMargin
: axisMargin + additionalAxisMargin;
sep->addChild(
createAxis("Z", Base::Vector3d::UnitZ, ViewParams::instance()->getAxisZColor(), zOffset)
);
}
return sep;
}
SoSeparator* SoFCPlacementIndicatorKit::createPlaneIndicator()
{
// cylinders are aligned with Y axis for some reason
auto rotation = Base::Rotation(Base::Vector3d::UnitY, Base::Vector3d::UnitZ);
auto pcRotation = new SoRotation();
pcRotation->rotation = Base::convertTo<SbRotation>(rotation);
auto pcComplexity = new SoComplexity();
pcComplexity->value = 1.0f;
auto pcCylinder = new SoCylinder();
pcCylinder->height = 0.f;
pcCylinder->radius = planeIndicatorRadius;
pcCylinder->parts = SoCylinder::TOP;
const auto gray = Base::Color(0.75f, 0.75f, 0.75f);
auto pcMaterial = new SoMaterial();
pcMaterial->diffuseColor.setValue(Base::convertTo<SbColor>(gray));
pcMaterial->ambientColor.connectFrom(&pcMaterial->diffuseColor);
pcMaterial->transparency = planeIndicatorTransparency;
auto sep = new SoSeparator;
sep->addChild(pcRotation);
sep->addChild(pcMaterial);
sep->addChild(pcComplexity);
sep->addChild(pcCylinder);
return sep;
}
SoFCPlacementIndicatorKit::~SoFCPlacementIndicatorKit() = default;
} // namespace Gui