Files
create/src/Mod/Sketcher/Gui/EditModeInformationOverlayCoinConverter.cpp

564 lines
22 KiB
C++

/***************************************************************************
* Copyright (c) 2021 Abdullah Tahiri <abdullah.tahiri.yo@gmail.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 <Inventor/nodes/SoCoordinate3.h>
#include <Inventor/nodes/SoFont.h>
#include <Inventor/nodes/SoGroup.h>
#include <Inventor/nodes/SoLineSet.h>
#include <Inventor/nodes/SoMaterial.h>
#include <Inventor/nodes/SoSeparator.h>
#include <Inventor/nodes/SoSwitch.h>
#include <Inventor/nodes/SoText2.h>
#include <Inventor/nodes/SoTranslation.h>
#endif // #ifndef _PreComp_
#include <Base/Console.h>
#include <Base/Exception.h>
#include <Base/UnitsApi.h>
#include "EditModeCoinManagerParameters.h"
#include "EditModeInformationOverlayCoinConverter.h"
#include "ViewProviderSketchCoinAttorney.h"
using namespace SketcherGui;
EditModeInformationOverlayCoinConverter::EditModeInformationOverlayCoinConverter(
ViewProviderSketch& vp,
SoGroup* infogroup,
OverlayParameters& overlayparameters,
DrawingParameters& drawingparameters)
: viewProvider(vp)
, infoGroup(infogroup)
, overlayParameters(overlayparameters)
, drawingParameters(drawingparameters)
, nodeId(0) {
};
void EditModeInformationOverlayCoinConverter::convert(const Part::Geometry* geometry, int geoid)
{
if (geometry->is<Part::GeomBSplineCurve>()) {
if (geoid < 0) {
return;
}
calculate<CalculationType::BSplineDegree>(geometry, geoid);
calculate<CalculationType::BSplineControlPolygon>(geometry, geoid);
calculate<CalculationType::BSplineCurvatureComb>(geometry, geoid);
calculate<CalculationType::BSplineKnotMultiplicity>(geometry, geoid);
calculate<CalculationType::BSplinePoleWeight>(geometry, geoid);
addUpdateNode(degree);
addUpdateNode(controlPolygon);
addUpdateNode(curvatureComb);
addUpdateNode(knotMultiplicity);
addUpdateNode(poleWeights);
}
else if (geometry->is<Part::GeomArcOfCircle>()) {
// at this point all calculations relate to ArcOfCircle
calculate<CalculationType::ArcCircleHelper>(geometry, geoid);
addUpdateNode(circleHelper);
}
}
void EditModeInformationOverlayCoinConverter::addToInfoGroup(SoSwitch* sw)
{
infoGroup->addChild(sw);
nodeId++;
}
template<EditModeInformationOverlayCoinConverter::CalculationType calculation>
void EditModeInformationOverlayCoinConverter::calculate(const Part::Geometry* geometry,
[[maybe_unused]] int geoid)
{
if constexpr (calculation == CalculationType::ArcCircleHelper) {
const Part::GeomArcOfCircle* arc = static_cast<const Part::GeomArcOfCircle*>(geometry);
clearCalculation(circleHelper);
Base::Vector3d center = arc->getCenter();
double radius = arc->getRadius();
Part::GeomCircle circle;
circle.setRadius(radius);
circle.setCenter(center);
const int ndiv = drawingParameters.curvedEdgeCountSegments;
circleHelper.coordinates.reserve(ndiv);
for (int i = 0; i < ndiv; i++) {
double param = i * std::atan(1) * 8 / ndiv;
circleHelper.coordinates.emplace_back(circle.value(param));
}
circleHelper.coordinates.emplace_back(circle.value(0.0));
circleHelper.indices.push_back(ndiv + 1);
}
else {
const Part::GeomBSplineCurve* spline = static_cast<const Part::GeomBSplineCurve*>(geometry);
if constexpr (calculation == CalculationType::BSplineDegree) {
clearCalculation(degree);
std::vector<Base::Vector3d> poles = spline->getPoles();
degree.strings.clear();
degree.positions.clear();
Base::Vector3d midp = Base::Vector3d(0, 0, 0);
for (auto val : poles) {
midp += val;
}
midp /= poles.size();
degree.strings.emplace_back(std::to_string(spline->getDegree()));
degree.positions.emplace_back(midp);
}
else if constexpr (calculation == CalculationType::BSplineControlPolygon) {
clearCalculation(controlPolygon);
std::vector<Base::Vector3d> poles = spline->getPoles();
controlPolygon.coordinates.clear();
controlPolygon.indices.clear();
size_t nvertices;
if (spline->isPeriodic()) {
nvertices = poles.size() + 1;
}
else {
nvertices = poles.size();
}
controlPolygon.coordinates.reserve(nvertices);
for (auto& v : poles) {
controlPolygon.coordinates.emplace_back(v);
}
if (spline->isPeriodic()) {
controlPolygon.coordinates.emplace_back(poles[0]);
}
controlPolygon.indices.push_back(
nvertices); // single continuous polygon starting at index 0
}
else if constexpr (calculation == CalculationType::BSplineCurvatureComb) {
clearCalculation(curvatureComb);
// curvature graph --------------------------------------------------------
// based on python source
// https://github.com/tomate44/CurvesWB/blob/master/freecad/Curves/ParametricComb.py
// by FreeCAD user Chris_G
std::vector<Base::Vector3d> poles = spline->getPoles();
auto knots = spline->getKnots();
auto mults = spline->getMultiplicities();
const int ndivPerPiece = 64; // heuristic of number of division to fill in
const int ndiv = ndivPerPiece * (knots.size() - 1);
std::vector<Base::Vector3d> pointatcurvelist;
std::vector<double> curvaturelist;
std::vector<Base::Vector3d> normallist;
pointatcurvelist.reserve(ndiv);
curvaturelist.reserve(ndiv);
normallist.reserve(ndiv);
// go through the polynomial pieces (i.e. from one knot to next)
for (size_t k = 0; k < knots.size() - 1; ++k) {
// first and last params are a little off to account for possible discontinuity at
// knots
double firstparam =
knots[k] + Precision::Approximation() * (knots[k + 1] - knots[k]);
double lastparam =
knots[k + 1] - Precision::Approximation() * (knots[k + 1] - knots[k]);
// TODO: Maybe this can be improved, specifically adapted for each piece
double step = (lastparam - firstparam) / (ndivPerPiece - 1);
for (int i = 0; i < ndivPerPiece; ++i) {
double param = firstparam + i * step;
pointatcurvelist.emplace_back(spline->value(param));
try {
curvaturelist.emplace_back(spline->curvatureAt(param));
}
catch (Base::CADKernelError& e) {
// it is "just" a visualisation matter OCC could not calculate the curvature
// terminating here would mean that the other shapes would not be drawn.
// Solution: Report the issue and set dummy curvature to 0
e.reportException();
Base::Console().developerError(
"EditModeInformationOverlayCoinConverter",
"Curvature graph for B-spline with GeoId=%d could not be calculated.\n",
geoid);
curvaturelist.emplace_back(0);
}
Base::Vector3d normal;
try {
spline->normalAt(param, normal);
normallist.emplace_back(normal);
}
catch (Base::Exception&) {
normallist.emplace_back(0, 0, 0);
}
}
}
std::vector<Base::Vector3d> pointatcomblist;
pointatcomblist.reserve(ndiv);
for (int i = 0; i < ndiv; i++) {
pointatcomblist.emplace_back(
pointatcurvelist[i]
- overlayParameters.currentBSplineCombRepresentationScale * curvaturelist[i]
* normallist[i]);
}
curvatureComb.coordinates.reserve(3 * ndiv); // 2*ndiv +1 points of ndiv separate
// segments + ndiv points for last segment
curvatureComb.indices.reserve(
ndiv + 1); // ndiv separate segments of radials + 1 segment connecting at comb end
auto zInfoH = ViewProviderSketchCoinAttorney::getViewOrientationFactor(viewProvider)
* drawingParameters.zInfo;
for (int i = 0; i < ndiv; i++) {
// note emplace emplaces on the position BEFORE the iterator given.
curvatureComb.coordinates.emplace_back(pointatcurvelist[i].x,
pointatcurvelist[i].y,
zInfoH); // radials
curvatureComb.coordinates.emplace_back(pointatcomblist[i].x,
pointatcomblist[i].y,
zInfoH); // radials
curvatureComb.indices.emplace_back(2); // line
}
for (int i = 0; i < ndiv; i++) {
curvatureComb.coordinates.emplace_back(pointatcomblist[i].x,
pointatcomblist[i].y,
zInfoH); // // comb endpoint closing segment
}
curvatureComb.indices.emplace_back(ndiv); // Comb line
}
else if constexpr (calculation == CalculationType::BSplineKnotMultiplicity) {
clearCalculation(knotMultiplicity);
std::vector<double> knots = spline->getKnots();
std::vector<int> mult = spline->getMultiplicities();
for (size_t i = 0; i < knots.size(); i++) {
knotMultiplicity.positions.emplace_back(spline->pointAtParameter(knots[i]));
std::ostringstream stringStream;
stringStream << "(" << mult[i] << ")";
knotMultiplicity.strings.emplace_back(stringStream.str());
}
}
else if constexpr (calculation == CalculationType::BSplinePoleWeight) {
clearCalculation(poleWeights);
std::vector<Base::Vector3d> poles = spline->getPoles();
auto weights = spline->getWeights();
for (size_t i = 0; i < poles.size(); i++) {
poleWeights.positions.emplace_back(poles[i]);
QString WeightString =
QStringLiteral("[%1]").arg(weights[i], 0, 'f', Base::UnitsApi::getDecimals());
poleWeights.strings.emplace_back(WeightString.toStdString());
}
}
}
}
template<typename Result>
void EditModeInformationOverlayCoinConverter::addUpdateNode(const Result& result)
{
if (overlayParameters.rebuildInformationLayer) {
addNode(result);
}
else {
updateNode(result);
}
}
template<EditModeInformationOverlayCoinConverter::CalculationType calculation>
bool EditModeInformationOverlayCoinConverter::isVisible()
{
if constexpr (calculation == CalculationType::BSplineDegree) {
return overlayParameters.bSplineDegreeVisible;
}
else if constexpr (calculation == CalculationType::BSplineControlPolygon) {
return overlayParameters.bSplineControlPolygonVisible;
}
else if constexpr (calculation == CalculationType::BSplineCurvatureComb) {
return overlayParameters.bSplineCombVisible;
}
else if constexpr (calculation == CalculationType::BSplineKnotMultiplicity) {
return overlayParameters.bSplineKnotMultiplicityVisible;
}
else if constexpr (calculation == CalculationType::BSplinePoleWeight) {
return overlayParameters.bSplinePoleWeightVisible;
}
else if constexpr (calculation == CalculationType::ArcCircleHelper) {
return overlayParameters.arcCircleHelperVisible;
}
}
template<typename Result>
void EditModeInformationOverlayCoinConverter::setPolygon(const Result& result,
SoLineSet* polygonlineset,
SoCoordinate3* polygoncoords)
{
polygoncoords->point.setNum(result.coordinates.size());
polygonlineset->numVertices.setNum(result.indices.size());
int32_t* index = polygonlineset->numVertices.startEditing();
SbVec3f* vts = polygoncoords->point.startEditing();
for (size_t i = 0; i < result.coordinates.size(); i++) {
vts[i].setValue(result.coordinates[i].x,
result.coordinates[i].y,
ViewProviderSketchCoinAttorney::getViewOrientationFactor(viewProvider)
* drawingParameters.zInfo);
}
for (size_t i = 0; i < result.indices.size(); i++) {
index[i] = result.indices[i];
}
polygoncoords->point.finishEditing();
polygonlineset->numVertices.finishEditing();
}
template<int line>
void EditModeInformationOverlayCoinConverter::setText(const std::string& string, SoText2* text)
{
if constexpr (line == 1) {
text->string = SbString(string.c_str());
}
else {
assert(line > 1);
SoMFString label;
for (int l = 0; l < (line - 1); l++) {
label.set1Value(l, SbString(""));
}
label.set1Value(line - 1, SbString(string.c_str()));
text->string = label;
}
}
template<typename Result>
void EditModeInformationOverlayCoinConverter::clearCalculation(Result& result)
{
if constexpr (Result::visualisationType == VisualisationType::Text) {
result.positions.clear();
result.strings.clear();
}
else if constexpr (Result::visualisationType == VisualisationType::Polygon) {
result.coordinates.clear();
result.indices.clear();
}
}
template<typename Result>
void EditModeInformationOverlayCoinConverter::addNode(const Result& result)
{
if constexpr (Result::visualisationType == VisualisationType::Text) {
for (size_t i = 0; i < result.strings.size(); i++) {
SoSwitch* sw = new SoSwitch();
sw->whichChild = isVisible<Result::calculationType>() ? SO_SWITCH_ALL : SO_SWITCH_NONE;
SoSeparator* sep = new SoSeparator();
sep->ref();
// no caching for frequently-changing data structures
sep->renderCaching = SoSeparator::OFF;
// every information visual node gets its own material for to-be-implemented
// preselection and selection
SoMaterial* mat = new SoMaterial;
mat->ref();
mat->diffuseColor = drawingParameters.InformationColor;
SoTranslation* translate = new SoTranslation;
translate->translation.setValue(
result.positions[i].x,
result.positions[i].y,
ViewProviderSketchCoinAttorney::getViewOrientationFactor(viewProvider)
* drawingParameters.zInfo);
SoFont* font = new SoFont;
font->name.setValue("Helvetica");
font->size.setValue(drawingParameters.coinFontSize);
SoText2* text = new SoText2;
// since the first and last control point of a spline is also treated as knot and thus
// can also have a displayed multiplicity, we must assure the multiplicity is not
// visibly overwritten therefore be output the weight in a second line
//
// This could be made into a more generic form, but it is probably not worth the effort
// at this time.
if constexpr (Result::calculationType == CalculationType::BSplinePoleWeight) {
setText<2>(result.strings[i], text);
}
else {
setText(result.strings[i], text);
}
sep->addChild(mat);
sep->addChild(font);
sep->addChild(translate);
sep->addChild(text);
sw->addChild(sep);
addToInfoGroup(sw);
sep->unref();
mat->unref();
}
}
else if constexpr (Result::visualisationType == VisualisationType::Polygon) {
SoSwitch* sw = new SoSwitch();
// hGrpsk->GetBool("BSplineControlPolygonVisible", true)
sw->whichChild = isVisible<Result::calculationType>() ? SO_SWITCH_ALL : SO_SWITCH_NONE;
SoSeparator* sep = new SoSeparator();
sep->ref();
// no caching for frequently-changing data structures
sep->renderCaching = SoSeparator::OFF;
// every information visual node gets its own material for to-be-implemented preselection
// and selection
SoMaterial* mat = new SoMaterial;
mat->ref();
mat->diffuseColor = drawingParameters.InformationColor;
SoLineSet* polygonlineset = new SoLineSet;
SoCoordinate3* polygoncoords = new SoCoordinate3;
setPolygon<Result>(result, polygonlineset, polygoncoords);
sep->addChild(mat);
sep->addChild(polygoncoords);
sep->addChild(polygonlineset);
sw->addChild(sep);
addToInfoGroup(sw);
sep->unref();
mat->unref();
}
}
template<typename Result>
void EditModeInformationOverlayCoinConverter::updateNode(const Result& result)
{
if constexpr (Result::visualisationType == VisualisationType::Text) {
for (size_t i = 0; i < result.strings.size(); i++) {
SoSwitch* sw = static_cast<SoSwitch*>(infoGroup->getChild(nodeId));
if (overlayParameters.visibleInformationChanged) {
sw->whichChild =
isVisible<Result::calculationType>() ? SO_SWITCH_ALL : SO_SWITCH_NONE;
}
SoSeparator* sep = static_cast<SoSeparator*>(sw->getChild(0));
static_cast<SoTranslation*>(
sep->getChild(static_cast<int>(TextNodePosition::TextCoordinates)))
->translation.setValue(
result.positions[i].x,
result.positions[i].y,
ViewProviderSketchCoinAttorney::getViewOrientationFactor(viewProvider)
* drawingParameters.zInfo);
// since the first and last control point of a spline is also treated as knot and thus
// can also have a displayed multiplicity, we must assure the multiplicity is not
// visibly overwritten therefore be output the weight in a second line
//
// This could be made into a more generic form, but it is probably not worth the effort
// at this time.
if constexpr (Result::calculationType == CalculationType::BSplinePoleWeight) {
setText<2>(result.strings[i],
static_cast<SoText2*>(
sep->getChild(static_cast<int>(TextNodePosition::TextInformation))));
}
else {
setText(result.strings[i],
static_cast<SoText2*>(
sep->getChild(static_cast<int>(TextNodePosition::TextInformation))));
}
nodeId++;
}
}
else if constexpr (Result::visualisationType == VisualisationType::Polygon) {
SoSwitch* sw = static_cast<SoSwitch*>(infoGroup->getChild(nodeId));
if (overlayParameters.visibleInformationChanged) {
sw->whichChild = isVisible<Result::calculationType>() ? SO_SWITCH_ALL : SO_SWITCH_NONE;
}
SoSeparator* sep = static_cast<SoSeparator*>(sw->getChild(0));
SoCoordinate3* polygoncoords = static_cast<SoCoordinate3*>(
sep->getChild(static_cast<int>(PolygonNodePosition::PolygonCoordinates)));
SoLineSet* polygonlineset = static_cast<SoLineSet*>(
sep->getChild(static_cast<int>(PolygonNodePosition::PolygonLineSet)));
setPolygon(result, polygonlineset, polygoncoords);
nodeId++;
}
}