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

2410 lines
125 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 <Gui/SoDatumLabel.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 "SoZoomTranslation.h"
#include "ViewProviderSketch.h"
#include "ViewProviderSketchCoinAttorney.h"
#include "Utils.h"
using namespace Gui;
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) {
pnt1 = geolistfacade.getPoint(Constr->First, Constr->FirstPos);
const Part::Geometry *geo = geolistfacade.getGeometryFromGeoId(Constr->Second);
if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { // point to line distance
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 if (geo->getTypeId() == Part::GeomCircle::getClassTypeId()) { // circle to circle distance
const Part::Geometry *geo1 = geolistfacade.getGeometryFromGeoId(Constr->First);
if (geo1->getTypeId() == Part::GeomCircle::getClassTypeId()) {
const Part::GeomCircle *circleSeg1 = static_cast<const Part::GeomCircle*>(geo1);
auto circleSeg2 = static_cast<const Part::GeomCircle*>(geo);
GetCirclesMinimalDistance(circleSeg1, circleSeg2, pnt1, pnt2);
}
} 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()) { // segment distance
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.getCoinLayerCount(); 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.getCoinLayerCount(); 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);
}