Files
create/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp
Abdullah Tahiri e566daa1af Sketcher: Correct font sizes and updates
========================================

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.
2023-02-15 17:29:46 +01:00

2399 lines
124 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 <memory>
# include <QPainter>
# include <QRegularExpression>
# include <Inventor/SbImage.h>
# include <Inventor/SbVec3f.h>
# include <Inventor/SoPickedPoint.h>
# include <Inventor/nodes/SoAnnotation.h>
# include <Inventor/nodes/SoDrawStyle.h>
# include <Inventor/nodes/SoGroup.h>
# include <Inventor/nodes/SoImage.h>
# include <Inventor/nodes/SoInfo.h>
# include <Inventor/nodes/SoMaterial.h>
# include <Inventor/nodes/SoMaterialBinding.h>
# include <Inventor/nodes/SoPickStyle.h>
# include <Inventor/nodes/SoSeparator.h>
# include <Inventor/nodes/SoTranslation.h>
#endif // #ifndef _PreComp_
#include <Base/Converter.h>
#include <Base/Exception.h>
#include <Base/Tools.h>
#include <Base/UnitsApi.h>
#include <Base/Vector3D.h>
#include <Gui/BitmapFactory.h>
#include <Gui/Tools.h>
#include <Gui/Utilities.h>
#include <Gui/Inventor/SmSwitchboard.h>
#include <Mod/Part/App/Geometry.h>
#include <Mod/Sketcher/App/GeometryFacade.h>
#include <Mod/Sketcher/App/SolverGeometryExtension.h>
#include <Mod/Sketcher/App/GeoEnum.h>
#include <Mod/Sketcher/App/Constraint.h>
#include <Mod/Sketcher/App/GeoList.h>
#include "EditModeConstraintCoinManager.h"
#include "SoDatumLabel.h"
#include "SoZoomTranslation.h"
#include "ViewProviderSketch.h"
#include "ViewProviderSketchCoinAttorney.h"
using namespace SketcherGui;
using namespace Sketcher;
//**************************** EditModeConstraintCoinManager class ******************************
EditModeConstraintCoinManager::EditModeConstraintCoinManager( ViewProviderSketch &vp,
DrawingParameters & drawingParams,
GeometryLayerParameters & geometryLayerParams,
ConstraintParameters & constraintParams,
EditModeScenegraphNodes & editModeScenegraph,
CoinMapping & coinMap):
viewProvider(vp),
drawingParameters(drawingParams),
geometryLayerParameters(geometryLayerParams),
constraintParameters(constraintParams),
editModeScenegraphNodes(editModeScenegraph),
coinMapping(coinMap)
{}
EditModeConstraintCoinManager::~EditModeConstraintCoinManager()
{}
void EditModeConstraintCoinManager::updateVirtualSpace()
{
const std::vector<Sketcher::Constraint *> &constrlist = ViewProviderSketchCoinAttorney::getConstraints(viewProvider);
bool isshownvirtualspace = ViewProviderSketchCoinAttorney::isShownVirtualSpace(viewProvider);
if (constrlist.size() == vConstrType.size()) {
editModeScenegraphNodes.constrGroup->enable.setNum(constrlist.size());
SbBool *sws = editModeScenegraphNodes.constrGroup->enable.startEditing();
for (size_t i = 0; i < constrlist.size(); i++)
sws[i] = !(constrlist[i]->isInVirtualSpace != isshownvirtualspace); // XOR of constraint mode and VP mode
editModeScenegraphNodes.constrGroup->enable.finishEditing();
}
}
void EditModeConstraintCoinManager::processConstraints(const GeoListFacade & geolistfacade)
{
const auto &constrlist = ViewProviderSketchCoinAttorney::getConstraints(viewProvider);
auto zConstrH = ViewProviderSketchCoinAttorney::getViewOrientationFactor(viewProvider) * drawingParameters.zConstr;
// After an undo/redo it can happen that we have an empty geometry list but a non-empty constraint list
// In this case just ignore the constraints. (See bug #0000421)
if (geolistfacade.geomlist.size() <= 2 && !constrlist.empty()) {
rebuildConstraintNodes(geolistfacade);
return;
}
int extGeoCount = geolistfacade.getExternalCount();
int intGeoCount = geolistfacade.getInternalCount();
// reset point if the constraint type has changed
Restart:
// check if a new constraint arrived
if (constrlist.size() != vConstrType.size())
rebuildConstraintNodes(geolistfacade);
assert(int(constrlist.size()) == editModeScenegraphNodes.constrGroup->getNumChildren());
assert(int(vConstrType.size()) == editModeScenegraphNodes.constrGroup->getNumChildren());
// update the virtual space
updateVirtualSpace();
auto getNormal = [] (const GeoListFacade & geolistfacade, const int geoid, const Base::Vector3d & pointoncurve) {
auto geom = geolistfacade.getGeometryFromGeoId(geoid);
auto curve = dynamic_cast<const Part::GeomCurve *>(geom);
Base::Vector3d normal;
try {
if (!(curve && curve->normalAt(pointoncurve, normal))) {
normal = Base::Vector3d(1,0,0);
}
}
catch (const Base::CADKernelError&) {
normal = Base::Vector3d(1,0,0);
}
return normal;
};
// go through the constraints and update the position
int i = 0;
for (std::vector<Sketcher::Constraint *>::const_iterator it=constrlist.begin();
it != constrlist.end(); ++it, i++) {
// check if the type has changed
if ((*it)->Type != vConstrType[i]) {
// clearing the type vector will force a rebuild of the visual nodes
vConstrType.clear();
//TODO: The 'goto' here is unsafe as it can happen that we cause an endless loop (see bug #0001956).
goto Restart;
}
try{//because calculateNormalAtPoint, used in there, can throw
// root separator for this constraint
SoSeparator *sep = static_cast<SoSeparator *>(editModeScenegraphNodes.constrGroup->getChild(i));
const Constraint *Constr = *it;
if (Constr->First < -extGeoCount || Constr->First >= intGeoCount
|| (Constr->Second!=GeoEnum::GeoUndef
&& (Constr->Second < -extGeoCount || Constr->Second >= intGeoCount))
|| (Constr->Third!=GeoEnum::GeoUndef
&& (Constr->Third < -extGeoCount || Constr->Third >= intGeoCount)))
{
// Constraint can refer to non-existent geometry during undo/redo
continue;
}
// distinguish different constraint types to build up
switch (Constr->Type) {
case Block:
case Horizontal: // write the new position of the Horizontal constraint Same as vertical position.
case Vertical: // write the new position of the Vertical constraint
{
assert(Constr->First >= -extGeoCount && Constr->First < intGeoCount);
bool alignment = Constr->Type!=Block && Constr->Second != GeoEnum::GeoUndef;
// get the geometry
const Part::Geometry *geo = geolistfacade.getGeometryFromGeoId(Constr->First);
if (!alignment) {
// Vertical & Horiz can only be a GeomLineSegment, but Blocked can be anything.
Base::Vector3d midpos;
Base::Vector3d dir;
Base::Vector3d norm;
if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
const Part::GeomLineSegment *lineSeg = static_cast<const Part::GeomLineSegment *>(geo);
// calculate the half distance between the start and endpoint
midpos = ((lineSeg->getEndPoint()+lineSeg->getStartPoint())/2);
//Get a set of vectors perpendicular and tangential to these
dir = (lineSeg->getEndPoint()-lineSeg->getStartPoint()).Normalize();
norm = Base::Vector3d(-dir.y,dir.x,0);
}
else if (geo->getTypeId() == Part::GeomBSplineCurve::getClassTypeId()) {
const Part::GeomBSplineCurve *bsp = static_cast<const Part::GeomBSplineCurve *>(geo);
midpos = Base::Vector3d(0,0,0);
std::vector<Base::Vector3d> poles = bsp->getPoles();
// Move center of gravity towards start not to collide with bspline degree information.
double ws = 1.0 / poles.size();
double w = 1.0;
for (std::vector<Base::Vector3d>::iterator it = poles.begin(); it != poles.end(); ++it) {
midpos += w*(*it);
w -= ws;
}
midpos /= poles.size();
dir = (bsp->getEndPoint() - bsp->getStartPoint()).Normalize();
norm = Base::Vector3d(-dir.y,dir.x,0);
}
else {
double ra=0,rb=0;
double angle,angleplus=0.;//angle = rotation of object as a whole; angleplus = arc angle (t parameter for ellipses).
if (geo->getTypeId() == Part::GeomCircle::getClassTypeId()) {
const Part::GeomCircle *circle = static_cast<const Part::GeomCircle *>(geo);
ra = circle->getRadius();
angle = M_PI/4;
midpos = circle->getCenter();
} else if (geo->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
const Part::GeomArcOfCircle *arc = static_cast<const Part::GeomArcOfCircle *>(geo);
ra = arc->getRadius();
double startangle, endangle;
arc->getRange(startangle, endangle, /*emulateCCW=*/true);
angle = (startangle + endangle)/2;
midpos = arc->getCenter();
} else if (geo->getTypeId() == Part::GeomEllipse::getClassTypeId()) {
const Part::GeomEllipse *ellipse = static_cast<const Part::GeomEllipse *>(geo);
ra = ellipse->getMajorRadius();
rb = ellipse->getMinorRadius();
Base::Vector3d majdir = ellipse->getMajorAxisDir();
angle = atan2(majdir.y, majdir.x);
angleplus = M_PI/4;
midpos = ellipse->getCenter();
} else if (geo->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()) {
const Part::GeomArcOfEllipse *aoe = static_cast<const Part::GeomArcOfEllipse *>(geo);
ra = aoe->getMajorRadius();
rb = aoe->getMinorRadius();
double startangle, endangle;
aoe->getRange(startangle, endangle, /*emulateCCW=*/true);
Base::Vector3d majdir = aoe->getMajorAxisDir();
angle = atan2(majdir.y, majdir.x);
angleplus = (startangle + endangle)/2;
midpos = aoe->getCenter();
} else if (geo->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId()) {
const Part::GeomArcOfHyperbola *aoh = static_cast<const Part::GeomArcOfHyperbola *>(geo);
ra = aoh->getMajorRadius();
rb = aoh->getMinorRadius();
double startangle, endangle;
aoh->getRange(startangle, endangle, /*emulateCCW=*/true);
Base::Vector3d majdir = aoh->getMajorAxisDir();
angle = atan2(majdir.y, majdir.x);
angleplus = (startangle + endangle)/2;
midpos = aoh->getCenter();
} else if (geo->getTypeId() == Part::GeomArcOfParabola::getClassTypeId()) {
const Part::GeomArcOfParabola *aop = static_cast<const Part::GeomArcOfParabola *>(geo);
ra = aop->getFocal();
double startangle, endangle;
aop->getRange(startangle, endangle, /*emulateCCW=*/true);
Base::Vector3d majdir = - aop->getXAxisDir();
angle = atan2(majdir.y, majdir.x);
angleplus = (startangle + endangle)/2;
midpos = aop->getFocus();
} else
break;
if (geo->getTypeId() == Part::GeomEllipse::getClassTypeId() ||
geo->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId() ||
geo->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId()){
Base::Vector3d majDir, minDir, rvec;
majDir = Base::Vector3d(cos(angle),sin(angle),0);//direction of major axis of ellipse
minDir = Base::Vector3d(-majDir.y,majDir.x,0);//direction of minor axis of ellipse
rvec = (ra*cos(angleplus)) * majDir + (rb*sin(angleplus)) * minDir;
midpos += rvec;
rvec.Normalize();
norm = rvec;
dir = Base::Vector3d(-rvec.y,rvec.x,0);//DeepSOIC: I'm not sure what dir is supposed to mean.
}
else {
norm = Base::Vector3d(cos(angle),sin(angle),0);
dir = Base::Vector3d(-norm.y,norm.x,0);
midpos += ra*norm;
}
}
Base::Vector3d relpos = seekConstraintPosition(midpos, norm, dir, 2.5, editModeScenegraphNodes.constrGroup->getChild(i));
auto translation = static_cast<SoZoomTranslation *>(sep->getChild(static_cast<int>(ConstraintNodePosition::FirstTranslationIndex)));
translation->abPos = SbVec3f(midpos.x, midpos.y, zConstrH); //Absolute Reference
//Reference Position that is scaled according to zoom
translation->translation = SbVec3f(relpos.x, relpos.y, 0);
}
else {
assert(Constr->Second >= -extGeoCount && Constr->Second < intGeoCount);
assert(Constr->FirstPos != Sketcher::PointPos::none && Constr->SecondPos != Sketcher::PointPos::none);
Base::Vector3d midpos1, dir1, norm1;
Base::Vector3d midpos2, dir2, norm2;
midpos1 = geolistfacade.getPoint(Constr->First, Constr->FirstPos);
midpos2 = geolistfacade.getPoint(Constr->Second, Constr->SecondPos);
dir1 = (midpos2-midpos1).Normalize();
dir2 = -dir1;
norm1 = Base::Vector3d(-dir1.y,dir1.x,0.);
norm2 = norm1;
Base::Vector3d relpos1 = seekConstraintPosition(midpos1, norm1, dir1, 4.0, editModeScenegraphNodes.constrGroup->getChild(i));
auto translation = static_cast<SoZoomTranslation *>(sep->getChild(static_cast<int>(ConstraintNodePosition::FirstTranslationIndex)));
translation->abPos = SbVec3f(midpos1.x, midpos1.y, zConstrH);
translation->translation = SbVec3f(relpos1.x, relpos1.y, 0);
Base::Vector3d relpos2 = seekConstraintPosition(midpos2, norm2, dir2, 4.0, editModeScenegraphNodes.constrGroup->getChild(i));
Base::Vector3d secondPos = midpos2 - midpos1;
translation = static_cast<SoZoomTranslation *>(sep->getChild(static_cast<int>(ConstraintNodePosition::SecondTranslationIndex)));
translation->abPos = SbVec3f(secondPos.x, secondPos.y, zConstrH);
translation->translation = SbVec3f(relpos2.x -relpos1.x, relpos2.y -relpos1.y, 0);
}
}
break;
case Perpendicular:
{
assert(Constr->First >= -extGeoCount && Constr->First < intGeoCount);
assert(Constr->Second >= -extGeoCount && Constr->Second < intGeoCount);
// get the geometry
const Part::Geometry *geo1 = geolistfacade.getGeometryFromGeoId(Constr->First);
const Part::Geometry *geo2 = geolistfacade.getGeometryFromGeoId(Constr->Second);
Base::Vector3d midpos1, dir1, norm1;
Base::Vector3d midpos2, dir2, norm2;
bool twoIcons = false;//a very local flag. It's set to true to indicate that the second dir+norm are valid and should be used
if (Constr->Third != GeoEnum::GeoUndef || //perpty via point
Constr->FirstPos != Sketcher::PointPos::none) { //endpoint-to-curve or endpoint-to-endpoint perpty
int ptGeoId;
Sketcher::PointPos ptPosId;
do {//dummy loop to use break =) Maybe goto?
ptGeoId = Constr->First;
ptPosId = Constr->FirstPos;
if (ptPosId != Sketcher::PointPos::none) break;
ptGeoId = Constr->Second;
ptPosId = Constr->SecondPos;
if (ptPosId != Sketcher::PointPos::none) break;
ptGeoId = Constr->Third;
ptPosId = Constr->ThirdPos;
if (ptPosId != Sketcher::PointPos::none) break;
assert(0);//no point found!
} while (false);
midpos1 = geolistfacade.getPoint(ptGeoId, ptPosId);
norm1 = getNormal(geolistfacade, Constr->Second, midpos1);
// TODO: Check the method above. This was the old one making use of the solver.
//norm1 = getSolvedSketch().calculateNormalAtPoint(Constr->Second, midpos1.x, midpos1.y);
norm1.Normalize();
dir1 = norm1; dir1.RotateZ(-M_PI/2.0);
} else if (Constr->FirstPos == Sketcher::PointPos::none) {
if (geo1->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
const Part::GeomLineSegment *lineSeg1 = static_cast<const Part::GeomLineSegment *>(geo1);
midpos1 = ((lineSeg1->getEndPoint()+lineSeg1->getStartPoint())/2);
dir1 = (lineSeg1->getEndPoint()-lineSeg1->getStartPoint()).Normalize();
norm1 = Base::Vector3d(-dir1.y,dir1.x,0.);
} else if (geo1->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
const Part::GeomArcOfCircle *arc = static_cast<const Part::GeomArcOfCircle *>(geo1);
double startangle, endangle, midangle;
arc->getRange(startangle, endangle, /*emulateCCW=*/true);
midangle = (startangle + endangle)/2;
norm1 = Base::Vector3d(cos(midangle),sin(midangle),0);
dir1 = Base::Vector3d(-norm1.y,norm1.x,0);
midpos1 = arc->getCenter() + arc->getRadius() * norm1;
} else if (geo1->getTypeId() == Part::GeomCircle::getClassTypeId()) {
const Part::GeomCircle *circle = static_cast<const Part::GeomCircle *>(geo1);
norm1 = Base::Vector3d(cos(M_PI/4),sin(M_PI/4),0);
dir1 = Base::Vector3d(-norm1.y,norm1.x,0);
midpos1 = circle->getCenter() + circle->getRadius() * norm1;
} else
break;
if (geo2->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
const Part::GeomLineSegment *lineSeg2 = static_cast<const Part::GeomLineSegment *>(geo2);
midpos2 = ((lineSeg2->getEndPoint()+lineSeg2->getStartPoint())/2);
dir2 = (lineSeg2->getEndPoint()-lineSeg2->getStartPoint()).Normalize();
norm2 = Base::Vector3d(-dir2.y,dir2.x,0.);
} else if (geo2->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
const Part::GeomArcOfCircle *arc = static_cast<const Part::GeomArcOfCircle *>(geo2);
double startangle, endangle, midangle;
arc->getRange(startangle, endangle, /*emulateCCW=*/true);
midangle = (startangle + endangle)/2;
norm2 = Base::Vector3d(cos(midangle),sin(midangle),0);
dir2 = Base::Vector3d(-norm2.y,norm2.x,0);
midpos2 = arc->getCenter() + arc->getRadius() * norm2;
} else if (geo2->getTypeId() == Part::GeomCircle::getClassTypeId()) {
const Part::GeomCircle *circle = static_cast<const Part::GeomCircle *>(geo2);
norm2 = Base::Vector3d(cos(M_PI/4),sin(M_PI/4),0);
dir2 = Base::Vector3d(-norm2.y,norm2.x,0);
midpos2 = circle->getCenter() + circle->getRadius() * norm2;
} else
break;
twoIcons = true;
}
Base::Vector3d relpos1 = seekConstraintPosition(midpos1, norm1, dir1, 4.0, editModeScenegraphNodes.constrGroup->getChild(i));
auto translation = static_cast<SoZoomTranslation *>(sep->getChild(static_cast<int>(ConstraintNodePosition::FirstTranslationIndex)));
translation->abPos = SbVec3f(midpos1.x, midpos1.y, zConstrH);
translation->translation = SbVec3f(relpos1.x, relpos1.y, 0);
if (twoIcons) {
Base::Vector3d relpos2 = seekConstraintPosition(midpos2, norm2, dir2, 4.0, editModeScenegraphNodes.constrGroup->getChild(i));
Base::Vector3d secondPos = midpos2 - midpos1;
auto translation = static_cast<SoZoomTranslation *>(sep->getChild(static_cast<int>(ConstraintNodePosition::SecondTranslationIndex)));
translation->abPos = SbVec3f(secondPos.x, secondPos.y, zConstrH);
translation->translation = SbVec3f(relpos2.x -relpos1.x, relpos2.y -relpos1.y, 0);
}
}
break;
case Parallel:
case Equal:
{
assert(Constr->First >= -extGeoCount && Constr->First < intGeoCount);
assert(Constr->Second >= -extGeoCount && Constr->Second < intGeoCount);
// get the geometry
const Part::Geometry *geo1 = geolistfacade.getGeometryFromGeoId(Constr->First);
const Part::Geometry *geo2 = geolistfacade.getGeometryFromGeoId(Constr->Second);
Base::Vector3d midpos1, dir1, norm1;
Base::Vector3d midpos2, dir2, norm2;
if (geo1->getTypeId() != Part::GeomLineSegment::getClassTypeId() ||
geo2->getTypeId() != Part::GeomLineSegment::getClassTypeId()) {
if (Constr->Type == Equal) {
double r1a=0,r1b=0,r2a=0,r2b=0;
double angle1,angle1plus=0., angle2, angle2plus=0.;//angle1 = rotation of object as a whole; angle1plus = arc angle (t parameter for ellipses).
if (geo1->getTypeId() == Part::GeomCircle::getClassTypeId()) {
const Part::GeomCircle *circle = static_cast<const Part::GeomCircle *>(geo1);
r1a = circle->getRadius();
angle1 = M_PI/4;
midpos1 = circle->getCenter();
} else if (geo1->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
const Part::GeomArcOfCircle *arc = static_cast<const Part::GeomArcOfCircle *>(geo1);
r1a = arc->getRadius();
double startangle, endangle;
arc->getRange(startangle, endangle, /*emulateCCW=*/true);
angle1 = (startangle + endangle)/2;
midpos1 = arc->getCenter();
} else if (geo1->getTypeId() == Part::GeomEllipse::getClassTypeId()) {
const Part::GeomEllipse *ellipse = static_cast<const Part::GeomEllipse *>(geo1);
r1a = ellipse->getMajorRadius();
r1b = ellipse->getMinorRadius();
Base::Vector3d majdir = ellipse->getMajorAxisDir();
angle1 = atan2(majdir.y, majdir.x);
angle1plus = M_PI/4;
midpos1 = ellipse->getCenter();
} else if (geo1->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()) {
const Part::GeomArcOfEllipse *aoe = static_cast<const Part::GeomArcOfEllipse *>(geo1);
r1a = aoe->getMajorRadius();
r1b = aoe->getMinorRadius();
double startangle, endangle;
aoe->getRange(startangle, endangle, /*emulateCCW=*/true);
Base::Vector3d majdir = aoe->getMajorAxisDir();
angle1 = atan2(majdir.y, majdir.x);
angle1plus = (startangle + endangle)/2;
midpos1 = aoe->getCenter();
} else if (geo1->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId()) {
const Part::GeomArcOfHyperbola *aoh = static_cast<const Part::GeomArcOfHyperbola *>(geo1);
r1a = aoh->getMajorRadius();
r1b = aoh->getMinorRadius();
double startangle, endangle;
aoh->getRange(startangle, endangle, /*emulateCCW=*/true);
Base::Vector3d majdir = aoh->getMajorAxisDir();
angle1 = atan2(majdir.y, majdir.x);
angle1plus = (startangle + endangle)/2;
midpos1 = aoh->getCenter();
} else if (geo1->getTypeId() == Part::GeomArcOfParabola::getClassTypeId()) {
const Part::GeomArcOfParabola *aop = static_cast<const Part::GeomArcOfParabola *>(geo1);
r1a = aop->getFocal();
double startangle, endangle;
aop->getRange(startangle, endangle, /*emulateCCW=*/true);
Base::Vector3d majdir = - aop->getXAxisDir();
angle1 = atan2(majdir.y, majdir.x);
angle1plus = (startangle + endangle)/2;
midpos1 = aop->getFocus();
} else
break;
if (geo2->getTypeId() == Part::GeomCircle::getClassTypeId()) {
const Part::GeomCircle *circle = static_cast<const Part::GeomCircle *>(geo2);
r2a = circle->getRadius();
angle2 = M_PI/4;
midpos2 = circle->getCenter();
} else if (geo2->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
const Part::GeomArcOfCircle *arc = static_cast<const Part::GeomArcOfCircle *>(geo2);
r2a = arc->getRadius();
double startangle, endangle;
arc->getRange(startangle, endangle, /*emulateCCW=*/true);
angle2 = (startangle + endangle)/2;
midpos2 = arc->getCenter();
} else if (geo2->getTypeId() == Part::GeomEllipse::getClassTypeId()) {
const Part::GeomEllipse *ellipse = static_cast<const Part::GeomEllipse *>(geo2);
r2a = ellipse->getMajorRadius();
r2b = ellipse->getMinorRadius();
Base::Vector3d majdir = ellipse->getMajorAxisDir();
angle2 = atan2(majdir.y, majdir.x);
angle2plus = M_PI/4;
midpos2 = ellipse->getCenter();
} else if (geo2->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()) {
const Part::GeomArcOfEllipse *aoe = static_cast<const Part::GeomArcOfEllipse *>(geo2);
r2a = aoe->getMajorRadius();
r2b = aoe->getMinorRadius();
double startangle, endangle;
aoe->getRange(startangle, endangle, /*emulateCCW=*/true);
Base::Vector3d majdir = aoe->getMajorAxisDir();
angle2 = atan2(majdir.y, majdir.x);
angle2plus = (startangle + endangle)/2;
midpos2 = aoe->getCenter();
} else if (geo2->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId()) {
const Part::GeomArcOfHyperbola *aoh = static_cast<const Part::GeomArcOfHyperbola *>(geo2);
r2a = aoh->getMajorRadius();
r2b = aoh->getMinorRadius();
double startangle, endangle;
aoh->getRange(startangle, endangle, /*emulateCCW=*/true);
Base::Vector3d majdir = aoh->getMajorAxisDir();
angle2 = atan2(majdir.y, majdir.x);
angle2plus = (startangle + endangle)/2;
midpos2 = aoh->getCenter();
} else if (geo2->getTypeId() == Part::GeomArcOfParabola::getClassTypeId()) {
const Part::GeomArcOfParabola *aop = static_cast<const Part::GeomArcOfParabola *>(geo2);
r2a = aop->getFocal();
double startangle, endangle;
aop->getRange(startangle, endangle, /*emulateCCW=*/true);
Base::Vector3d majdir = -aop->getXAxisDir();
angle2 = atan2(majdir.y, majdir.x);
angle2plus = (startangle + endangle)/2;
midpos2 = aop->getFocus();
} else
break;
if (geo1->getTypeId() == Part::GeomEllipse::getClassTypeId() ||
geo1->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId() ||
geo1->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId() ){
Base::Vector3d majDir, minDir, rvec;
majDir = Base::Vector3d(cos(angle1),sin(angle1),0);//direction of major axis of ellipse
minDir = Base::Vector3d(-majDir.y,majDir.x,0);//direction of minor axis of ellipse
rvec = (r1a*cos(angle1plus)) * majDir + (r1b*sin(angle1plus)) * minDir;
midpos1 += rvec;
rvec.Normalize();
norm1 = rvec;
dir1 = Base::Vector3d(-rvec.y,rvec.x,0);//DeepSOIC: I'm not sure what dir is supposed to mean.
}
else {
norm1 = Base::Vector3d(cos(angle1),sin(angle1),0);
dir1 = Base::Vector3d(-norm1.y,norm1.x,0);
midpos1 += r1a*norm1;
}
if (geo2->getTypeId() == Part::GeomEllipse::getClassTypeId() ||
geo2->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId() ||
geo2->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId()) {
Base::Vector3d majDir, minDir, rvec;
majDir = Base::Vector3d(cos(angle2),sin(angle2),0);//direction of major axis of ellipse
minDir = Base::Vector3d(-majDir.y,majDir.x,0);//direction of minor axis of ellipse
rvec = (r2a*cos(angle2plus)) * majDir + (r2b*sin(angle2plus)) * minDir;
midpos2 += rvec;
rvec.Normalize();
norm2 = rvec;
dir2 = Base::Vector3d(-rvec.y,rvec.x,0);
}
else {
norm2 = Base::Vector3d(cos(angle2),sin(angle2),0);
dir2 = Base::Vector3d(-norm2.y,norm2.x,0);
midpos2 += r2a*norm2;
}
} else // Parallel can only apply to a GeomLineSegment
break;
} else {
const Part::GeomLineSegment *lineSeg1 = static_cast<const Part::GeomLineSegment *>(geo1);
const Part::GeomLineSegment *lineSeg2 = static_cast<const Part::GeomLineSegment *>(geo2);
// calculate the half distance between the start and endpoint
midpos1 = ((lineSeg1->getEndPoint()+lineSeg1->getStartPoint())/2);
midpos2 = ((lineSeg2->getEndPoint()+lineSeg2->getStartPoint())/2);
//Get a set of vectors perpendicular and tangential to these
dir1 = (lineSeg1->getEndPoint()-lineSeg1->getStartPoint()).Normalize();
dir2 = (lineSeg2->getEndPoint()-lineSeg2->getStartPoint()).Normalize();
norm1 = Base::Vector3d(-dir1.y,dir1.x,0.);
norm2 = Base::Vector3d(-dir2.y,dir2.x,0.);
}
Base::Vector3d relpos1 = seekConstraintPosition(midpos1, norm1, dir1, 4.0, editModeScenegraphNodes.constrGroup->getChild(i));
Base::Vector3d relpos2 = seekConstraintPosition(midpos2, norm2, dir2, 4.0, editModeScenegraphNodes.constrGroup->getChild(i));
auto translation = static_cast<SoZoomTranslation *>(sep->getChild(static_cast<int>(ConstraintNodePosition::FirstTranslationIndex)));
translation->abPos = SbVec3f(midpos1.x, midpos1.y, zConstrH); //Absolute Reference
//Reference Position that is scaled according to zoom
translation->translation = SbVec3f(relpos1.x, relpos1.y, 0);
Base::Vector3d secondPos = midpos2 - midpos1;
translation = static_cast<SoZoomTranslation *>(sep->getChild(static_cast<int>(ConstraintNodePosition::SecondTranslationIndex)));
translation->abPos = SbVec3f(secondPos.x, secondPos.y, zConstrH); //Absolute Reference
//Reference Position that is scaled according to zoom
translation->translation = SbVec3f(relpos2.x - relpos1.x, relpos2.y -relpos1.y, 0);
}
break;
case Distance:
case DistanceX:
case DistanceY:
{
assert(Constr->First >= -extGeoCount && Constr->First < intGeoCount);
Base::Vector3d pnt1(0.,0.,0.), pnt2(0.,0.,0.);
if (Constr->SecondPos != Sketcher::PointPos::none) { // point to point distance
pnt1 = geolistfacade.getPoint(Constr->First, Constr->FirstPos);
pnt2 = geolistfacade.getPoint(Constr->Second, Constr->SecondPos);
} else if (Constr->Second != GeoEnum::GeoUndef) { // point to line distance
pnt1 = geolistfacade.getPoint(Constr->First, Constr->FirstPos);
const Part::Geometry *geo = geolistfacade.getGeometryFromGeoId(Constr->Second);
if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
const Part::GeomLineSegment *lineSeg = static_cast<const Part::GeomLineSegment *>(geo);
Base::Vector3d l2p1 = lineSeg->getStartPoint();
Base::Vector3d l2p2 = lineSeg->getEndPoint();
// calculate the projection of p1 onto line2
pnt2.ProjectToLine(pnt1-l2p1, l2p2-l2p1);
pnt2 += pnt1;
} else
break;
} else if (Constr->FirstPos != Sketcher::PointPos::none) {
pnt2 = geolistfacade.getPoint(Constr->First, Constr->FirstPos);
} else if (Constr->First != GeoEnum::GeoUndef) {
const Part::Geometry *geo = geolistfacade.getGeometryFromGeoId(Constr->First);
if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
const Part::GeomLineSegment *lineSeg = static_cast<const Part::GeomLineSegment *>(geo);
pnt1 = lineSeg->getStartPoint();
pnt2 = lineSeg->getEndPoint();
} else
break;
} else
break;
SoDatumLabel *asciiText = static_cast<SoDatumLabel *>(sep->getChild(static_cast<int>(ConstraintNodePosition::DatumLabelIndex)));
// Get presentation string (w/o units if option is set)
asciiText->string = SbString( getPresentationString(Constr).toUtf8().constData() );
if (Constr->Type == Distance)
asciiText->datumtype = SoDatumLabel::DISTANCE;
else if (Constr->Type == DistanceX)
asciiText->datumtype = SoDatumLabel::DISTANCEX;
else if (Constr->Type == DistanceY)
asciiText->datumtype = SoDatumLabel::DISTANCEY;
// Assign the Datum Points
asciiText->pnts.setNum(2);
SbVec3f *verts = asciiText->pnts.startEditing();
verts[0] = SbVec3f (pnt1.x, pnt1.y, zConstrH);
verts[1] = SbVec3f (pnt2.x, pnt2.y, zConstrH);
asciiText->pnts.finishEditing();
//Assign the Label Distance
asciiText->param1 = Constr->LabelDistance;
asciiText->param2 = Constr->LabelPosition;
}
break;
case PointOnObject:
case Tangent:
case SnellsLaw:
{
assert(Constr->First >= -extGeoCount && Constr->First < intGeoCount);
assert(Constr->Second >= -extGeoCount && Constr->Second < intGeoCount);
Base::Vector3d pos, relPos;
if (Constr->Type == PointOnObject ||
Constr->Type == SnellsLaw ||
(Constr->Type == Tangent && Constr->Third != GeoEnum::GeoUndef) || //Tangency via point
(Constr->Type == Tangent && Constr->FirstPos != Sketcher::PointPos::none) //endpoint-to-curve or endpoint-to-endpoint tangency
) {
//find the point of tangency/point that is on object
//just any point among first/second/third should be OK
int ptGeoId;
Sketcher::PointPos ptPosId;
do {//dummy loop to use break =) Maybe goto?
ptGeoId = Constr->First;
ptPosId = Constr->FirstPos;
if (ptPosId != Sketcher::PointPos::none) break;
ptGeoId = Constr->Second;
ptPosId = Constr->SecondPos;
if (ptPosId != Sketcher::PointPos::none) break;
ptGeoId = Constr->Third;
ptPosId = Constr->ThirdPos;
if (ptPosId != Sketcher::PointPos::none) break;
assert(0);//no point found!
} while (false);
pos = geolistfacade.getPoint(ptGeoId, ptPosId);
auto norm = getNormal(geolistfacade, Constr->Second, pos);
// TODO: Check substitution
// Base::Vector3d norm = getSolvedSketch().calculateNormalAtPoint(Constr->Second, pos.x, pos.y);
norm.Normalize();
Base::Vector3d dir = norm; dir.RotateZ(-M_PI/2.0);
relPos = seekConstraintPosition(pos, norm, dir, 2.5, editModeScenegraphNodes.constrGroup->getChild(i));
auto translation = static_cast<SoZoomTranslation *>(sep->getChild(static_cast<int>(ConstraintNodePosition::FirstTranslationIndex)));
translation->abPos = SbVec3f(pos.x, pos.y, zConstrH); //Absolute Reference
translation->translation = SbVec3f(relPos.x, relPos.y, 0);
}
else if (Constr->Type == Tangent) {
// get the geometry
const Part::Geometry *geo1 = geolistfacade.getGeometryFromGeoId(Constr->First);
const Part::Geometry *geo2 = geolistfacade.getGeometryFromGeoId(Constr->Second);
if (geo1->getTypeId() == Part::GeomLineSegment::getClassTypeId() &&
geo2->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
const Part::GeomLineSegment *lineSeg1 = static_cast<const Part::GeomLineSegment *>(geo1);
const Part::GeomLineSegment *lineSeg2 = static_cast<const Part::GeomLineSegment *>(geo2);
// tangency between two lines
Base::Vector3d midpos1 = ((lineSeg1->getEndPoint()+lineSeg1->getStartPoint())/2);
Base::Vector3d midpos2 = ((lineSeg2->getEndPoint()+lineSeg2->getStartPoint())/2);
Base::Vector3d dir1 = (lineSeg1->getEndPoint()-lineSeg1->getStartPoint()).Normalize();
Base::Vector3d dir2 = (lineSeg2->getEndPoint()-lineSeg2->getStartPoint()).Normalize();
Base::Vector3d norm1 = Base::Vector3d(-dir1.y,dir1.x,0.f);
Base::Vector3d norm2 = Base::Vector3d(-dir2.y,dir2.x,0.f);
Base::Vector3d relpos1 = seekConstraintPosition(midpos1, norm1, dir1, 4.0, editModeScenegraphNodes.constrGroup->getChild(i));
Base::Vector3d relpos2 = seekConstraintPosition(midpos2, norm2, dir2, 4.0, editModeScenegraphNodes.constrGroup->getChild(i));
auto translation = static_cast<SoZoomTranslation *>(sep->getChild(static_cast<int>(ConstraintNodePosition::FirstTranslationIndex)));
translation->abPos = SbVec3f(midpos1.x, midpos1.y, zConstrH); //Absolute Reference
translation->translation = SbVec3f(relpos1.x, relpos1.y, 0);
Base::Vector3d secondPos = midpos2 - midpos1;
translation = static_cast<SoZoomTranslation *>(sep->getChild(static_cast<int>(ConstraintNodePosition::SecondTranslationIndex)));
translation->abPos = SbVec3f(secondPos.x, secondPos.y, zConstrH); //Absolute Reference
translation->translation = SbVec3f(relpos2.x -relpos1.x, relpos2.y -relpos1.y, 0);
break;
}
else if (geo2->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
std::swap(geo1,geo2);
}
if (geo1->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
const Part::GeomLineSegment *lineSeg = static_cast<const Part::GeomLineSegment *>(geo1);
Base::Vector3d dir = (lineSeg->getEndPoint() - lineSeg->getStartPoint()).Normalize();
Base::Vector3d norm(-dir.y, dir.x, 0);
if (geo2->getTypeId()== Part::GeomCircle::getClassTypeId()) {
const Part::GeomCircle *circle = static_cast<const Part::GeomCircle *>(geo2);
// tangency between a line and a circle
float length = (circle->getCenter() - lineSeg->getStartPoint())*dir;
pos = lineSeg->getStartPoint() + dir * length;
relPos = norm * 1; //TODO Huh?
}
else if (geo2->getTypeId()== Part::GeomEllipse::getClassTypeId() ||
geo2->getTypeId()== Part::GeomArcOfEllipse::getClassTypeId()) {
Base::Vector3d center;
if (geo2->getTypeId()== Part::GeomEllipse::getClassTypeId()){
const Part::GeomEllipse *ellipse = static_cast<const Part::GeomEllipse *>(geo2);
center=ellipse->getCenter();
} else {
const Part::GeomArcOfEllipse *aoc = static_cast<const Part::GeomArcOfEllipse *>(geo2);
center=aoc->getCenter();
}
// tangency between a line and an ellipse
float length = (center - lineSeg->getStartPoint())*dir;
pos = lineSeg->getStartPoint() + dir * length;
relPos = norm * 1;
}
else if (geo2->getTypeId()== Part::GeomArcOfCircle::getClassTypeId()) {
const Part::GeomArcOfCircle *arc = static_cast<const Part::GeomArcOfCircle *>(geo2);
// tangency between a line and an arc
float length = (arc->getCenter() - lineSeg->getStartPoint())*dir;
pos = lineSeg->getStartPoint() + dir * length;
relPos = norm * 1; //TODO Huh?
}
}
if (geo1->getTypeId()== Part::GeomCircle::getClassTypeId() &&
geo2->getTypeId()== Part::GeomCircle::getClassTypeId()) {
const Part::GeomCircle *circle1 = static_cast<const Part::GeomCircle *>(geo1);
const Part::GeomCircle *circle2 = static_cast<const Part::GeomCircle *>(geo2);
// tangency between two cicles
Base::Vector3d dir = (circle2->getCenter() - circle1->getCenter()).Normalize();
pos = circle1->getCenter() + dir * circle1->getRadius();
relPos = dir * 1;
}
else if (geo2->getTypeId()== Part::GeomCircle::getClassTypeId()) {
std::swap(geo1,geo2);
}
if (geo1->getTypeId()== Part::GeomCircle::getClassTypeId() &&
geo2->getTypeId()== Part::GeomArcOfCircle::getClassTypeId()) {
const Part::GeomCircle *circle = static_cast<const Part::GeomCircle *>(geo1);
const Part::GeomArcOfCircle *arc = static_cast<const Part::GeomArcOfCircle *>(geo2);
// tangency between a circle and an arc
Base::Vector3d dir = (arc->getCenter() - circle->getCenter()).Normalize();
pos = circle->getCenter() + dir * circle->getRadius();
relPos = dir * 1;
}
else if (geo1->getTypeId()== Part::GeomArcOfCircle::getClassTypeId() &&
geo2->getTypeId()== Part::GeomArcOfCircle::getClassTypeId()) {
const Part::GeomArcOfCircle *arc1 = static_cast<const Part::GeomArcOfCircle *>(geo1);
const Part::GeomArcOfCircle *arc2 = static_cast<const Part::GeomArcOfCircle *>(geo2);
// tangency between two arcs
Base::Vector3d dir = (arc2->getCenter() - arc1->getCenter()).Normalize();
pos = arc1->getCenter() + dir * arc1->getRadius();
relPos = dir * 1;
}
auto translation = static_cast<SoZoomTranslation *>(sep->getChild(static_cast<int>(ConstraintNodePosition::FirstTranslationIndex)));
translation->abPos = SbVec3f(pos.x, pos.y, zConstrH); //Absolute Reference
translation->translation = SbVec3f(relPos.x, relPos.y, 0);
}
}
break;
case Symmetric:
{
assert(Constr->First >= -extGeoCount && Constr->First < intGeoCount);
assert(Constr->Second >= -extGeoCount && Constr->Second < intGeoCount);
Base::Vector3d pnt1 = geolistfacade.getPoint(Constr->First, Constr->FirstPos);
Base::Vector3d pnt2 = geolistfacade.getPoint(Constr->Second, Constr->SecondPos);
SbVec3f p1(pnt1.x, pnt1.y, zConstrH);
SbVec3f p2(pnt2.x, pnt2.y, zConstrH);
SbVec3f dir = (p2-p1);
dir.normalize();
SbVec3f norm (-dir[1],dir[0],0);
SoDatumLabel *asciiText = static_cast<SoDatumLabel *>(sep->getChild(static_cast<int>(ConstraintNodePosition::DatumLabelIndex)));
asciiText->datumtype = SoDatumLabel::SYMMETRIC;
asciiText->pnts.setNum(2);
SbVec3f *verts = asciiText->pnts.startEditing();
verts[0] = p1;
verts[1] = p2;
asciiText->pnts.finishEditing();
auto translation = static_cast<SoTranslation *>(sep->getChild(static_cast<int>(ConstraintNodePosition::FirstTranslationIndex)));
translation->translation = (p1 + p2)/2;
}
break;
case Angle:
{
assert(Constr->First >= -extGeoCount && Constr->First < intGeoCount);
assert((Constr->Second >= -extGeoCount && Constr->Second < intGeoCount) ||
Constr->Second == GeoEnum::GeoUndef);
SbVec3f p0;
double startangle,range,endangle;
if (Constr->Second != GeoEnum::GeoUndef) {
Base::Vector3d dir1, dir2;
if (Constr->Third == GeoEnum::GeoUndef) { //angle between two lines
const Part::Geometry *geo1 = geolistfacade.getGeometryFromGeoId(Constr->First);
const Part::Geometry *geo2 = geolistfacade.getGeometryFromGeoId(Constr->Second);
if (geo1->getTypeId() != Part::GeomLineSegment::getClassTypeId() ||
geo2->getTypeId() != Part::GeomLineSegment::getClassTypeId())
break;
const Part::GeomLineSegment *lineSeg1 = static_cast<const Part::GeomLineSegment *>(geo1);
const Part::GeomLineSegment *lineSeg2 = static_cast<const Part::GeomLineSegment *>(geo2);
bool flip1 = (Constr->FirstPos == PointPos::end);
bool flip2 = (Constr->SecondPos == PointPos::end);
dir1 = (flip1 ? -1. : 1.) * (lineSeg1->getEndPoint()-lineSeg1->getStartPoint());
dir2 = (flip2 ? -1. : 1.) * (lineSeg2->getEndPoint()-lineSeg2->getStartPoint());
Base::Vector3d pnt1 = flip1 ? lineSeg1->getEndPoint() : lineSeg1->getStartPoint();
Base::Vector3d pnt2 = flip2 ? lineSeg2->getEndPoint() : lineSeg2->getStartPoint();
// line-line intersection
{
double det = dir1.x*dir2.y - dir1.y*dir2.x;
if ((det > 0 ? det : -det) < 1e-10) {
// lines are coincident (or parallel) and in this case the center
// of the point pairs with the shortest distance is used
Base::Vector3d p1[2], p2[2];
p1[0] = lineSeg1->getStartPoint();
p1[1] = lineSeg1->getEndPoint();
p2[0] = lineSeg2->getStartPoint();
p2[1] = lineSeg2->getEndPoint();
double length = DBL_MAX;
for (int i=0; i <= 1; i++) {
for (int j=0; j <= 1; j++) {
double tmp = (p2[j]-p1[i]).Length();
if (tmp < length) {
length = tmp;
p0.setValue((p2[j].x+p1[i].x)/2,(p2[j].y+p1[i].y)/2,0);
}
}
}
}
else {
double c1 = dir1.y*pnt1.x - dir1.x*pnt1.y;
double c2 = dir2.y*pnt2.x - dir2.x*pnt2.y;
double x = (dir1.x*c2 - dir2.x*c1)/det;
double y = (dir1.y*c2 - dir2.y*c1)/det;
p0 = SbVec3f(x,y,0);
}
}
range = Constr->getValue(); // WYSIWYG
startangle = atan2(dir1.y,dir1.x);
}
else {//angle-via-point
Base::Vector3d p = geolistfacade.getPoint(Constr->Third, Constr->ThirdPos);
p0 = SbVec3f(p.x, p.y, 0);
dir1 = getNormal(geolistfacade, Constr->First, p);
// TODO: Check
// dir1 = getSolvedSketch().calculateNormalAtPoint(Constr->First, p.x, p.y);
dir1.RotateZ(-M_PI/2);//convert to vector of tangency by rotating
dir2 = getNormal(geolistfacade, Constr->Second, p);
// TODO: Check
// dir2 = getSolvedSketch().calculateNormalAtPoint(Constr->Second, p.x, p.y);
dir2.RotateZ(-M_PI/2);
startangle = atan2(dir1.y,dir1.x);
range = atan2(dir1.x*dir2.y-dir1.y*dir2.x,
dir1.x*dir2.x+dir1.y*dir2.y);
}
endangle = startangle + range;
} else if (Constr->First != GeoEnum::GeoUndef) {
const Part::Geometry *geo = geolistfacade.getGeometryFromGeoId(Constr->First);
if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
const Part::GeomLineSegment *lineSeg = static_cast<const Part::GeomLineSegment *>(geo);
p0 = Base::convertTo<SbVec3f>((lineSeg->getEndPoint()+lineSeg->getStartPoint())/2);
Base::Vector3d dir = lineSeg->getEndPoint()-lineSeg->getStartPoint();
startangle = 0.;
range = atan2(dir.y,dir.x);
endangle = startangle + range;
}
else if (geo->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
const Part::GeomArcOfCircle *arc = static_cast<const Part::GeomArcOfCircle *>(geo);
p0 = Base::convertTo<SbVec3f>(arc->getCenter());
arc->getRange(startangle, endangle,/*emulateCCWXY=*/true);
range = endangle - startangle;
}
else {
break;
}
} else
break;
SoDatumLabel *asciiText = static_cast<SoDatumLabel *>(sep->getChild(static_cast<int>(ConstraintNodePosition::DatumLabelIndex)));
asciiText->string = SbString(getPresentationString(Constr).toUtf8().constData());
asciiText->datumtype = SoDatumLabel::ANGLE;
asciiText->param1 = Constr->LabelDistance;
asciiText->param2 = startangle;
asciiText->param3 = range;
asciiText->pnts.setNum(2);
SbVec3f *verts = asciiText->pnts.startEditing();
verts[0] = p0;
asciiText->pnts.finishEditing();
}
break;
case Diameter:
{
assert(Constr->First >= -extGeoCount && Constr->First < intGeoCount);
Base::Vector3d pnt1(0.,0.,0.), pnt2(0.,0.,0.);
if (Constr->First != GeoEnum::GeoUndef) {
const Part::Geometry *geo = geolistfacade.getGeometryFromGeoId(Constr->First);
if (geo->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
const Part::GeomArcOfCircle *arc = static_cast<const Part::GeomArcOfCircle *>(geo);
double radius = arc->getRadius();
double angle = (double) Constr->LabelPosition;
if (angle == 10) {
double startangle, endangle;
arc->getRange(startangle, endangle, /*emulateCCW=*/true);
angle = (startangle + endangle)/2;
}
Base::Vector3d center = arc->getCenter();
pnt1 = center - radius * Base::Vector3d(cos(angle),sin(angle),0.);
pnt2 = center + radius * Base::Vector3d(cos(angle),sin(angle),0.);
}
else if (geo->getTypeId() == Part::GeomCircle::getClassTypeId()) {
const Part::GeomCircle *circle = static_cast<const Part::GeomCircle *>(geo);
double radius = circle->getRadius();
double angle = (double) Constr->LabelPosition;
if (angle == 10) {
angle = 0;
}
Base::Vector3d center = circle->getCenter();
pnt1 = center - radius * Base::Vector3d(cos(angle),sin(angle),0.);
pnt2 = center + radius * Base::Vector3d(cos(angle),sin(angle),0.);
}
else
break;
} else
break;
SbVec3f p1(pnt1.x, pnt1.y, zConstrH);
SbVec3f p2(pnt2.x, pnt2.y, zConstrH);
SoDatumLabel *asciiText = static_cast<SoDatumLabel *>(sep->getChild(static_cast<int>(ConstraintNodePosition::DatumLabelIndex)));
// Get display string with units hidden if so requested
asciiText->string = SbString( getPresentationString(Constr).toUtf8().constData() );
asciiText->datumtype = SoDatumLabel::DIAMETER;
asciiText->param1 = Constr->LabelDistance;
asciiText->param2 = Constr->LabelPosition;
asciiText->pnts.setNum(2);
SbVec3f *verts = asciiText->pnts.startEditing();
verts[0] = p1;
verts[1] = p2;
asciiText->pnts.finishEditing();
}
break;
case Weight:
case Radius:
{
assert(Constr->First >= -extGeoCount && Constr->First < intGeoCount);
Base::Vector3d pnt1(0.,0.,0.), pnt2(0.,0.,0.);
if (Constr->First != GeoEnum::GeoUndef) {
const Part::Geometry *geo = geolistfacade.getGeometryFromGeoId(Constr->First);
if (geo->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
const Part::GeomArcOfCircle *arc = static_cast<const Part::GeomArcOfCircle *>(geo);
double radius = arc->getRadius();
double angle = (double) Constr->LabelPosition;
if (angle == 10) {
double startangle, endangle;
arc->getRange(startangle, endangle, /*emulateCCW=*/true);
angle = (startangle + endangle)/2;
}
pnt1 = arc->getCenter();
pnt2 = pnt1 + radius * Base::Vector3d(cos(angle),sin(angle),0.);
}
else if (geo->getTypeId() == Part::GeomCircle::getClassTypeId()) {
const Part::GeomCircle *circle = static_cast<const Part::GeomCircle *>(geo);
auto gf = GeometryFacade::getFacade(geo);
double radius;
radius = circle->getRadius();
double angle = (double) Constr->LabelPosition;
if (angle == 10) {
angle = 0;
}
pnt1 = circle->getCenter();
pnt2 = pnt1 + radius * Base::Vector3d(cos(angle),sin(angle),0.);
}
else
break;
} else
break;
SbVec3f p1(pnt1.x, pnt1.y, zConstrH);
SbVec3f p2(pnt2.x, pnt2.y, zConstrH);
SoDatumLabel *asciiText = static_cast<SoDatumLabel *>(sep->getChild(static_cast<int>(ConstraintNodePosition::DatumLabelIndex)));
// Get display string with units hidden if so requested
if (Constr->Type == Weight)
asciiText->string = SbString( QString::number(Constr->getValue()).toStdString().c_str());
else
asciiText->string = SbString( getPresentationString(Constr).toUtf8().constData() );
asciiText->datumtype = SoDatumLabel::RADIUS;
asciiText->param1 = Constr->LabelDistance;
asciiText->param2 = Constr->LabelPosition;
asciiText->pnts.setNum(2);
SbVec3f *verts = asciiText->pnts.startEditing();
verts[0] = p1;
verts[1] = p2;
asciiText->pnts.finishEditing();
}
break;
case Coincident: // nothing to do for coincident
case None:
case InternalAlignment:
case NumConstraintTypes:
break;
}
} catch (Base::Exception &e) {
Base::Console().Error("Exception during draw: %s\n", e.what());
e.ReportException();
} catch (...){
Base::Console().Error("Exception during draw: unknown\n");
}
}
}
Base::Vector3d EditModeConstraintCoinManager::seekConstraintPosition(const Base::Vector3d &origPos,
const Base::Vector3d &norm,
const Base::Vector3d &dir, float step,
const SoNode *constraint)
{
auto rp = ViewProviderSketchCoinAttorney::getRayPickAction(viewProvider);
float scaled_step = step * ViewProviderSketchCoinAttorney::getScaleFactor(viewProvider);
int multiplier = 0;
Base::Vector3d relPos, freePos;
bool isConstraintAtPosition = true;
while (isConstraintAtPosition && multiplier < 10) {
// Calculate new position of constraint
relPos = norm * 0.5f + dir * multiplier;
freePos = origPos + relPos * scaled_step;
// Prevent crash : https://forum.freecadweb.org/viewtopic.php?f=8&t=65305
if (!rp) {
return relPos * step;
}
rp->setRadius(0.1f);
rp->setPickAll(true);
rp->setRay(SbVec3f(freePos.x, freePos.y, -1.f), SbVec3f(0, 0, 1) );
//problem
rp->apply(editModeScenegraphNodes.constrGroup); // We could narrow it down to just the SoGroup containing the constraints
// returns a copy of the point
SoPickedPoint *pp = rp->getPickedPoint();
const SoPickedPointList ppl = rp->getPickedPointList();
if (ppl.getLength() <= 1 && pp) {
SoPath *path = pp->getPath();
int length = path->getLength();
SoNode *tailFather1 = path->getNode(length-2);
SoNode *tailFather2 = path->getNode(length-3);
// checking if a constraint is the same as the one selected
if (tailFather1 == constraint || tailFather2 == constraint)
isConstraintAtPosition = false;
}
else {
isConstraintAtPosition = false;
}
multiplier *= -1; // search in both sides
if (multiplier >= 0)
multiplier++; // Increment the multiplier
}
if (multiplier == 10)
relPos = norm * 0.5f; // no free position found
return relPos * step;
}
void EditModeConstraintCoinManager::updateConstraintColor(const std::vector<Sketcher::Constraint *> &constraints)
{
// Because coincident constraints are selected using the point color, we need to edit the point materials.
std::vector<int> PtNum;
std::vector<SbColor *> pcolor; // point color
std::vector<int> CurvNum;
std::vector<SbColor *> color; // curve color
for(int l=0; l<geometryLayerParameters.CoinLayers; l++) {
PtNum.push_back(editModeScenegraphNodes.PointsMaterials[l]->diffuseColor.getNum());
pcolor.push_back(editModeScenegraphNodes.PointsMaterials[l]->diffuseColor.startEditing());
CurvNum.push_back(editModeScenegraphNodes.CurvesMaterials[l]->diffuseColor.getNum());
color.push_back(editModeScenegraphNodes.CurvesMaterials[l]->diffuseColor.startEditing());
}
int maxNumberOfConstraints = std::min(editModeScenegraphNodes.constrGroup->getNumChildren(), static_cast<int>(constraints.size()));
// colors of the constraints
for (int i = 0; i < maxNumberOfConstraints; i++) {
SoSeparator *s = static_cast<SoSeparator *>(editModeScenegraphNodes.constrGroup->getChild(i));
// Check Constraint Type
Sketcher::Constraint* constraint = constraints[i];
ConstraintType type = constraint->Type;
// It may happen that color updates are triggered by programmatic selection changes before a command final update. Then
// constraints may have been changed and the color will be updated as part
if (type != vConstrType[i])
break;
bool hasDatumLabel = (type == Sketcher::Angle ||
type == Sketcher::Radius ||
type == Sketcher::Diameter ||
type == Sketcher::Weight ||
type == Sketcher::Symmetric ||
type == Sketcher::Distance ||
type == Sketcher::DistanceX ||
type == Sketcher::DistanceY);
// Non DatumLabel Nodes will have a material excluding coincident
bool hasMaterial = false;
SoMaterial *m = nullptr;
if (!hasDatumLabel && type != Sketcher::Coincident && type != Sketcher::InternalAlignment) {
hasMaterial = true;
m = static_cast<SoMaterial *>(s->getChild(static_cast<int>(ConstraintNodePosition::MaterialIndex)));
}
auto selectpoint = [this, pcolor, PtNum](int geoid, Sketcher::PointPos pos){
if (geoid >= 0) {
auto multifieldIndex = coinMapping.getIndexLayer(geoid, pos);
if (multifieldIndex != MultiFieldId::Invalid) {
int index = multifieldIndex.fieldIndex;
int layer = multifieldIndex.layerId;
if (layer < static_cast<int>(PtNum.size()) && index >= 0 && index < PtNum[layer]) {
pcolor[layer][index] = drawingParameters.SelectColor;
}
}
}
};
auto selectline = [this, color, CurvNum](int geoid){
if (geoid >= 0) {
auto multifieldIndex = coinMapping.getIndexLayer(geoid, Sketcher::PointPos::none);
if (multifieldIndex != MultiFieldId::Invalid) {
int index = multifieldIndex.fieldIndex;
int layer = multifieldIndex.layerId;
if (layer < static_cast<int>(CurvNum.size()) && index >= 0 && index < CurvNum[layer]) {
color[layer][index] = drawingParameters.SelectColor;
}
}
}
};
if (ViewProviderSketchCoinAttorney::isConstraintSelected(viewProvider, i)) {
if (hasDatumLabel) {
SoDatumLabel *l = static_cast<SoDatumLabel *>(s->getChild(static_cast<int>(ConstraintNodePosition::DatumLabelIndex)));
l->textColor = drawingParameters.SelectColor;
} else if (hasMaterial) {
m->diffuseColor = drawingParameters.SelectColor;
} else if (type == Sketcher::Coincident) {
selectpoint(constraint->First, constraint->FirstPos);
selectpoint(constraint->Second, constraint->SecondPos);
} else if (type == Sketcher::InternalAlignment) {
switch(constraint->AlignmentType) {
case EllipseMajorDiameter:
case EllipseMinorDiameter:
case HyperbolaMajor:
case HyperbolaMinor:
case ParabolaFocalAxis:
{
selectline(constraint->First);
}
break;
case EllipseFocus1:
case EllipseFocus2:
case HyperbolaFocus:
case ParabolaFocus:
case BSplineControlPoint:
case BSplineKnotPoint:
{
selectpoint(constraint->First, constraint->FirstPos);
}
break;
default:
break;
}
}
} else if (ViewProviderSketchCoinAttorney::isConstraintPreselected(viewProvider,i)) {
if (hasDatumLabel) {
SoDatumLabel *l = static_cast<SoDatumLabel *>(s->getChild(static_cast<int>(ConstraintNodePosition::DatumLabelIndex)));
l->textColor = drawingParameters.PreselectColor;
} else if (hasMaterial) {
m->diffuseColor = drawingParameters.PreselectColor;
}
}
else {
if (hasDatumLabel) {
SoDatumLabel *l = static_cast<SoDatumLabel *>(s->getChild(static_cast<int>(ConstraintNodePosition::DatumLabelIndex)));
l->textColor = constraint->isActive ?
ViewProviderSketchCoinAttorney::constraintHasExpression(viewProvider, i) ?
drawingParameters.ExprBasedConstrDimColor
:(constraint->isDriving ?
drawingParameters.ConstrDimColor
: drawingParameters.NonDrivingConstrDimColor)
:drawingParameters.DeactivatedConstrDimColor;
} else if (hasMaterial) {
m->diffuseColor = constraint->isActive ?
(constraint->isDriving ?
drawingParameters.ConstrDimColor
:drawingParameters.NonDrivingConstrDimColor)
:drawingParameters.DeactivatedConstrDimColor;
}
}
}
for(int l=0; l<geometryLayerParameters.CoinLayers; l++) {
editModeScenegraphNodes.PointsMaterials[l]->diffuseColor.finishEditing();
editModeScenegraphNodes.CurvesMaterials[l]->diffuseColor.finishEditing();
}
}
void EditModeConstraintCoinManager::rebuildConstraintNodes()
{
auto geolistfacade = ViewProviderSketchCoinAttorney::getGeoListFacade(viewProvider);
rebuildConstraintNodes(geolistfacade);
}
void EditModeConstraintCoinManager::setConstraintSelectability(bool enabled /* = true */)
{
if (enabled) {
editModeScenegraphNodes.constrGrpSelect->style.setValue(SoPickStyle::SHAPE);
}
else {
editModeScenegraphNodes.constrGrpSelect->style.setValue(SoPickStyle::UNPICKABLE);
}
}
void EditModeConstraintCoinManager::rebuildConstraintNodes(const GeoListFacade & geolistfacade)
{
const std::vector<Sketcher::Constraint *> &constrlist = ViewProviderSketchCoinAttorney::getConstraints(viewProvider);
// clean up
Gui::coinRemoveAllChildren(editModeScenegraphNodes.constrGroup);
vConstrType.clear();
// Get sketch normal
Base::Vector3d RN(0,0,1);
// move to position of Sketch
Base::Placement Plz = ViewProviderSketchCoinAttorney::getEditingPlacement(viewProvider);
Base::Rotation tmp(Plz.getRotation());
tmp.multVec(RN,RN);
Plz.setRotation(tmp);
SbVec3f norm(RN.x, RN.y, RN.z);
rebuildConstraintNodes(geolistfacade, constrlist, norm);
}
void EditModeConstraintCoinManager::rebuildConstraintNodes(const GeoListFacade & geolistfacade, const std::vector<Sketcher::Constraint *> constrlist, SbVec3f norm)
{
for (std::vector<Sketcher::Constraint *>::const_iterator it=constrlist.begin(); it != constrlist.end(); ++it) {
// root separator for one constraint
SoSeparator *sep = new SoSeparator();
sep->ref();
// no caching for frequently-changing data structures
sep->renderCaching = SoSeparator::OFF;
// every constrained visual node gets its own material for preselection and selection
SoMaterial *mat = new SoMaterial;
mat->ref();
mat->diffuseColor = (*it)->isActive ?
((*it)->isDriving ?
drawingParameters.ConstrDimColor
:drawingParameters.NonDrivingConstrDimColor)
:drawingParameters.DeactivatedConstrDimColor;
// distinguish different constraint types to build up
switch ((*it)->Type) {
case Distance:
case DistanceX:
case DistanceY:
case Radius:
case Diameter:
case Weight:
case Angle:
{
SoDatumLabel *text = new SoDatumLabel();
text->norm.setValue(norm);
text->string = "";
text->textColor = (*it)->isActive ?
((*it)->isDriving ?
drawingParameters.ConstrDimColor
:drawingParameters.NonDrivingConstrDimColor)
:drawingParameters.DeactivatedConstrDimColor;
text->size.setValue(drawingParameters.labelFontSize);
text->lineWidth = 2 * drawingParameters.pixelScalingFactor;
text->useAntialiasing = false;
SoAnnotation *anno = new SoAnnotation();
anno->renderCaching = SoSeparator::OFF;
anno->addChild(text);
// #define CONSTRAINT_SEPARATOR_INDEX_MATERIAL_OR_DATUMLABEL 0
sep->addChild(text);
editModeScenegraphNodes.constrGroup->addChild(anno);
vConstrType.push_back((*it)->Type);
// nodes not needed
sep->unref();
mat->unref();
continue; // jump to next constraint
}
break;
case Horizontal:
case Vertical:
case Block:
{
// #define CONSTRAINT_SEPARATOR_INDEX_MATERIAL_OR_DATUMLABEL 0
sep->addChild(mat);
// #define CONSTRAINT_SEPARATOR_INDEX_FIRST_TRANSLATION 1
sep->addChild(new SoZoomTranslation());
// #define CONSTRAINT_SEPARATOR_INDEX_FIRST_ICON 2
sep->addChild(new SoImage());
// #define CONSTRAINT_SEPARATOR_INDEX_FIRST_CONSTRAINTID 3
sep->addChild(new SoInfo());
// #define CONSTRAINT_SEPARATOR_INDEX_SECOND_TRANSLATION 4
sep->addChild(new SoZoomTranslation());
// #define CONSTRAINT_SEPARATOR_INDEX_SECOND_ICON 5
sep->addChild(new SoImage());
// #define CONSTRAINT_SEPARATOR_INDEX_SECOND_CONSTRAINTID 6
sep->addChild(new SoInfo());
// remember the type of this constraint node
vConstrType.push_back((*it)->Type);
}
break;
case Coincident: // no visual for coincident so far
vConstrType.push_back(Coincident);
break;
case Parallel:
case Perpendicular:
case Equal:
{
// #define CONSTRAINT_SEPARATOR_INDEX_MATERIAL_OR_DATUMLABEL 0
sep->addChild(mat);
// #define CONSTRAINT_SEPARATOR_INDEX_FIRST_TRANSLATION 1
sep->addChild(new SoZoomTranslation());
// #define CONSTRAINT_SEPARATOR_INDEX_FIRST_ICON 2
sep->addChild(new SoImage());
// #define CONSTRAINT_SEPARATOR_INDEX_FIRST_CONSTRAINTID 3
sep->addChild(new SoInfo());
// #define CONSTRAINT_SEPARATOR_INDEX_SECOND_TRANSLATION 4
sep->addChild(new SoZoomTranslation());
// #define CONSTRAINT_SEPARATOR_INDEX_SECOND_ICON 5
sep->addChild(new SoImage());
// #define CONSTRAINT_SEPARATOR_INDEX_SECOND_CONSTRAINTID 6
sep->addChild(new SoInfo());
// remember the type of this constraint node
vConstrType.push_back((*it)->Type);
}
break;
case PointOnObject:
case Tangent:
case SnellsLaw:
{
// #define CONSTRAINT_SEPARATOR_INDEX_MATERIAL_OR_DATUMLABEL 0
sep->addChild(mat);
// #define CONSTRAINT_SEPARATOR_INDEX_FIRST_TRANSLATION 1
sep->addChild(new SoZoomTranslation());
// #define CONSTRAINT_SEPARATOR_INDEX_FIRST_ICON 2
sep->addChild(new SoImage());
// #define CONSTRAINT_SEPARATOR_INDEX_FIRST_CONSTRAINTID 3
sep->addChild(new SoInfo());
if ((*it)->Type == Tangent) {
const Part::Geometry *geo1 = geolistfacade.getGeometryFromGeoId((*it)->First);
const Part::Geometry *geo2 = geolistfacade.getGeometryFromGeoId((*it)->Second);
if (!geo1 || !geo2) {
Base::Console().Warning("Tangent constraint references non-existing geometry\n");
}
else if (geo1->getTypeId() == Part::GeomLineSegment::getClassTypeId() &&
geo2->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
// #define CONSTRAINT_SEPARATOR_INDEX_SECOND_TRANSLATION 4
sep->addChild(new SoZoomTranslation());
// #define CONSTRAINT_SEPARATOR_INDEX_SECOND_ICON 5
sep->addChild(new SoImage());
// #define CONSTRAINT_SEPARATOR_INDEX_SECOND_CONSTRAINTID 6
sep->addChild(new SoInfo());
}
}
vConstrType.push_back((*it)->Type);
}
break;
case Symmetric:
{
SoDatumLabel *arrows = new SoDatumLabel();
arrows->norm.setValue(norm);
arrows->string = "";
arrows->textColor = drawingParameters.ConstrDimColor;
arrows->lineWidth = 2 * drawingParameters.pixelScalingFactor;
// #define CONSTRAINT_SEPARATOR_INDEX_MATERIAL_OR_DATUMLABEL 0
sep->addChild(arrows);
// #define CONSTRAINT_SEPARATOR_INDEX_FIRST_TRANSLATION 1
sep->addChild(new SoTranslation());
// #define CONSTRAINT_SEPARATOR_INDEX_FIRST_ICON 2
sep->addChild(new SoImage());
// #define CONSTRAINT_SEPARATOR_INDEX_FIRST_CONSTRAINTID 3
sep->addChild(new SoInfo());
vConstrType.push_back((*it)->Type);
}
break;
case InternalAlignment:
{
vConstrType.push_back((*it)->Type);
}
break;
default:
vConstrType.push_back((*it)->Type);
}
editModeScenegraphNodes.constrGroup->addChild(sep);
// decrement ref counter again
sep->unref();
mat->unref();
}
}
QString EditModeConstraintCoinManager::getPresentationString(const Constraint *constraint)
{
QString nameStr; // name parameter string
QString valueStr; // dimensional value string
QString presentationStr; // final return string
QString unitStr; // the actual unit string
QString baseUnitStr; // the expected base unit string
double factor; // unit scaling factor, currently not used
Base::UnitSystem unitSys; // current unit system
if (!constraint->isActive)
return QString::fromLatin1(" ");
// Get the current name parameter string of the constraint
nameStr = QString::fromStdString(constraint->Name);
// Get the current value string including units
valueStr = constraint->getPresentationValue().getUserString(factor, unitStr);
// Hide units if user has requested it, is being displayed in the base
// units, and the schema being used has a clear base unit in the first
// place. Otherwise, display units.
if (constraintParameters.bHideUnits && constraint->Type != Sketcher::Angle)
{
// Only hide the default length unit. Right now there is not an easy way
// to get that from the Unit system so we have to manually add it here.
// Hopefully this can be added in the future so this code won't have to
// be updated if a new units schema is added.
unitSys = Base::UnitsApi::getSchema();
// If this is a supported unit system then define what the base unit is.
switch (unitSys)
{
case Base::UnitSystem::SI1:
case Base::UnitSystem::MmMin:
baseUnitStr = QString::fromLatin1("mm");
break;
case Base::UnitSystem::SI2:
baseUnitStr = QString::fromLatin1("m");
break;
case Base::UnitSystem::ImperialDecimal:
baseUnitStr = QString::fromLatin1("in");
break;
case Base::UnitSystem::Centimeters:
baseUnitStr = QString::fromLatin1("cm");
break;
default:
// Nothing to do
break;
}
if (!baseUnitStr.isEmpty())
{
// expected unit string matches actual unit string. remove.
if (QString::compare(baseUnitStr, unitStr)==0)
{
// Example code from: Mod/TechDraw/App/DrawViewDimension.cpp:372
QRegularExpression rxUnits(QString::fromUtf8(" \\D*$")); //space + any non digits at end of string
valueStr.remove(rxUnits); //getUserString(defaultDecimals) without units
}
}
}
if (constraint->Type == Sketcher::Diameter){
valueStr.prepend(QChar(216)); // Diameter sign
}
else if (constraint->Type == Sketcher::Radius){
valueStr.prepend(QChar(82)); // Capital letter R
}
/**
Create the representation string from the user defined format string
Format options are:
%N - the constraint name parameter
%V - the value of the dimensional constraint, including any unit characters
*/
if (constraintParameters.bShowDimensionalName && !nameStr.isEmpty())
{
if (constraintParameters.sDimensionalStringFormat.contains(QLatin1String("%V")) ||
constraintParameters.sDimensionalStringFormat.contains(QLatin1String("%N")))
{
presentationStr = constraintParameters.sDimensionalStringFormat;
presentationStr.replace(QLatin1String("%N"), nameStr);
presentationStr.replace(QLatin1String("%V"), valueStr);
}
else
{
// user defined format string does not contain any valid parameter, using default format "%N = %V"
presentationStr = nameStr + QLatin1String(" = ") + valueStr;
}
return presentationStr;
}
return valueStr;
}
std::set<int> EditModeConstraintCoinManager::detectPreselectionConstr(const SoPickedPoint *Point,
const SbVec2s &cursorPos)
{
std::set<int> constrIndices;
SoPath *path = Point->getPath();
// Get the constraints' tail
SoNode *tailFather2 = path->getNode(path->getLength()-3);
if (tailFather2 != editModeScenegraphNodes.constrGroup)
return constrIndices;
SoNode *tail = path->getTail();
SoNode *tailFather = path->getNode(path->getLength()-2);
for (int i=0; i < editModeScenegraphNodes.constrGroup->getNumChildren(); ++i) {
if (editModeScenegraphNodes.constrGroup->getChild(i) == tailFather) {
SoSeparator *sep = static_cast<SoSeparator *>(tailFather);
if (sep->getNumChildren() > static_cast<int>(ConstraintNodePosition::FirstConstraintIdIndex)) {
SoInfo *constrIds = nullptr;
if (tail == sep->getChild(static_cast<int>(ConstraintNodePosition::FirstIconIndex))) {
// First icon was hit
constrIds = static_cast<SoInfo *>(sep->getChild(static_cast<int>(ConstraintNodePosition::FirstConstraintIdIndex)));
}
else {
// Assume second icon was hit
if (static_cast<int>(ConstraintNodePosition::SecondConstraintIdIndex) < sep->getNumChildren()) {
constrIds = static_cast<SoInfo *>(sep->getChild(static_cast<int>(ConstraintNodePosition::SecondConstraintIdIndex)));
}
}
if (constrIds) {
QString constrIdsStr = QString::fromLatin1(constrIds->string.getValue().getString());
if (combinedConstrBoxes.count(constrIdsStr) && dynamic_cast<SoImage *>(tail)) {
// If it's a combined constraint icon
// Screen dimensions of the icon
SbVec3s iconSize = getDisplayedSize(static_cast<SoImage *>(tail));
// Center of the icon
//SbVec2f iconCoords = viewer->screenCoordsOfPath(path);
// The use of the Path to get the screen coordinates to get the icon center coordinates
// does not work.
//
// This implementation relies on the use of ZoomTranslation to get the absolute and relative
// positions of the icons.
//
// In the case of second icons (the same constraint has two icons at two different positions),
// the translation vectors have to be added, as the second ZoomTranslation operates on top of
// the first.
//
// Coordinates are projected on the sketch plane and then to the screen in the interval [0 1]
// Then this result is converted to pixels using the scale factor.
SbVec3f absPos;
SbVec3f trans;
auto translation = static_cast<SoZoomTranslation *>(static_cast<SoSeparator *>(tailFather)->getChild( static_cast<int>(ConstraintNodePosition::FirstTranslationIndex)));
absPos = translation->abPos.getValue();
trans = translation->translation.getValue();
if (tail != sep->getChild(static_cast<int>(ConstraintNodePosition::FirstIconIndex))) {
auto translation2 = static_cast<SoZoomTranslation *>(static_cast<SoSeparator *>(tailFather)->getChild( static_cast<int>(ConstraintNodePosition::SecondTranslationIndex)));
absPos += translation2->abPos.getValue();
trans += translation2->translation.getValue();
}
// TODO: Is this calculation actually sound? Why the absolute position is not scaled and the translation is? Review.
SbVec3f constrPos = absPos + trans*ViewProviderSketchCoinAttorney::getScaleFactor(viewProvider);
SbVec2f iconCoords = ViewProviderSketchCoinAttorney::getScreenCoordinates(viewProvider, SbVec2f(constrPos[0],constrPos[1]));
// cursorPos is SbVec2s in screen coordinates coming from SoEvent in mousemove
//
// Coordinates of the mouse cursor on the icon, origin at top-left for Qt
// but bottom-left for OIV.
// The coordinates are needed in Qt format, i.e. from top to bottom.
int iconX = cursorPos[0] - iconCoords[0] + iconSize[0]/2,
iconY = cursorPos[1] - iconCoords[1] + iconSize[1]/2;
iconY = iconSize[1] - iconY;
for (ConstrIconBBVec::iterator b = combinedConstrBoxes[constrIdsStr].begin();
b != combinedConstrBoxes[constrIdsStr].end(); ++b) {
#ifdef FC_DEBUG
// Useful code to debug coordinates and bounding boxes that does not need to be compiled in for
// any debug operations.
/*Base::Console().Log("Abs(%f,%f),Trans(%f,%f),Coords(%d,%d),iCoords(%f,%f),icon(%d,%d),isize(%d,%d),boundingbox([%d,%d],[%d,%d])\n", absPos[0],absPos[1],trans[0], trans[1], cursorPos[0], cursorPos[1], iconCoords[0], iconCoords[1], iconX, iconY, iconSize[0], iconSize[1], b->first.topLeft().x(),b->first.topLeft().y(),b->first.bottomRight().x(),b->first.bottomRight().y());*/
#endif
if (b->first.contains(iconX, iconY)) {
// We've found a bounding box that contains the mouse pointer!
for (std::set<int>::iterator k = b->second.begin(); k != b->second.end(); ++k)
constrIndices.insert(*k);
}
}
}
else {
// It's a constraint icon, not a combined one
QStringList constrIdStrings = constrIdsStr.split(QString::fromLatin1(","));
while (!constrIdStrings.empty())
constrIndices.insert(constrIdStrings.takeAt(0).toInt());
}
}
}
else {
// other constraint icons - eg radius...
constrIndices.clear();
constrIndices.insert(i);
}
break;
}
}
return constrIndices;
}
SbVec3s EditModeConstraintCoinManager::getDisplayedSize(const SoImage *iconPtr) const
{
#if (COIN_MAJOR_VERSION >= 3)
SbVec3s iconSize = iconPtr->image.getValue().getSize();
#else
SbVec2s size;
int nc;
const unsigned char * bytes = iconPtr->image.getValue(size, nc);
SbImage img (bytes, size, nc);
SbVec3s iconSize = img.getSize();
#endif
if (iconPtr->width.getValue() != -1)
iconSize[0] = iconPtr->width.getValue();
if (iconPtr->height.getValue() != -1)
iconSize[1] = iconPtr->height.getValue();
return iconSize;
}
// public function that triggers drawing of most constraint icons
void EditModeConstraintCoinManager::drawConstraintIcons()
{
auto geolistfacade = ViewProviderSketchCoinAttorney::getGeoListFacade(viewProvider);
drawConstraintIcons(geolistfacade);
}
void EditModeConstraintCoinManager::drawConstraintIcons(const GeoListFacade & geolistfacade)
{
const std::vector<Sketcher::Constraint *> &constraints = ViewProviderSketchCoinAttorney::getConstraints(viewProvider);
std::vector<constrIconQueueItem> iconQueue;
int maxNumberOfConstraints = std::min(editModeScenegraphNodes.constrGroup->getNumChildren(), static_cast<int>(constraints.size()));
for (int constrId = 0; constrId < maxNumberOfConstraints; ++constrId) {
Sketcher::Constraint* constraint = constraints[constrId];
// Check if Icon Should be created
bool multipleIcons = false;
QString icoType = iconTypeFromConstraint(constraint);
if (icoType.isEmpty())
continue;
if (constraint->Type != vConstrType[constrId])
break;
switch(constraint->Type) {
case Tangent:
{ // second icon is available only for colinear line segments
const Part::Geometry *geo1 = geolistfacade.getGeometryFromGeoId(constraint->First);
const Part::Geometry *geo2 = geolistfacade.getGeometryFromGeoId(constraint->Second);
if (geo1 && geo1->getTypeId() == Part::GeomLineSegment::getClassTypeId() &&
geo2 && geo2->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
multipleIcons = true;
}
}
break;
case Horizontal:
case Vertical:
{ // second icon is available only for point alignment
if (constraint->Second != GeoEnum::GeoUndef &&
constraint->FirstPos != Sketcher::PointPos::none &&
constraint->SecondPos != Sketcher::PointPos::none) {
multipleIcons = true;
}
}
break;
case Parallel:
multipleIcons = true;
break;
case Perpendicular:
// second icon is available only when there is no common point
if (constraint->FirstPos == Sketcher::PointPos::none && constraint->Third == GeoEnum::GeoUndef)
multipleIcons = true;
break;
case Equal:
multipleIcons = true;
break;
default:
break;
}
// Double-check that we can safely access the Inventor nodes
if (constrId >= editModeScenegraphNodes.constrGroup->getNumChildren()) {
Base::Console().Warning("Can't update constraint icons because view is not in sync with sketch\n");
break;
}
// Find the Constraint Icon SoImage Node
SoSeparator *sep = static_cast<SoSeparator *>(editModeScenegraphNodes.constrGroup->getChild(constrId));
int numChildren = sep->getNumChildren();
SbVec3f absPos;
// Somewhat hacky - we use SoZoomTranslations for most types of icon,
// but symmetry icons use SoTranslations...
SoTranslation *translationPtr = static_cast<SoTranslation *>(sep->getChild(static_cast<int>(ConstraintNodePosition::FirstTranslationIndex)));
if (dynamic_cast<SoZoomTranslation *>(translationPtr))
absPos = static_cast<SoZoomTranslation *>(translationPtr)->abPos.getValue();
else
absPos = translationPtr->translation.getValue();
SoImage *coinIconPtr = dynamic_cast<SoImage *>(sep->getChild(static_cast<int>(ConstraintNodePosition::FirstIconIndex)));
SoInfo *infoPtr = static_cast<SoInfo *>(sep->getChild(static_cast<int>(ConstraintNodePosition::FirstConstraintIdIndex)));
constrIconQueueItem thisIcon;
thisIcon.type = icoType;
thisIcon.constraintId = constrId;
thisIcon.position = absPos;
thisIcon.destination = coinIconPtr;
thisIcon.infoPtr = infoPtr;
thisIcon.visible = constraint->isInVirtualSpace == ViewProviderSketchCoinAttorney::isShownVirtualSpace(viewProvider);
if (constraint->Type==Symmetric) {
Base::Vector3d startingpoint = geolistfacade.getPoint(constraint->First, constraint->FirstPos);
Base::Vector3d endpoint = geolistfacade.getPoint(constraint->Second, constraint->SecondPos);
SbVec3f pos0(startingpoint.x,startingpoint.y,startingpoint.z);
SbVec3f pos1(endpoint.x,endpoint.y,endpoint.z);
thisIcon.iconRotation = ViewProviderSketchCoinAttorney::getRotation(viewProvider, pos0, pos1);
}
else {
thisIcon.iconRotation = 0;
}
if (multipleIcons) {
if (constraint->Name.empty())
thisIcon.label = QString::number(constrId + 1);
else
thisIcon.label = QString::fromUtf8(constraint->Name.c_str());
iconQueue.push_back(thisIcon);
// Note that the second translation is meant to be applied after the first.
// So, to get the position of the second icon, we add the two translations together
//
// See note ~30 lines up.
if (numChildren > static_cast<int>(ConstraintNodePosition::SecondConstraintIdIndex)) {
translationPtr = static_cast<SoTranslation *>(sep->getChild(static_cast<int>(ConstraintNodePosition::SecondTranslationIndex)));
if (dynamic_cast<SoZoomTranslation *>(translationPtr))
thisIcon.position += static_cast<SoZoomTranslation *>(translationPtr)->abPos.getValue();
else
thisIcon.position += translationPtr->translation.getValue();
thisIcon.destination = dynamic_cast<SoImage *>(sep->getChild(static_cast<int>(ConstraintNodePosition::SecondIconIndex)));
thisIcon.infoPtr = static_cast<SoInfo *>(sep->getChild(static_cast<int>(ConstraintNodePosition::SecondConstraintIdIndex)));
}
}
else {
if (constraint->Name.empty())
thisIcon.label = QString();
else
thisIcon.label = QString::fromUtf8(constraint->Name.c_str());
}
iconQueue.push_back(thisIcon);
}
combineConstraintIcons(iconQueue);
}
void EditModeConstraintCoinManager::combineConstraintIcons(IconQueue iconQueue)
{
// getScaleFactor gives us a ratio of pixels per some kind of real units
float maxDistSquared = pow(ViewProviderSketchCoinAttorney::getScaleFactor(viewProvider), 2);
// There's room for optimisation here; we could reuse the combined icons...
combinedConstrBoxes.clear();
while(!iconQueue.empty()) {
// A group starts with an item popped off the back of our initial queue
IconQueue thisGroup;
thisGroup.push_back(iconQueue.back());
constrIconQueueItem init = iconQueue.back();
iconQueue.pop_back();
// we group only icons not being Symmetry icons, because we want those on the line
// and only icons that are visible
if (init.type != QString::fromLatin1("Constraint_Symmetric") && init.visible){
IconQueue::iterator i = iconQueue.begin();
while(i != iconQueue.end()) {
if ((*i).visible) {
bool addedToGroup = false;
for(IconQueue::iterator j = thisGroup.begin();
j != thisGroup.end(); ++j) {
float distSquared = pow(i->position[0]-j->position[0],2) + pow(i->position[1]-j->position[1],2);
if (distSquared <= maxDistSquared && (*i).type != QString::fromLatin1("Constraint_Symmetric")) {
// Found an icon in iconQueue that's close enough to
// a member of thisGroup, so move it into thisGroup
thisGroup.push_back(*i);
i = iconQueue.erase(i);
addedToGroup = true;
break;
}
}
if (addedToGroup) {
if (i == iconQueue.end())
// We just got the last icon out of iconQueue
break;
else
// Start looking through the iconQueue again, in case
// we have an icon that's now close enough to thisGroup
i = iconQueue.begin();
} else
++i;
}
else // if !visible we skip it
i++;
}
}
if (thisGroup.size() == 1) {
drawTypicalConstraintIcon(thisGroup[0]);
}
else {
drawMergedConstraintIcons(thisGroup);
}
}
}
void EditModeConstraintCoinManager::drawMergedConstraintIcons(IconQueue iconQueue)
{
for(IconQueue::iterator i = iconQueue.begin(); i != iconQueue.end(); ++i) {
clearCoinImage(i->destination);
}
QImage compositeIcon;
SoImage *thisDest = iconQueue[0].destination;
SoInfo *thisInfo = iconQueue[0].infoPtr;
// Tracks all constraint IDs that are combined into this icon
QString idString;
int lastVPad = 0;
QStringList labels;
std::vector<int> ids;
QString thisType;
QColor iconColor;
QList<QColor> labelColors;
int maxColorPriority;
double iconRotation;
ConstrIconBBVec boundingBoxes;
while(!iconQueue.empty()) {
IconQueue::iterator i = iconQueue.begin();
labels.clear();
labels.append(i->label);
ids.clear();
ids.push_back(i->constraintId);
thisType = i->type;
iconColor = constrColor(i->constraintId);
labelColors.clear();
labelColors.append(iconColor);
iconRotation= i->iconRotation;
maxColorPriority = constrColorPriority(i->constraintId);
if (idString.length())
idString.append(QString::fromLatin1(","));
idString.append(QString::number(i->constraintId));
i = iconQueue.erase(i);
while(i != iconQueue.end()) {
if (i->type != thisType) {
++i;
continue;
}
labels.append(i->label);
ids.push_back(i->constraintId);
labelColors.append(constrColor(i->constraintId));
if (constrColorPriority(i->constraintId) > maxColorPriority) {
maxColorPriority = constrColorPriority(i->constraintId);
iconColor= constrColor(i->constraintId);
}
idString.append(QString::fromLatin1(",") +
QString::number(i->constraintId));
i = iconQueue.erase(i);
}
// To be inserted into edit->combinedConstBoxes
std::vector<QRect> boundingBoxesVec;
int oldHeight = 0;
// Render the icon here.
if (compositeIcon.isNull()) {
compositeIcon = renderConstrIcon(thisType,
iconColor,
labels,
labelColors,
iconRotation,
&boundingBoxesVec,
&lastVPad);
} else {
int thisVPad;
QImage partialIcon = renderConstrIcon(thisType,
iconColor,
labels,
labelColors,
iconRotation,
&boundingBoxesVec,
&thisVPad);
// Stack vertically for now. Down the road, it might make sense
// to figure out the best orientation automatically.
oldHeight = compositeIcon.height();
// This is overkill for the currently used (20 July 2014) font,
// since it always seems to have the same vertical pad, but this
// might not always be the case. The 3 pixel buffer might need
// to vary depending on font size too...
oldHeight -= std::max(lastVPad - 3, 0);
compositeIcon = compositeIcon.copy(0, 0,
std::max(partialIcon.width(),
compositeIcon.width()),
partialIcon.height() +
compositeIcon.height());
QPainter qp(&compositeIcon);
qp.drawImage(0, oldHeight, partialIcon);
lastVPad = thisVPad;
}
// Add bounding boxes for the icon we just rendered to boundingBoxes
std::vector<int>::iterator id = ids.begin();
std::set<int> nextIds;
for(std::vector<QRect>::iterator bb = boundingBoxesVec.begin();
bb != boundingBoxesVec.end(); ++bb) {
nextIds.clear();
if (bb == boundingBoxesVec.begin()) {
// The first bounding box is for the icon at left, so assign
// all IDs for that type of constraint to the icon.
for(std::vector<int>::iterator j = ids.begin(); j != ids.end(); ++j)
nextIds.insert(*j);
}
else {
nextIds.insert(*(id++));
}
ConstrIconBB newBB(bb->adjusted(0, oldHeight, 0, oldHeight),
nextIds);
boundingBoxes.push_back(newBB);
}
}
combinedConstrBoxes[idString] = boundingBoxes;
thisInfo->string.setValue(idString.toLatin1().data());
sendConstraintIconToCoin(compositeIcon, thisDest);
}
/// Note: labels, labelColors, and boundingBoxes are all
/// assumed to be the same length.
QImage EditModeConstraintCoinManager::renderConstrIcon(const QString &type,
const QColor &iconColor,
const QStringList &labels,
const QList<QColor> &labelColors,
double iconRotation,
std::vector<QRect> *boundingBoxes,
int *vPad)
{
// Constants to help create constraint icons
QString joinStr = QString::fromLatin1(", ");
QPixmap pxMap;
std::stringstream constraintName;
constraintName << type.toLatin1().data() << drawingParameters.constraintIconSize; // allow resizing by embedding size
if (! Gui::BitmapFactory().findPixmapInCache(constraintName.str().c_str(), pxMap)) {
pxMap = Gui::BitmapFactory().pixmapFromSvg(type.toLatin1().data(),QSizeF(drawingParameters.constraintIconSize, drawingParameters.constraintIconSize));
Gui::BitmapFactory().addPixmapToCache(constraintName.str().c_str(), pxMap); // Cache for speed, avoiding pixmapFromSvg
}
QImage icon = pxMap.toImage();
QFont font = ViewProviderSketchCoinAttorney::getApplicationFont(viewProvider);
font.setPixelSize(static_cast<int>(1.0 * drawingParameters.constraintIconSize));
font.setBold(true);
QFontMetrics qfm = QFontMetrics(font);
int labelWidth = qfm.boundingRect(labels.join(joinStr)).width();
// See Qt docs on qRect::bottom() for explanation of the +1
int pxBelowBase = qfm.boundingRect(labels.join(joinStr)).bottom() + 1;
if (vPad)
*vPad = pxBelowBase;
QTransform rotation;
rotation.rotate(iconRotation);
QImage roticon = icon.transformed(rotation);
QImage image = roticon.copy(0, 0, roticon.width() + labelWidth,
roticon.height() + pxBelowBase);
// Make a bounding box for the icon
if (boundingBoxes)
boundingBoxes->push_back(QRect(0, 0, roticon.width(), roticon.height()));
// Render the Icons
QPainter qp(&image);
qp.setCompositionMode(QPainter::CompositionMode_SourceIn);
qp.fillRect(roticon.rect(), iconColor);
// Render constraint label if necessary
if (!labels.join(QString()).isEmpty()) {
qp.setCompositionMode(QPainter::CompositionMode_SourceOver);
qp.setFont(font);
int cursorOffset = 0;
//In Python: "for label, color in zip(labels, labelColors):"
QStringList::const_iterator labelItr;
QString labelStr;
QList<QColor>::const_iterator colorItr;
QRect labelBB;
for(labelItr = labels.begin(), colorItr = labelColors.begin();
labelItr != labels.end() && colorItr != labelColors.end();
++labelItr, ++colorItr) {
qp.setPen(*colorItr);
if (labelItr + 1 == labels.end()) // if this is the last label
labelStr = *labelItr;
else
labelStr = *labelItr + joinStr;
// Note: text can sometimes draw to the left of the starting
// position, eg italic fonts. Check QFontMetrics
// documentation for more info, but be mindful if the
// icon.width() is ever very small (or removed).
qp.drawText(icon.width() + cursorOffset, icon.height(), labelStr);
if (boundingBoxes) {
labelBB = qfm.boundingRect(labelStr);
labelBB.moveTo(icon.width() + cursorOffset,
icon.height() - qfm.height() + pxBelowBase);
boundingBoxes->push_back(labelBB);
}
cursorOffset += Gui::QtTools::horizontalAdvance(qfm, labelStr);
}
}
return image;
}
void EditModeConstraintCoinManager::drawTypicalConstraintIcon(const constrIconQueueItem &i)
{
QColor color = constrColor(i.constraintId);
QImage image = renderConstrIcon(i.type,
color,
QStringList(i.label),
QList<QColor>() << color,
i.iconRotation);
i.infoPtr->string.setValue(QString::number(i.constraintId).toLatin1().data());
sendConstraintIconToCoin(image, i.destination);
}
QString EditModeConstraintCoinManager::iconTypeFromConstraint(Constraint *constraint)
{
/*! TODO: Consider pushing this functionality up into Constraint
*
Abdullah: Please, don't. An icon is visualisation information and
does not belong in App, but in Gui. Rather consider refactoring it
in a separate class dealing with visualisation of constraints.*/
switch(constraint->Type) {
case Horizontal:
return QString::fromLatin1("Constraint_Horizontal");
case Vertical:
return QString::fromLatin1("Constraint_Vertical");
case PointOnObject:
return QString::fromLatin1("Constraint_PointOnObject");
case Tangent:
return QString::fromLatin1("Constraint_Tangent");
case Parallel:
return QString::fromLatin1("Constraint_Parallel");
case Perpendicular:
return QString::fromLatin1("Constraint_Perpendicular");
case Equal:
return QString::fromLatin1("Constraint_EqualLength");
case Symmetric:
return QString::fromLatin1("Constraint_Symmetric");
case SnellsLaw:
return QString::fromLatin1("Constraint_SnellsLaw");
case Block:
return QString::fromLatin1("Constraint_Block");
default:
return QString();
}
}
void EditModeConstraintCoinManager::sendConstraintIconToCoin(const QImage &icon, SoImage *soImagePtr)
{
SoSFImage icondata = SoSFImage();
Gui::BitmapFactory().convert(icon, icondata);
SbVec2s iconSize(icon.width(), icon.height());
int four = 4;
soImagePtr->image.setValue(iconSize, 4, icondata.getValue(iconSize, four));
//Set Image Alignment to Center
soImagePtr->vertAlignment = SoImage::HALF;
soImagePtr->horAlignment = SoImage::CENTER;
}
void EditModeConstraintCoinManager::clearCoinImage(SoImage *soImagePtr)
{
soImagePtr->setToDefaults();
}
QColor EditModeConstraintCoinManager::constrColor(int constraintId)
{
auto toQColor = [](auto sbcolor) -> QColor {
return QColor( (int)(sbcolor[0] * 255.0f),
(int)(sbcolor[1] * 255.0f),
(int)(sbcolor[2] * 255.0f));
};
const auto constraints = ViewProviderSketchCoinAttorney::getConstraints(viewProvider);
if (ViewProviderSketchCoinAttorney::isConstraintPreselected(viewProvider,constraintId))
return toQColor(drawingParameters.PreselectColor);
else if (ViewProviderSketchCoinAttorney::isConstraintSelected(viewProvider, constraintId))
return toQColor(drawingParameters.SelectColor);
else if (!constraints[constraintId]->isActive)
return toQColor(drawingParameters.DeactivatedConstrDimColor);
else if (!constraints[constraintId]->isDriving)
return toQColor(drawingParameters.NonDrivingConstrDimColor);
else
return toQColor(drawingParameters.ConstrIcoColor);
}
int EditModeConstraintCoinManager::constrColorPriority(int constraintId)
{
if (ViewProviderSketchCoinAttorney::isConstraintPreselected(viewProvider,constraintId))
return 3;
else if (ViewProviderSketchCoinAttorney::isConstraintSelected(viewProvider, constraintId))
return 2;
else
return 1;
}
SoSeparator * EditModeConstraintCoinManager::getConstraintIdSeparator(int i)
{
return dynamic_cast<SoSeparator *>(editModeScenegraphNodes.constrGroup->getChild(i));
}
void EditModeConstraintCoinManager::createEditModeInventorNodes()
{
// group node for the Constraint visual +++++++++++++++++++++++++++++++++++
SoMaterialBinding *MtlBind = new SoMaterialBinding;
MtlBind->setName("ConstraintMaterialBinding");
MtlBind->value = SoMaterialBinding::OVERALL ;
editModeScenegraphNodes.EditRoot->addChild(MtlBind);
// use small line width for the Constraints
editModeScenegraphNodes.ConstraintDrawStyle = new SoDrawStyle;
editModeScenegraphNodes.ConstraintDrawStyle->setName("ConstraintDrawStyle");
editModeScenegraphNodes.ConstraintDrawStyle->lineWidth = 1 * drawingParameters.pixelScalingFactor;
editModeScenegraphNodes.EditRoot->addChild(editModeScenegraphNodes.ConstraintDrawStyle);
// add the group where all the constraints has its SoSeparator
editModeScenegraphNodes.constrGrpSelect = new SoPickStyle(); // used to toggle constraints selectability
editModeScenegraphNodes.constrGrpSelect->style.setValue(SoPickStyle::SHAPE);
editModeScenegraphNodes.EditRoot->addChild(editModeScenegraphNodes.constrGrpSelect);
setConstraintSelectability(); // Ensure default value;
editModeScenegraphNodes.constrGroup = new SmSwitchboard();
editModeScenegraphNodes.constrGroup->setName("ConstraintGroup");
editModeScenegraphNodes.EditRoot->addChild(editModeScenegraphNodes.constrGroup);
SoPickStyle *ps = new SoPickStyle(); // used to following nodes aren't impacted
ps->style.setValue(SoPickStyle::SHAPE);
editModeScenegraphNodes.EditRoot->addChild(ps);
}