======================================== Fonts: - SoDatumLabel internally uses QFont and thus points. It was being fed pixels, for which the configured size in preferences did not match the actual on screen size. This is corrected. - Font sizes fed directly from parameters to the coin nodes skeleton were not being updated, as the parameter observer is initialised after the nodes are created, and the parameter observer was not configured to update this nodes. This is corrected. Colors: - Some color parameters were not being being updated by the parameter observer. This is corrected.
461 lines
19 KiB
C++
461 lines
19 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/SoLineSet.h>
|
|
# include <Inventor/nodes/SoFont.h>
|
|
# include <Inventor/nodes/SoGroup.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 "EditModeInformationOverlayCoinConverter.h"
|
|
#include "EditModeCoinManagerParameters.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) {
|
|
|
|
// at this point all calculations relate to BSplineCurves
|
|
assert(geometry->getTypeId() == Part::GeomBSplineCurve::getClassTypeId());
|
|
|
|
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);
|
|
|
|
};
|
|
|
|
void EditModeInformationOverlayCoinConverter::addToInfoGroup(SoSwitch * sw) {
|
|
infoGroup->addChild(sw);
|
|
nodeId++;
|
|
}
|
|
|
|
template < EditModeInformationOverlayCoinConverter::CalculationType calculation >
|
|
void EditModeInformationOverlayCoinConverter::calculate(const Part::Geometry * geometry, [[maybe_unused]] int geoid) {
|
|
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().Error("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 = QString::fromLatin1("[%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;
|
|
}
|
|
}
|
|
|
|
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++;
|
|
}
|
|
}
|