======================================================= When adding a reference constraint of type radius or diameter to an external geometry using the method of select the continuous mode method (select tool first, click geometry afterwards), with ActiveUpdate checked, there was a missing solving to bring the solver information in line with the Driven constraint. fixes #4054
7652 lines
316 KiB
C++
7652 lines
316 KiB
C++
/***************************************************************************
|
|
* Copyright (c) 2010 Jürgen Riegel <juergen.riegel@web.de> *
|
|
* *
|
|
* 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 <cfloat>
|
|
# include <QMessageBox>
|
|
# include <Precision.hxx>
|
|
# include <QPainter>
|
|
#endif
|
|
|
|
#include <Base/Tools.h>
|
|
#include <Base/Tools2D.h>
|
|
#include <App/Application.h>
|
|
#include <Gui/Application.h>
|
|
#include <Gui/Document.h>
|
|
#include <Gui/Selection.h>
|
|
#include <Gui/SelectionFilter.h>
|
|
#include <Gui/CommandT.h>
|
|
#include <Gui/MainWindow.h>
|
|
#include <Gui/DlgEditFileIncludePropertyExternal.h>
|
|
#include <Gui/Action.h>
|
|
#include <Gui/BitmapFactory.h>
|
|
|
|
#include <Mod/Part/App/Geometry.h>
|
|
#include <Mod/Sketcher/App/SketchObject.h>
|
|
#include <Mod/Sketcher/App/Sketch.h>
|
|
|
|
#include "ViewProviderSketch.h"
|
|
#include "DrawSketchHandler.h"
|
|
#include "ui_InsertDatum.h"
|
|
#include "EditDatumDialog.h"
|
|
#include "CommandConstraints.h"
|
|
|
|
using namespace std;
|
|
using namespace SketcherGui;
|
|
using namespace Sketcher;
|
|
|
|
/***** Creation Mode ************/
|
|
namespace SketcherGui
|
|
{
|
|
enum ConstraintCreationMode {
|
|
Driving,
|
|
Reference
|
|
};
|
|
}
|
|
|
|
ConstraintCreationMode constraintCreationMode = Driving;
|
|
|
|
void ActivateHandler(Gui::Document *doc, DrawSketchHandler *handler);
|
|
|
|
bool isCreateGeoActive(Gui::Document *doc);
|
|
|
|
bool isCreateConstraintActive(Gui::Document *doc)
|
|
{
|
|
if (doc) {
|
|
// checks if a Sketch View provider is in Edit and is in no special mode
|
|
if (doc->getInEdit() && doc->getInEdit()->isDerivedFrom(SketcherGui::ViewProviderSketch::getClassTypeId())) {
|
|
if (static_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit())
|
|
->getSketchMode() == ViewProviderSketch::STATUS_NONE) {
|
|
if (Gui::Selection().countObjectsOfType(Sketcher::SketchObject::getClassTypeId()) > 0)
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Utility method to avoid repeating the same code over and over again
|
|
void finishDistanceConstraint(Gui::Command* cmd, Sketcher::SketchObject* sketch, bool isDriven=true)
|
|
{
|
|
// Get the latest constraint
|
|
const std::vector<Sketcher::Constraint *> &ConStr = sketch->Constraints.getValues();
|
|
Sketcher::Constraint *constr = ConStr[ConStr.size() -1];
|
|
|
|
// Guess some reasonable distance for placing the datum text
|
|
Gui::Document *doc = cmd->getActiveGuiDocument();
|
|
float sf = 1.f;
|
|
if (doc && doc->getInEdit() && doc->getInEdit()->isDerivedFrom(SketcherGui::ViewProviderSketch::getClassTypeId())) {
|
|
SketcherGui::ViewProviderSketch *vp = static_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit());
|
|
sf = vp->getScaleFactor();
|
|
|
|
constr->LabelDistance = 2. * sf;
|
|
vp->draw(false,false); // Redraw
|
|
}
|
|
|
|
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
|
bool show = hGrp->GetBool("ShowDialogOnDistanceConstraint", true);
|
|
|
|
// Ask for the value of the distance immediately
|
|
if (show && isDriven) {
|
|
EditDatumDialog editDatumDialog(sketch, ConStr.size() - 1);
|
|
editDatumDialog.exec();
|
|
}
|
|
else {
|
|
// no dialog was shown so commit the command
|
|
cmd->commitCommand();
|
|
}
|
|
|
|
tryAutoRecompute(sketch);
|
|
cmd->getSelection().clearSelection();
|
|
}
|
|
|
|
void showNoConstraintBetweenExternal()
|
|
{
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Cannot add a constraint between two external geometries!"));
|
|
}
|
|
|
|
void showNoConstraintBetweenFixedGeometry()
|
|
{
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Cannot add a constraint between two fixed geometries! Fixed geometries involve external geometry, blocked geometry or special points as B-spline knot points."));
|
|
}
|
|
|
|
bool SketcherGui::checkBothExternal(int GeoId1, int GeoId2)
|
|
{
|
|
if (GeoId1 == Constraint::GeoUndef || GeoId2 == Constraint::GeoUndef)
|
|
return false;
|
|
else
|
|
return (GeoId1 < 0 && GeoId2 < 0);
|
|
}
|
|
|
|
bool SketcherGui::checkBothExternalOrConstructionPoints(const Sketcher::SketchObject* Obj,int GeoId1, int GeoId2)
|
|
{
|
|
if (GeoId1 == Constraint::GeoUndef || GeoId2 == Constraint::GeoUndef)
|
|
return false;
|
|
else
|
|
return (GeoId1 < 0 && GeoId2 < 0) || (isConstructionPoint(Obj,GeoId1) && isConstructionPoint(Obj,GeoId2)) ||
|
|
(GeoId1 < 0 && isConstructionPoint(Obj,GeoId2)) || (GeoId2 < 0 && isConstructionPoint(Obj,GeoId1));
|
|
}
|
|
|
|
bool SketcherGui::isPointOrSegmentFixed(const Sketcher::SketchObject* Obj, int GeoId)
|
|
{
|
|
const std::vector< Sketcher::Constraint * > &vals = Obj->Constraints.getValues();
|
|
|
|
if (GeoId == Constraint::GeoUndef)
|
|
return false;
|
|
else
|
|
return checkConstraint(vals, Sketcher::Block, GeoId, Sketcher::none) || GeoId <= Sketcher::GeoEnum::RefExt || isConstructionPoint(Obj,GeoId);
|
|
}
|
|
|
|
bool SketcherGui::areBothPointsOrSegmentsFixed(const Sketcher::SketchObject* Obj, int GeoId1, int GeoId2)
|
|
{
|
|
const std::vector< Sketcher::Constraint * > &vals = Obj->Constraints.getValues();
|
|
|
|
if (GeoId1 == Constraint::GeoUndef || GeoId2 == Constraint::GeoUndef)
|
|
return false;
|
|
else
|
|
return ((checkConstraint(vals, Sketcher::Block, GeoId1, Sketcher::none) || GeoId1 <= Sketcher::GeoEnum::RefExt || isConstructionPoint(Obj,GeoId1)) &&
|
|
(checkConstraint(vals, Sketcher::Block, GeoId2, Sketcher::none) || GeoId2 <= Sketcher::GeoEnum::RefExt || isConstructionPoint(Obj,GeoId2)));
|
|
}
|
|
|
|
bool SketcherGui::areAllPointsOrSegmentsFixed(const Sketcher::SketchObject* Obj, int GeoId1, int GeoId2, int GeoId3)
|
|
{
|
|
const std::vector< Sketcher::Constraint * > &vals = Obj->Constraints.getValues();
|
|
|
|
if (GeoId1 == Constraint::GeoUndef || GeoId2 == Constraint::GeoUndef || GeoId3 == Constraint::GeoUndef)
|
|
return false;
|
|
else
|
|
return ((checkConstraint(vals, Sketcher::Block, GeoId1, Sketcher::none) || GeoId1 <= Sketcher::GeoEnum::RefExt || isConstructionPoint(Obj,GeoId1)) &&
|
|
(checkConstraint(vals, Sketcher::Block, GeoId2, Sketcher::none) || GeoId2 <= Sketcher::GeoEnum::RefExt || isConstructionPoint(Obj,GeoId2)) &&
|
|
(checkConstraint(vals, Sketcher::Block, GeoId3, Sketcher::none) || GeoId3 <= Sketcher::GeoEnum::RefExt || isConstructionPoint(Obj,GeoId3)));
|
|
}
|
|
|
|
void SketcherGui::getIdsFromName(const std::string &name, const Sketcher::SketchObject* Obj,
|
|
int &GeoId, PointPos &PosId)
|
|
{
|
|
GeoId = Constraint::GeoUndef;
|
|
PosId = Sketcher::none;
|
|
|
|
if (name.size() > 4 && name.substr(0,4) == "Edge") {
|
|
GeoId = std::atoi(name.substr(4,4000).c_str()) - 1;
|
|
}
|
|
else if (name.size() == 9 && name.substr(0,9) == "RootPoint") {
|
|
GeoId = Sketcher::GeoEnum::RtPnt;
|
|
PosId = Sketcher::start;
|
|
}
|
|
else if (name.size() == 6 && name.substr(0,6) == "H_Axis")
|
|
GeoId = Sketcher::GeoEnum::HAxis;
|
|
else if (name.size() == 6 && name.substr(0,6) == "V_Axis")
|
|
GeoId = Sketcher::GeoEnum::VAxis;
|
|
else if (name.size() > 12 && name.substr(0,12) == "ExternalEdge")
|
|
GeoId = Sketcher::GeoEnum::RefExt + 1 - std::atoi(name.substr(12,4000).c_str());
|
|
else if (name.size() > 6 && name.substr(0,6) == "Vertex") {
|
|
int VtId = std::atoi(name.substr(6,4000).c_str()) - 1;
|
|
Obj->getGeoVertexIndex(VtId,GeoId,PosId);
|
|
}
|
|
}
|
|
|
|
bool inline SketcherGui::isVertex(int GeoId, PointPos PosId)
|
|
{
|
|
return (GeoId != Constraint::GeoUndef && PosId != Sketcher::none);
|
|
}
|
|
|
|
bool inline SketcherGui::isEdge(int GeoId, PointPos PosId)
|
|
{
|
|
return (GeoId != Constraint::GeoUndef && PosId == Sketcher::none);
|
|
}
|
|
|
|
bool SketcherGui::isSimpleVertex(const Sketcher::SketchObject* Obj, int GeoId, PointPos PosId)
|
|
{
|
|
if (PosId == Sketcher::start && (GeoId == Sketcher::GeoEnum::HAxis || GeoId == Sketcher::GeoEnum::VAxis))
|
|
return true;
|
|
const Part::Geometry *geo = Obj->getGeometry(GeoId);
|
|
if (geo->getTypeId() == Part::GeomPoint::getClassTypeId())
|
|
return true;
|
|
else if (PosId == Sketcher::mid)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
bool SketcherGui::isConstructionPoint(const Sketcher::SketchObject* Obj, int GeoId)
|
|
{
|
|
const Part::Geometry * geo = Obj->getGeometry(GeoId);
|
|
return (geo && geo->getTypeId() == Part::GeomPoint::getClassTypeId() && geo->Construction == true);
|
|
}
|
|
|
|
bool SketcherGui::IsPointAlreadyOnCurve(int GeoIdCurve, int GeoIdPoint, Sketcher::PointPos PosIdPoint, Sketcher::SketchObject* Obj)
|
|
{
|
|
//This func is a "smartness" behind three-element tangent-, perp.- and angle-via-point.
|
|
//We want to find out, if the point supplied by user is already on
|
|
// both of the curves. If not, necessary point-on-object constraints
|
|
// are to be added automatically.
|
|
//Simple geometric test seems to be the best, because a point can be
|
|
// constrained to a curve in a number of ways (e.g. it is an endpoint of an
|
|
// arc, or is coincident to endpoint of an arc, or it is an endpoint of an
|
|
// ellipse's majopr diameter line). Testing all those possibilities is way
|
|
// too much trouble, IMO(DeepSOIC).
|
|
Base::Vector3d p = Obj->getPoint(GeoIdPoint, PosIdPoint);
|
|
return Obj->isPointOnCurve(GeoIdCurve, p.x, p.y);
|
|
}
|
|
|
|
/// Makes a simple tangency constraint using extra point + tangent via point
|
|
/// ellipse => an ellipse
|
|
/// geom2 => any of an ellipse, an arc of ellipse, a circle, or an arc (of circle)
|
|
/// geoId1 => geoid of the ellipse
|
|
/// geoId2 => geoid of geom2
|
|
/// NOTE: A command must be opened before calling this function, which this function
|
|
/// commits or aborts as appropriate. The reason is for compatibility reasons with
|
|
/// other code e.g. "Autoconstraints" in DrawSketchHandler.cpp
|
|
void SketcherGui::makeTangentToEllipseviaNewPoint(Sketcher::SketchObject* Obj,
|
|
const Part::GeomEllipse *ellipse,
|
|
const Part::Geometry *geom2,
|
|
int geoId1,
|
|
int geoId2
|
|
)
|
|
{
|
|
|
|
Base::Vector3d center=ellipse->getCenter();
|
|
double majord=ellipse->getMajorRadius();
|
|
double minord=ellipse->getMinorRadius();
|
|
double phi=atan2(ellipse->getMajorAxisDir().y, ellipse->getMajorAxisDir().x);
|
|
|
|
Base::Vector3d center2;
|
|
|
|
if( geom2->getTypeId() == Part::GeomEllipse::getClassTypeId() )
|
|
center2= (static_cast<const Part::GeomEllipse *>(geom2))->getCenter();
|
|
else if( geom2->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId())
|
|
center2= (static_cast<const Part::GeomArcOfEllipse *>(geom2))->getCenter();
|
|
else if( geom2->getTypeId() == Part::GeomCircle::getClassTypeId())
|
|
center2= (static_cast<const Part::GeomCircle *>(geom2))->getCenter();
|
|
else if( geom2->getTypeId() == Part::GeomArcOfCircle::getClassTypeId())
|
|
center2= (static_cast<const Part::GeomArcOfCircle *>(geom2))->getCenter();
|
|
|
|
Base::Vector3d direction=center2-center;
|
|
double tapprox=atan2(direction.y,direction.x)-phi; // we approximate the eccentric anomaly by the polar
|
|
|
|
Base::Vector3d PoE = Base::Vector3d(center.x+majord*cos(tapprox)*cos(phi)-minord*sin(tapprox)*sin(phi),
|
|
center.y+majord*cos(tapprox)*sin(phi)+minord*sin(tapprox)*cos(phi), 0);
|
|
|
|
try {
|
|
// Add a point
|
|
Gui::cmdAppObjectArgs(Obj, "addGeometry(Part.Point(App.Vector(%f,%f,0)))",
|
|
PoE.x,PoE.y);
|
|
int GeoIdPoint = Obj->getHighestCurveIndex();
|
|
|
|
// Point on first object
|
|
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
|
GeoIdPoint,Sketcher::start,geoId1); // constrain major axis
|
|
// Point on second object
|
|
Gui::cmdAppObjectArgs(Obj,"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
|
GeoIdPoint,Sketcher::start,geoId2); // constrain major axis
|
|
// tangent via point
|
|
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('TangentViaPoint',%d,%d,%d,%d))",
|
|
geoId1, geoId2 ,GeoIdPoint, Sketcher::start);
|
|
}
|
|
catch (const Base::Exception& e) {
|
|
Base::Console().Error("%s\n", e.what());
|
|
Gui::Command::abortCommand();
|
|
|
|
tryAutoRecompute(Obj);
|
|
return;
|
|
}
|
|
|
|
Gui::Command::commitCommand();
|
|
tryAutoRecompute(Obj);
|
|
}
|
|
|
|
/// Makes a simple tangency constraint using extra point + tangent via point
|
|
/// aoe => an arc of ellipse
|
|
/// geom2 => any of an arc of ellipse, a circle, or an arc (of circle)
|
|
/// geoId1 => geoid of the arc of ellipse
|
|
/// geoId2 => geoid of geom2
|
|
/// NOTE: A command must be opened before calling this function, which this function
|
|
/// commits or aborts as appropriate. The reason is for compatibility reasons with
|
|
/// other code e.g. "Autoconstraints" in DrawSketchHandler.cpp
|
|
void SketcherGui::makeTangentToArcOfEllipseviaNewPoint(Sketcher::SketchObject* Obj,
|
|
const Part::GeomArcOfEllipse *aoe,
|
|
const Part::Geometry *geom2,
|
|
int geoId1,
|
|
int geoId2
|
|
)
|
|
{
|
|
|
|
Base::Vector3d center=aoe->getCenter();
|
|
double majord=aoe->getMajorRadius();
|
|
double minord=aoe->getMinorRadius();
|
|
double phi=atan2(aoe->getMajorAxisDir().y, aoe->getMajorAxisDir().x);
|
|
|
|
Base::Vector3d center2;
|
|
|
|
if( geom2->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId())
|
|
center2= (static_cast<const Part::GeomArcOfEllipse *>(geom2))->getCenter();
|
|
else if( geom2->getTypeId() == Part::GeomCircle::getClassTypeId())
|
|
center2= (static_cast<const Part::GeomCircle *>(geom2))->getCenter();
|
|
else if( geom2->getTypeId() == Part::GeomArcOfCircle::getClassTypeId())
|
|
center2= (static_cast<const Part::GeomArcOfCircle *>(geom2))->getCenter();
|
|
|
|
Base::Vector3d direction=center2-center;
|
|
double tapprox=atan2(direction.y,direction.x)-phi; // we approximate the eccentric anomaly by the polar
|
|
|
|
Base::Vector3d PoE = Base::Vector3d(center.x+majord*cos(tapprox)*cos(phi)-minord*sin(tapprox)*sin(phi),
|
|
center.y+majord*cos(tapprox)*sin(phi)+minord*sin(tapprox)*cos(phi), 0);
|
|
|
|
try {
|
|
// Add a point
|
|
Gui::cmdAppObjectArgs(Obj, "addGeometry(Part.Point(App.Vector(%f,%f,0)))",
|
|
PoE.x,PoE.y);
|
|
int GeoIdPoint = Obj->getHighestCurveIndex();
|
|
|
|
// Point on first object
|
|
Gui::cmdAppObjectArgs(Obj,"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
|
GeoIdPoint,Sketcher::start,geoId1); // constrain major axis
|
|
// Point on second object
|
|
Gui::cmdAppObjectArgs(Obj,"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
|
GeoIdPoint,Sketcher::start,geoId2); // constrain major axis
|
|
// tangent via point
|
|
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('TangentViaPoint',%d,%d,%d,%d))",
|
|
geoId1, geoId2 ,GeoIdPoint, Sketcher::start);
|
|
}
|
|
catch (const Base::Exception& e) {
|
|
Base::Console().Error("%s\n", e.what());
|
|
Gui::Command::abortCommand();
|
|
|
|
tryAutoRecompute(Obj);
|
|
return;
|
|
}
|
|
|
|
Gui::Command::commitCommand();
|
|
tryAutoRecompute(Obj);
|
|
}
|
|
|
|
/// Makes a simple tangency constraint using extra point + tangent via point
|
|
/// aoh => an arc of hyperbola
|
|
/// geom2 => any of an arc of hyperbola, an arc of ellipse, a circle, or an arc (of circle)
|
|
/// geoId1 => geoid of the arc of hyperbola
|
|
/// geoId2 => geoid of geom2
|
|
/// NOTE: A command must be opened before calling this function, which this function
|
|
/// commits or aborts as appropriate. The reason is for compatibility reasons with
|
|
/// other code e.g. "Autoconstraints" in DrawSketchHandler.cpp
|
|
void SketcherGui::makeTangentToArcOfHyperbolaviaNewPoint(Sketcher::SketchObject* Obj,
|
|
const Part::GeomArcOfHyperbola *aoh,
|
|
const Part::Geometry *geom2,
|
|
int geoId1,
|
|
int geoId2
|
|
)
|
|
{
|
|
|
|
Base::Vector3d center=aoh->getCenter();
|
|
double majord=aoh->getMajorRadius();
|
|
double minord=aoh->getMinorRadius();
|
|
Base::Vector3d dirmaj = aoh->getMajorAxisDir();
|
|
double phi=atan2(dirmaj.y, dirmaj.x);
|
|
double df = sqrt(majord*majord+minord*minord);
|
|
Base::Vector3d focus = center+df*dirmaj; // positive focus
|
|
|
|
Base::Vector3d center2;
|
|
|
|
if( geom2->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId()){
|
|
const Part::GeomArcOfHyperbola *aoh2 = static_cast<const Part::GeomArcOfHyperbola *>(geom2);
|
|
Base::Vector3d dirmaj2 = aoh2->getMajorAxisDir();
|
|
double majord2 = aoh2->getMajorRadius();
|
|
double minord2 = aoh2->getMinorRadius();
|
|
double df2 = sqrt(majord2*majord2+minord2*minord2);
|
|
center2 = aoh2->getCenter()+df2*dirmaj2; // positive focus
|
|
}
|
|
else if( geom2->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId())
|
|
center2= (static_cast<const Part::GeomArcOfEllipse *>(geom2))->getCenter();
|
|
else if( geom2->getTypeId() == Part::GeomEllipse::getClassTypeId())
|
|
center2= (static_cast<const Part::GeomEllipse *>(geom2))->getCenter();
|
|
else if( geom2->getTypeId() == Part::GeomCircle::getClassTypeId())
|
|
center2= (static_cast<const Part::GeomCircle *>(geom2))->getCenter();
|
|
else if( geom2->getTypeId() == Part::GeomArcOfCircle::getClassTypeId())
|
|
center2= (static_cast<const Part::GeomArcOfCircle *>(geom2))->getCenter();
|
|
else if( geom2->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
|
|
const Part::GeomLineSegment *l2 = static_cast<const Part::GeomLineSegment *>(geom2);
|
|
center2= (l2->getStartPoint() + l2->getEndPoint())/2;
|
|
}
|
|
|
|
Base::Vector3d direction=center2-focus;
|
|
double tapprox=atan2(direction.y,direction.x)-phi;
|
|
|
|
Base::Vector3d PoH = Base::Vector3d(center.x+majord*cosh(tapprox)*cos(phi)-minord*sinh(tapprox)*sin(phi),
|
|
center.y+majord*cosh(tapprox)*sin(phi)+minord*sinh(tapprox)*cos(phi), 0);
|
|
|
|
try {
|
|
// Add a point
|
|
Gui::cmdAppObjectArgs(Obj, "addGeometry(Part.Point(App.Vector(%f,%f,0)))",
|
|
PoH.x,PoH.y);
|
|
int GeoIdPoint = Obj->getHighestCurveIndex();
|
|
|
|
// Point on first object
|
|
Gui::cmdAppObjectArgs(Obj,"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
|
GeoIdPoint,Sketcher::start,geoId1); // constrain major axis
|
|
// Point on second object
|
|
Gui::cmdAppObjectArgs(Obj,"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
|
GeoIdPoint,Sketcher::start,geoId2); // constrain major axis
|
|
// tangent via point
|
|
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('TangentViaPoint',%d,%d,%d,%d))",
|
|
geoId1, geoId2 ,GeoIdPoint, Sketcher::start);
|
|
}
|
|
catch (const Base::Exception& e) {
|
|
Base::Console().Error("%s\n", e.what());
|
|
Gui::Command::abortCommand();
|
|
|
|
tryAutoRecompute(Obj);
|
|
return;
|
|
}
|
|
|
|
Gui::Command::commitCommand();
|
|
|
|
tryAutoRecompute(Obj);
|
|
}
|
|
|
|
/// Makes a simple tangency constraint using extra point + tangent via point
|
|
/// aop => an arc of parabola
|
|
/// geom2 => any of an arc of parabola, an arc of hyperbola an arc of ellipse, a circle, or an arc (of circle)
|
|
/// geoId1 => geoid of the arc of parabola
|
|
/// geoId2 => geoid of geom2
|
|
/// NOTE: A command must be opened before calling this function, which this function
|
|
/// commits or aborts as appropriate. The reason is for compatibility reasons with
|
|
/// other code e.g. "Autoconstraints" in DrawSketchHandler.cpp
|
|
void SketcherGui::makeTangentToArcOfParabolaviaNewPoint(Sketcher::SketchObject* Obj,
|
|
const Part::GeomArcOfParabola *aop,
|
|
const Part::Geometry *geom2,
|
|
int geoId1,
|
|
int geoId2
|
|
)
|
|
{
|
|
|
|
Base::Vector3d focus = aop->getFocus();
|
|
|
|
Base::Vector3d center2;
|
|
|
|
if( geom2->getTypeId() == Part::GeomArcOfParabola::getClassTypeId())
|
|
center2= (static_cast<const Part::GeomArcOfParabola *>(geom2))->getFocus();
|
|
else if( geom2->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId()){
|
|
const Part::GeomArcOfHyperbola *aoh2 = static_cast<const Part::GeomArcOfHyperbola *>(geom2);
|
|
Base::Vector3d dirmaj2 = aoh2->getMajorAxisDir();
|
|
double majord2 = aoh2->getMajorRadius();
|
|
double minord2 = aoh2->getMinorRadius();
|
|
double df2 = sqrt(majord2*majord2+minord2*minord2);
|
|
center2 = aoh2->getCenter()+df2*dirmaj2; // positive focus
|
|
}
|
|
else if( geom2->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId())
|
|
center2= (static_cast<const Part::GeomArcOfEllipse *>(geom2))->getCenter();
|
|
else if( geom2->getTypeId() == Part::GeomEllipse::getClassTypeId())
|
|
center2= (static_cast<const Part::GeomEllipse *>(geom2))->getCenter();
|
|
else if( geom2->getTypeId() == Part::GeomCircle::getClassTypeId())
|
|
center2= (static_cast<const Part::GeomCircle *>(geom2))->getCenter();
|
|
else if( geom2->getTypeId() == Part::GeomArcOfCircle::getClassTypeId())
|
|
center2= (static_cast<const Part::GeomArcOfCircle *>(geom2))->getCenter();
|
|
else if( geom2->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
|
|
const Part::GeomLineSegment *l2 = static_cast<const Part::GeomLineSegment *>(geom2);
|
|
center2= (l2->getStartPoint() + l2->getEndPoint())/2;
|
|
}
|
|
|
|
Base::Vector3d direction = center2-focus;
|
|
|
|
Base::Vector3d PoP = focus + direction / 2;
|
|
|
|
try {
|
|
// Add a point
|
|
Gui::cmdAppObjectArgs(Obj, "addGeometry(Part.Point(App.Vector(%f,%f,0)))",
|
|
PoP.x,PoP.y);
|
|
int GeoIdPoint = Obj->getHighestCurveIndex();
|
|
|
|
// Point on first object
|
|
Gui::cmdAppObjectArgs(Obj,"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
|
GeoIdPoint,Sketcher::start,geoId1); // constrain major axis
|
|
// Point on second object
|
|
Gui::cmdAppObjectArgs(Obj,"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
|
GeoIdPoint,Sketcher::start,geoId2); // constrain major axis
|
|
// tangent via point
|
|
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('TangentViaPoint',%d,%d,%d,%d))",
|
|
geoId1, geoId2 ,GeoIdPoint, Sketcher::start);
|
|
}
|
|
catch (const Base::Exception& e) {
|
|
Base::Console().Error("%s\n", e.what());
|
|
Gui::Command::abortCommand();
|
|
|
|
tryAutoRecompute(Obj);
|
|
return;
|
|
}
|
|
|
|
Gui::Command::commitCommand();
|
|
tryAutoRecompute(Obj);
|
|
}
|
|
|
|
std::string SketcherGui::getStrippedPythonExceptionString(const Base::Exception& e)
|
|
{
|
|
std::string msg = e.what();
|
|
|
|
if( msg.length() > 26 && msg.substr(0,26) == "FreeCAD exception thrown (") {
|
|
return msg.substr(26, msg.length()-27);
|
|
}
|
|
else
|
|
return msg;
|
|
}
|
|
|
|
bool SketcherGui::tryAutoRecompute(Sketcher::SketchObject* obj, bool &autoremoveredundants)
|
|
{
|
|
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
|
bool autoRecompute = hGrp->GetBool("AutoRecompute",false);
|
|
bool autoRemoveRedundants = hGrp->GetBool("AutoRemoveRedundants",false);
|
|
|
|
// We need to make sure the solver has right redundancy information before trying to remove the redundants.
|
|
// for example if a non-driving constraint has been added.
|
|
if(autoRemoveRedundants && autoRecompute)
|
|
obj->solve();
|
|
|
|
if(autoRemoveRedundants)
|
|
obj->autoRemoveRedundants();
|
|
|
|
if (autoRecompute)
|
|
Gui::Command::updateActive();
|
|
|
|
autoremoveredundants = autoRemoveRedundants;
|
|
|
|
return autoRecompute;
|
|
}
|
|
|
|
bool SketcherGui::tryAutoRecompute(Sketcher::SketchObject* obj)
|
|
{
|
|
bool autoremoveredundants;
|
|
|
|
return tryAutoRecompute(obj,autoremoveredundants);
|
|
}
|
|
|
|
void SketcherGui::tryAutoRecomputeIfNotSolve(Sketcher::SketchObject* obj)
|
|
{
|
|
bool autoremoveredundants;
|
|
|
|
if(!tryAutoRecompute(obj,autoremoveredundants)) {
|
|
obj->solve();
|
|
|
|
if(autoremoveredundants) {
|
|
obj->autoRemoveRedundants();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool SketcherGui::checkConstraint(const std::vector< Sketcher::Constraint * > &vals, ConstraintType type, int geoid, PointPos pos)
|
|
{
|
|
for (std::vector< Sketcher::Constraint * >::const_iterator itc= vals.begin(); itc != vals.end(); ++itc) {
|
|
if ((*itc)->Type == type && (*itc)->First == geoid && (*itc)->FirstPos == pos){
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
void SketcherGui::doEndpointTangency(Sketcher::SketchObject* Obj, Gui::SelectionObject &selection,
|
|
int GeoId1, int GeoId2, PointPos PosId1, PointPos PosId2){
|
|
// This code supports simple B-spline endpoint tangency to any other geometric curve
|
|
const Part::Geometry *geom1 = Obj->getGeometry(GeoId1);
|
|
const Part::Geometry *geom2 = Obj->getGeometry(GeoId2);
|
|
|
|
if (geom1 && geom2 &&
|
|
(geom1->getTypeId() == Part::GeomBSplineCurve::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomBSplineCurve::getClassTypeId())){
|
|
|
|
if(geom1->getTypeId() != Part::GeomBSplineCurve::getClassTypeId()) {
|
|
std::swap(GeoId1,GeoId2);
|
|
std::swap(PosId1,PosId2);
|
|
}
|
|
// GeoId1 is the B-spline now
|
|
} // end of code supports simple B-spline endpoint tangency
|
|
|
|
Gui::cmdAppObjectArgs(selection.getObject(), "addConstraint(Sketcher.Constraint('Tangent',%d,%d,%d,%d)) ",
|
|
GeoId1,PosId1,GeoId2,PosId2);
|
|
}
|
|
|
|
|
|
namespace SketcherGui {
|
|
|
|
struct SelIdPair{
|
|
int GeoId;
|
|
Sketcher::PointPos PosId;
|
|
};
|
|
|
|
struct SketchSelection{
|
|
enum GeoType {
|
|
Point,
|
|
Line,
|
|
Circle,
|
|
Arc
|
|
};
|
|
int setUp(void);
|
|
struct SketchSelectionItem {
|
|
GeoType type;
|
|
int GeoId;
|
|
bool Extern;
|
|
};
|
|
std::list<SketchSelectionItem> Items;
|
|
QString ErrorMsg;
|
|
};
|
|
|
|
int SketchSelection::setUp(void)
|
|
{
|
|
std::vector<Gui::SelectionObject> selection = Gui::Selection().getSelectionEx();
|
|
|
|
Sketcher::SketchObject *SketchObj=0;
|
|
std::vector<std::string> SketchSubNames;
|
|
std::vector<std::string> SupportSubNames;
|
|
|
|
// only one sketch with its subelements are allowed to be selected
|
|
if (selection.size() == 1) {
|
|
// if one selectetd, only sketch allowed. should be done by activity of command
|
|
if(!selection[0].getObject()->getTypeId().isDerivedFrom(Sketcher::SketchObject::getClassTypeId()))
|
|
{
|
|
ErrorMsg = QObject::tr("Only sketch and its support is allowed to select");
|
|
return -1;
|
|
}
|
|
|
|
SketchObj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
|
|
SketchSubNames = selection[0].getSubNames();
|
|
} else if(selection.size() == 2) {
|
|
if(selection[0].getObject()->getTypeId().isDerivedFrom(Sketcher::SketchObject::getClassTypeId())){
|
|
SketchObj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
|
|
// check if the none sketch object is the support of the sketch
|
|
if(selection[1].getObject() != SketchObj->Support.getValue()){
|
|
ErrorMsg = QObject::tr("Only sketch and its support is allowed to select");
|
|
return-1;
|
|
}
|
|
// assume always a Part::Feature derived object as support
|
|
assert(selection[1].getObject()->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId()));
|
|
SketchSubNames = selection[0].getSubNames();
|
|
SupportSubNames = selection[1].getSubNames();
|
|
|
|
} else if (selection[1].getObject()->getTypeId().isDerivedFrom(Sketcher::SketchObject::getClassTypeId())) {
|
|
SketchObj = static_cast<Sketcher::SketchObject*>(selection[1].getObject());
|
|
// check if the none sketch object is the support of the sketch
|
|
if(selection[0].getObject() != SketchObj->Support.getValue()){
|
|
ErrorMsg = QObject::tr("Only sketch and its support is allowed to select");
|
|
return -1;
|
|
}
|
|
// assume always a Part::Feature derived object as support
|
|
assert(selection[0].getObject()->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId()));
|
|
SketchSubNames = selection[1].getSubNames();
|
|
SupportSubNames = selection[0].getSubNames();
|
|
|
|
} else {
|
|
ErrorMsg = QObject::tr("One of the selected has to be on the sketch");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return Items.size();
|
|
}
|
|
|
|
} // namespace SketcherGui
|
|
|
|
/* Constrain commands =======================================================*/
|
|
|
|
namespace SketcherGui {
|
|
/**
|
|
* @brief The SelType enum
|
|
* Types of sketch elements that can be (pre)selected. The root/origin and the
|
|
* axes are put up separately so that they can be specifically disallowed, for
|
|
* example, when in lock, horizontal, or vertical constraint modes.
|
|
*/
|
|
enum SelType {
|
|
SelUnknown = 0,
|
|
SelVertex = 1,
|
|
SelVertexOrRoot = 64,
|
|
SelRoot = 2,
|
|
SelEdge = 4,
|
|
SelEdgeOrAxis = 128,
|
|
SelHAxis = 8,
|
|
SelVAxis = 16,
|
|
SelExternalEdge = 32
|
|
};
|
|
|
|
/**
|
|
* @brief The GenericConstraintSelection class
|
|
* SelectionFilterGate with changeable filters. In a constraint creation mode
|
|
* like point on object, if the first selection object can be a point, the next
|
|
* has to be a curve for the constraint to make sense. Thus filters are
|
|
* changeable so that same filter can be kept on while in one mode.
|
|
*/
|
|
class GenericConstraintSelection : public Gui::SelectionFilterGate
|
|
{
|
|
App::DocumentObject* object;
|
|
public:
|
|
GenericConstraintSelection(App::DocumentObject* obj)
|
|
: Gui::SelectionFilterGate((Gui::SelectionFilter*)0)
|
|
, object(obj), allowedSelTypes(0)
|
|
{}
|
|
|
|
bool allow(App::Document *, App::DocumentObject *pObj, const char *sSubName)
|
|
{
|
|
if (pObj != this->object)
|
|
return false;
|
|
if (!sSubName || sSubName[0] == '\0')
|
|
return false;
|
|
std::string element(sSubName);
|
|
if ( (allowedSelTypes & (SelRoot | SelVertexOrRoot) && element.substr(0,9) == "RootPoint") ||
|
|
(allowedSelTypes & (SelVertex | SelVertexOrRoot) && element.substr(0,6) == "Vertex") ||
|
|
(allowedSelTypes & (SelEdge | SelEdgeOrAxis) && element.substr(0,4) == "Edge") ||
|
|
(allowedSelTypes & (SelHAxis | SelEdgeOrAxis) && element.substr(0,6) == "H_Axis") ||
|
|
(allowedSelTypes & (SelVAxis | SelEdgeOrAxis) && element.substr(0,6) == "V_Axis") ||
|
|
(allowedSelTypes & SelExternalEdge && element.substr(0,12) == "ExternalEdge"))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
void setAllowedSelTypes(unsigned int types) {
|
|
if (types < 256) allowedSelTypes = types;
|
|
}
|
|
|
|
protected:
|
|
int allowedSelTypes;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* @brief The CmdSketcherConstraint class
|
|
* Superclass for all sketcher constraints to ease generation of constraint
|
|
* creation modes.
|
|
*/
|
|
class CmdSketcherConstraint : public Gui::Command
|
|
{
|
|
friend class DrawSketchHandlerGenConstraint;
|
|
public:
|
|
CmdSketcherConstraint(const char* name)
|
|
: Command(name) {}
|
|
|
|
virtual ~CmdSketcherConstraint(){}
|
|
|
|
virtual const char* className() const
|
|
{ return "CmdSketcherConstraint"; }
|
|
|
|
protected:
|
|
/**
|
|
* @brief allowedSelSequences
|
|
* Each element is a vector representing sequence of selections allowable.
|
|
* DrawSketchHandlerGenConstraint will use these to filter elements and
|
|
* generate sequences to be passed to applyConstraint().
|
|
* Whenever any sequence is completed, applyConstraint() is called, so it's
|
|
* best to keep them prefix-free.
|
|
* Be mindful that when SelVertex and SelRoot are given preference over
|
|
* SelVertexOrRoot, and similar for edges/axes. Thus if a vertex is selected
|
|
* when SelVertex and SelVertexOrRoot are both applicable, only sequences with
|
|
* SelVertex will be continue.
|
|
*
|
|
* TODO: Introduce structs to allow keeping first selection
|
|
*/
|
|
std::vector<std::vector<SketcherGui::SelType> > allowedSelSequences;
|
|
|
|
const char** constraintCursor = 0;
|
|
|
|
virtual void applyConstraint(std::vector<SelIdPair> &, int) {}
|
|
virtual void activated(int /*iMsg*/);
|
|
virtual bool isActive(void)
|
|
{ return isCreateGeoActive(getActiveGuiDocument()); }
|
|
};
|
|
|
|
extern char cursor_crosshair_color[];
|
|
|
|
/* XPM */
|
|
static const char *cursor_genericconstraint[]={
|
|
"32 32 2 1",
|
|
" c None",
|
|
cursor_crosshair_color,
|
|
" + ",
|
|
" + ",
|
|
" + ",
|
|
" + ",
|
|
" + ",
|
|
" ",
|
|
"+++++ +++++ ",
|
|
" ",
|
|
" + ",
|
|
" + ",
|
|
" + ",
|
|
" + ",
|
|
" + ",
|
|
" ",
|
|
" ",
|
|
" ",
|
|
" ",
|
|
" ",
|
|
" ",
|
|
" ",
|
|
" ",
|
|
" ",
|
|
" ",
|
|
" ",
|
|
" ",
|
|
" ",
|
|
" ",
|
|
" ",
|
|
" ",
|
|
" ",
|
|
" ",
|
|
" ",};
|
|
|
|
class DrawSketchHandlerGenConstraint: public DrawSketchHandler
|
|
{
|
|
public:
|
|
DrawSketchHandlerGenConstraint(const char* cursor[], CmdSketcherConstraint *_cmd)
|
|
: constraintCursor(cursor), cmd(_cmd), seqIndex(0) {}
|
|
virtual ~DrawSketchHandlerGenConstraint()
|
|
{
|
|
Gui::Selection().rmvSelectionGate();
|
|
}
|
|
|
|
virtual void activated(ViewProviderSketch *)
|
|
{
|
|
selFilterGate = new GenericConstraintSelection(sketchgui->getObject());
|
|
|
|
resetOngoingSequences();
|
|
|
|
selSeq.clear();
|
|
|
|
Gui::Selection().rmvSelectionGate();
|
|
Gui::Selection().addSelectionGate(selFilterGate);
|
|
|
|
setCrosshairColor();
|
|
|
|
// Constrain icon size in px
|
|
int iconSize = 16;
|
|
QPixmap cursorPixmap(cursor_genericconstraint),
|
|
icon = Gui::BitmapFactory().pixmap(cmd->sPixmap).scaledToWidth(iconSize);
|
|
QPainter cursorPainter;
|
|
cursorPainter.begin(&cursorPixmap);
|
|
cursorPainter.drawPixmap(16, 16, icon);
|
|
cursorPainter.end();
|
|
setCursor(cursorPixmap, 7, 7);
|
|
}
|
|
|
|
virtual void mouseMove(Base::Vector2d /*onSketchPos*/) {}
|
|
|
|
virtual bool pressButton(Base::Vector2d /*onSketchPos*/)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
virtual bool releaseButton(Base::Vector2d onSketchPos)
|
|
{
|
|
SelIdPair selIdPair;
|
|
selIdPair.GeoId = Constraint::GeoUndef;
|
|
selIdPair.PosId = Sketcher::none;
|
|
std::stringstream ss;
|
|
SelType newSelType = SelUnknown;
|
|
|
|
//For each SelType allowed, check if button is released there and assign it to selIdPair
|
|
int VtId = sketchgui->getPreselectPoint();
|
|
int CrvId = sketchgui->getPreselectCurve();
|
|
int CrsId = sketchgui->getPreselectCross();
|
|
if (allowedSelTypes & (SelRoot | SelVertexOrRoot) && CrsId == 0) {
|
|
selIdPair.GeoId = Sketcher::GeoEnum::RtPnt;
|
|
selIdPair.PosId = Sketcher::start;
|
|
newSelType = (allowedSelTypes & SelRoot) ? SelRoot : SelVertexOrRoot;
|
|
ss << "RootPoint";
|
|
}
|
|
else if (allowedSelTypes & (SelVertex | SelVertexOrRoot) && VtId >= 0) {
|
|
sketchgui->getSketchObject()->getGeoVertexIndex(VtId,
|
|
selIdPair.GeoId,
|
|
selIdPair.PosId);
|
|
newSelType = (allowedSelTypes & SelVertex) ? SelVertex : SelVertexOrRoot;
|
|
ss << "Vertex" << VtId + 1;
|
|
}
|
|
else if (allowedSelTypes & (SelEdge | SelEdgeOrAxis) && CrvId >= 0) {
|
|
selIdPair.GeoId = CrvId;
|
|
newSelType = (allowedSelTypes & SelEdge) ? SelEdge : SelEdgeOrAxis;
|
|
ss << "Edge" << CrvId + 1;
|
|
}
|
|
else if (allowedSelTypes & (SelHAxis | SelEdgeOrAxis) && CrsId == 1) {
|
|
selIdPair.GeoId = Sketcher::GeoEnum::HAxis;
|
|
newSelType = (allowedSelTypes & SelHAxis) ? SelHAxis : SelEdgeOrAxis;
|
|
ss << "H_Axis";
|
|
}
|
|
else if (allowedSelTypes & (SelVAxis | SelEdgeOrAxis) && CrsId == 2) {
|
|
selIdPair.GeoId = Sketcher::GeoEnum::VAxis;
|
|
newSelType = (allowedSelTypes & SelVAxis) ? SelVAxis : SelEdgeOrAxis;
|
|
ss << "V_Axis";
|
|
}
|
|
else if (allowedSelTypes & SelExternalEdge && CrvId <= Sketcher::GeoEnum::RefExt) {
|
|
//TODO: Figure out how this works
|
|
selIdPair.GeoId = CrvId;
|
|
newSelType = SelExternalEdge;
|
|
ss << "ExternalEdge" << Sketcher::GeoEnum::RefExt + 1 - CrvId;
|
|
}
|
|
|
|
if (selIdPair.GeoId == Constraint::GeoUndef) {
|
|
// If mouse is released on "blank" space, start over
|
|
selSeq.clear();
|
|
resetOngoingSequences();
|
|
Gui::Selection().clearSelection();
|
|
}
|
|
else {
|
|
// If mouse is released on something allowed, select it and move forward
|
|
selSeq.push_back(selIdPair);
|
|
Gui::Selection().addSelection(sketchgui->getSketchObject()->getDocument()->getName(),
|
|
sketchgui->getSketchObject()->getNameInDocument(),
|
|
ss.str().c_str(),
|
|
onSketchPos.x,
|
|
onSketchPos.y,
|
|
0.f);
|
|
_tempOnSequences.clear();
|
|
allowedSelTypes = 0;
|
|
for (std::set<int>::iterator token = ongoingSequences.begin();
|
|
token != ongoingSequences.end(); ++token) {
|
|
if ((cmd->allowedSelSequences).at(*token).at(seqIndex) == newSelType) {
|
|
if (seqIndex == (cmd->allowedSelSequences).at(*token).size()-1) {
|
|
// One of the sequences is completed. Pass to cmd->applyConstraint
|
|
cmd->applyConstraint(selSeq, *token); // replace arg 2 by ongoingToken
|
|
|
|
selSeq.clear();
|
|
resetOngoingSequences();
|
|
|
|
return true;
|
|
}
|
|
_tempOnSequences.insert(*token);
|
|
allowedSelTypes = allowedSelTypes | (cmd->allowedSelSequences).at(*token).at(seqIndex+1);
|
|
}
|
|
}
|
|
|
|
// Progress to next seqIndex
|
|
std::swap(_tempOnSequences, ongoingSequences);
|
|
seqIndex++;
|
|
selFilterGate->setAllowedSelTypes(allowedSelTypes);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
protected:
|
|
const char** constraintCursor;
|
|
CmdSketcherConstraint* cmd;
|
|
|
|
GenericConstraintSelection* selFilterGate = nullptr;
|
|
|
|
std::vector<SelIdPair> selSeq;
|
|
unsigned int allowedSelTypes = 0;
|
|
|
|
/// indices of currently ongoing sequences in cmd->allowedSequences
|
|
std::set<int> ongoingSequences, _tempOnSequences;
|
|
/// Index within the selection sequences active
|
|
unsigned int seqIndex;
|
|
|
|
void resetOngoingSequences() {
|
|
ongoingSequences.clear();
|
|
for (unsigned int i = 0; i < cmd->allowedSelSequences.size(); i++) {
|
|
ongoingSequences.insert(i);
|
|
}
|
|
seqIndex = 0;
|
|
|
|
// Estimate allowed selections from the first types in allowedSelTypes
|
|
allowedSelTypes = 0;
|
|
for (std::vector< std::vector< SelType > >::const_iterator it = cmd->allowedSelSequences.begin();
|
|
it != cmd->allowedSelSequences.end(); ++it) {
|
|
allowedSelTypes = allowedSelTypes | (*it).at(seqIndex);
|
|
}
|
|
selFilterGate->setAllowedSelTypes(allowedSelTypes);
|
|
|
|
Gui::Selection().clearSelection();
|
|
}
|
|
};
|
|
|
|
void CmdSketcherConstraint::activated(int /*iMsg*/)
|
|
{
|
|
ActivateHandler(getActiveGuiDocument(),
|
|
new DrawSketchHandlerGenConstraint(constraintCursor, this));
|
|
getSelection().clearSelection();
|
|
}
|
|
|
|
// ============================================================================
|
|
|
|
/* XPM */
|
|
static const char *cursor_createhoriconstraint[]={
|
|
"32 32 3 1",
|
|
"+ c white",
|
|
"# c red",
|
|
". c None",
|
|
"......+.........................",
|
|
"......+.........................",
|
|
"......+.........................",
|
|
"......+.........................",
|
|
"......+.........................",
|
|
"................................",
|
|
"+++++...+++++...................",
|
|
"................................",
|
|
"......+.........................",
|
|
"......+.........................",
|
|
"......+.........................",
|
|
"......+.........................",
|
|
"......+......#..........#.......",
|
|
".............#..........#.......",
|
|
".............#..........#.......",
|
|
".............#..........#.......",
|
|
".............#..........#.......",
|
|
".............#..........#.......",
|
|
".............############.......",
|
|
".............#..........#.......",
|
|
".............#..........#.......",
|
|
".............#..........#.......",
|
|
".............#..........#.......",
|
|
".............#..........#.......",
|
|
".............#..........#.......",
|
|
"................................",
|
|
"................................",
|
|
"................................",
|
|
"................................",
|
|
"................................",
|
|
"................................",
|
|
"................................"};
|
|
|
|
class CmdSketcherConstrainHorizontal : public CmdSketcherConstraint
|
|
{
|
|
public:
|
|
CmdSketcherConstrainHorizontal();
|
|
virtual ~CmdSketcherConstrainHorizontal(){}
|
|
virtual const char* className() const
|
|
{ return "CmdSketcherConstrainHorizontal"; }
|
|
protected:
|
|
virtual void activated(int iMsg);
|
|
virtual void applyConstraint(std::vector<SelIdPair> &selSeq, int seqIndex);
|
|
|
|
};
|
|
|
|
CmdSketcherConstrainHorizontal::CmdSketcherConstrainHorizontal()
|
|
:CmdSketcherConstraint("Sketcher_ConstrainHorizontal")
|
|
{
|
|
sAppModule = "Sketcher";
|
|
sGroup = QT_TR_NOOP("Sketcher");
|
|
sMenuText = QT_TR_NOOP("Constrain horizontally");
|
|
sToolTipText = QT_TR_NOOP("Create a horizontal constraint on the selected item");
|
|
sWhatsThis = "Sketcher_ConstrainHorizontal";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "Constraint_Horizontal";
|
|
sAccel = "H";
|
|
eType = ForEdit;
|
|
|
|
allowedSelSequences = {{SelEdge}};
|
|
constraintCursor = cursor_createhoriconstraint;
|
|
}
|
|
|
|
void CmdSketcherConstrainHorizontal::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
|
|
// get the selection
|
|
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
|
|
|
|
// only one sketch with its subelements are allowed to be selected
|
|
if (selection.size() != 1 || !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
|
|
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
|
bool constraintMode = hGrp->GetBool("ContinuousConstraintMode", true);
|
|
|
|
if (constraintMode) {
|
|
ActivateHandler(getActiveGuiDocument(),
|
|
new DrawSketchHandlerGenConstraint(constraintCursor, this));
|
|
getSelection().clearSelection();
|
|
} else {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select an edge from the sketch."));
|
|
}
|
|
return;
|
|
}
|
|
|
|
// get the needed lists and objects
|
|
const std::vector<std::string> &SubNames = selection[0].getSubNames();
|
|
Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
|
|
const std::vector< Sketcher::Constraint * > &vals = Obj->Constraints.getValues();
|
|
|
|
std::vector<int> edgegeoids;
|
|
std::vector<int> pointgeoids;
|
|
std::vector<Sketcher::PointPos> pointpos;
|
|
|
|
int fixedpoints = 0;
|
|
// go through the selected subelements
|
|
for (std::vector<std::string>::const_iterator it=SubNames.begin(); it != SubNames.end(); ++it) {
|
|
int GeoId;
|
|
Sketcher::PointPos PosId;
|
|
getIdsFromName((*it), Obj, GeoId, PosId);
|
|
|
|
|
|
if (isEdge(GeoId,PosId)) {// it is an edge
|
|
const Part::Geometry *geo = Obj->getGeometry(GeoId);
|
|
if (geo->getTypeId() != Part::GeomLineSegment::getClassTypeId()) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Impossible constraint"),
|
|
QObject::tr("The selected edge is not a line segment"));
|
|
return;
|
|
}
|
|
|
|
// check if the edge already has a Horizontal/Vertical/Block constraint
|
|
for (std::vector< Sketcher::Constraint * >::const_iterator it= vals.begin();
|
|
it != vals.end(); ++it) {
|
|
if ((*it)->Type == Sketcher::Horizontal && (*it)->First == GeoId && (*it)->FirstPos == Sketcher::none){
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Double constraint"),
|
|
QObject::tr("The selected edge already has a horizontal constraint!"));
|
|
return;
|
|
}
|
|
if ((*it)->Type == Sketcher::Vertical && (*it)->First == GeoId && (*it)->FirstPos == Sketcher::none) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Impossible constraint"),
|
|
QObject::tr("The selected edge already has a vertical constraint!"));
|
|
return;
|
|
}
|
|
// check if the edge already has a Block constraint
|
|
if ((*it)->Type == Sketcher::Block && (*it)->First == GeoId && (*it)->FirstPos == Sketcher::none) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Impossible constraint"),
|
|
QObject::tr("The selected edge already has a Block constraint!"));
|
|
return;
|
|
}
|
|
}
|
|
edgegeoids.push_back(GeoId);
|
|
}
|
|
else if(isVertex(GeoId,PosId)) {
|
|
// can be a point, a construction point, an external point or root
|
|
|
|
if(isPointOrSegmentFixed(Obj, GeoId))
|
|
fixedpoints++;
|
|
|
|
pointgeoids.push_back(GeoId);
|
|
pointpos.push_back(PosId);
|
|
}
|
|
}
|
|
|
|
if (edgegeoids.empty() && pointgeoids.empty()) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Impossible constraint"),
|
|
QObject::tr("The selected item(s) can't accept a horizontal constraint!"));
|
|
return;
|
|
}
|
|
|
|
// if there is at least one edge selected, ignore the point alignment functionality
|
|
if (!edgegeoids.empty()) {
|
|
// undo command open
|
|
openCommand("add horizontal constraint");
|
|
for (std::vector<int>::iterator it=edgegeoids.begin(); it != edgegeoids.end(); it++) {
|
|
// issue the actual commands to create the constraint
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),
|
|
"addConstraint(Sketcher.Constraint('Horizontal',%d))", *it);
|
|
}
|
|
}
|
|
else if (fixedpoints <= 1) { // pointgeoids
|
|
// undo command open
|
|
openCommand("add horizontal alignment");
|
|
std::vector<int>::iterator it;
|
|
std::vector<Sketcher::PointPos>::iterator itp;
|
|
for (it=pointgeoids.begin(), itp=pointpos.begin(); it != std::prev(pointgeoids.end()) && itp != std::prev(pointpos.end()); it++,itp++) {
|
|
// issue the actual commands to create the constraint
|
|
Gui::cmdAppObjectArgs(selection[0].getObject()
|
|
,"addConstraint(Sketcher.Constraint('Horizontal',%d,%d,%d,%d)) "
|
|
,*it,*itp,*std::next(it),*std::next(itp));
|
|
}
|
|
}
|
|
else { // vertex mode, fixedpoints > 1
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Impossible constraint"),
|
|
QObject::tr("There are more than one fixed point selected. Select a maximum of one fixed point!"));
|
|
return;
|
|
}
|
|
// finish the transaction and update
|
|
commitCommand();
|
|
|
|
tryAutoRecompute(Obj);
|
|
|
|
// clear the selection (convenience)
|
|
getSelection().clearSelection();
|
|
}
|
|
|
|
void CmdSketcherConstrainHorizontal::applyConstraint(std::vector<SelIdPair> &selSeq, int seqIndex)
|
|
{
|
|
switch (seqIndex) {
|
|
case 0: // {Edge}
|
|
// create the constraint
|
|
SketcherGui::ViewProviderSketch* sketchgui = static_cast<SketcherGui::ViewProviderSketch*>(getActiveGuiDocument()->getInEdit());
|
|
Sketcher::SketchObject* Obj = sketchgui->getSketchObject();
|
|
|
|
const std::vector< Sketcher::Constraint * > &vals = Obj->Constraints.getValues();
|
|
|
|
int CrvId = selSeq.front().GeoId;
|
|
if (CrvId != -1) {
|
|
const Part::Geometry *geo = Obj->getGeometry(CrvId);
|
|
if (geo->getTypeId() != Part::GeomLineSegment::getClassTypeId()) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Impossible constraint"),
|
|
QObject::tr("The selected edge is not a line segment"));
|
|
return;
|
|
}
|
|
|
|
// check if the edge already has a Horizontal/Vertical/Block constraint
|
|
for (std::vector< Sketcher::Constraint * >::const_iterator it= vals.begin();
|
|
it != vals.end(); ++it) {
|
|
if ((*it)->Type == Sketcher::Horizontal && (*it)->First == CrvId && (*it)->FirstPos == Sketcher::none){
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Double constraint"),
|
|
QObject::tr("The selected edge already has a horizontal constraint!"));
|
|
return;
|
|
}
|
|
if ((*it)->Type == Sketcher::Vertical && (*it)->First == CrvId && (*it)->FirstPos == Sketcher::none) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Impossible constraint"),
|
|
QObject::tr("The selected edge already has a vertical constraint!"));
|
|
return;
|
|
}
|
|
// check if the edge already has a Block constraint
|
|
if ((*it)->Type == Sketcher::Block && (*it)->First == CrvId && (*it)->FirstPos == Sketcher::none) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Impossible constraint"),
|
|
QObject::tr("The selected edge already has a Block constraint!"));
|
|
return;
|
|
}
|
|
}
|
|
|
|
// undo command open
|
|
Gui::Command::openCommand("add horizontal constraint");
|
|
// issue the actual commands to create the constraint
|
|
Gui::cmdAppObjectArgs(sketchgui->getObject(), "addConstraint(Sketcher.Constraint('Horizontal',%d)) ",CrvId);
|
|
// finish the transaction and update
|
|
Gui::Command::commitCommand();
|
|
|
|
tryAutoRecompute(Obj);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// ================================================================================
|
|
|
|
static const char *cursor_createvertconstraint[]={
|
|
"32 32 3 1",
|
|
"+ c white",
|
|
"# c red",
|
|
". c None",
|
|
"......+.........................",
|
|
"......+.........................",
|
|
"......+.........................",
|
|
"......+.........................",
|
|
"......+.........................",
|
|
"................................",
|
|
"+++++...+++++...................",
|
|
"................................",
|
|
"......+.........................",
|
|
"......+.........................",
|
|
"......+.........................",
|
|
"......+.........................",
|
|
"......+......#..........#.......",
|
|
".............#..........#.......",
|
|
".............#..........#.......",
|
|
".............#..........#.......",
|
|
".............#..........#.......",
|
|
".............#..........#.......",
|
|
".............#..........#.......",
|
|
".............#..........#.......",
|
|
"..............#........#........",
|
|
"...............#......#.........",
|
|
"................#....#..........",
|
|
".................#..#...........",
|
|
"..................##............",
|
|
"................................",
|
|
"................................",
|
|
"................................",
|
|
"................................",
|
|
"................................",
|
|
"................................",
|
|
"................................"};
|
|
|
|
class CmdSketcherConstrainVertical : public CmdSketcherConstraint
|
|
{
|
|
public:
|
|
CmdSketcherConstrainVertical();
|
|
virtual ~CmdSketcherConstrainVertical(){}
|
|
virtual const char* className() const
|
|
{ return "CmdSketcherConstrainVertical"; }
|
|
protected:
|
|
virtual void activated(int iMsg);
|
|
virtual void applyConstraint(std::vector<SelIdPair> &selSeq, int seqIndex);
|
|
|
|
};
|
|
|
|
CmdSketcherConstrainVertical::CmdSketcherConstrainVertical()
|
|
:CmdSketcherConstraint("Sketcher_ConstrainVertical")
|
|
{
|
|
sAppModule = "Sketcher";
|
|
sGroup = QT_TR_NOOP("Sketcher");
|
|
sMenuText = QT_TR_NOOP("Constrain vertically");
|
|
sToolTipText = QT_TR_NOOP("Create a vertical constraint on the selected item");
|
|
sWhatsThis = "Sketcher_ConstrainVertical";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "Constraint_Vertical";
|
|
sAccel = "V";
|
|
eType = ForEdit;
|
|
|
|
allowedSelSequences = {{SelEdge}};
|
|
constraintCursor = cursor_createvertconstraint;
|
|
}
|
|
|
|
void CmdSketcherConstrainVertical::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
|
|
// get the selection
|
|
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
|
|
|
|
// only one sketch with its subelements are allowed to be selected
|
|
if (selection.size() != 1 || !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
|
|
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
|
bool constraintMode = hGrp->GetBool("ContinuousConstraintMode", true);
|
|
|
|
if (constraintMode) {
|
|
ActivateHandler(getActiveGuiDocument(),
|
|
new DrawSketchHandlerGenConstraint(constraintCursor, this));
|
|
getSelection().clearSelection();
|
|
} else {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select an edge from the sketch."));
|
|
}
|
|
return;
|
|
}
|
|
|
|
// get the needed lists and objects
|
|
const std::vector<std::string> &SubNames = selection[0].getSubNames();
|
|
Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
|
|
const std::vector< Sketcher::Constraint * > &vals = Obj->Constraints.getValues();
|
|
|
|
std::vector<int> edgegeoids;
|
|
std::vector<int> pointgeoids;
|
|
std::vector<Sketcher::PointPos> pointpos;
|
|
|
|
int fixedpoints = 0;
|
|
// go through the selected subelements
|
|
for (std::vector<std::string>::const_iterator it=SubNames.begin(); it != SubNames.end(); ++it) {
|
|
int GeoId;
|
|
Sketcher::PointPos PosId;
|
|
getIdsFromName((*it), Obj, GeoId, PosId);
|
|
|
|
|
|
if (isEdge(GeoId,PosId)) {// it is an edge
|
|
const Part::Geometry *geo = Obj->getGeometry(GeoId);
|
|
if (geo->getTypeId() != Part::GeomLineSegment::getClassTypeId()) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Impossible constraint"),
|
|
QObject::tr("The selected edge is not a line segment"));
|
|
return;
|
|
}
|
|
|
|
// check if the edge already has a Horizontal/Vertical/Block constraint
|
|
for (std::vector< Sketcher::Constraint * >::const_iterator it= vals.begin();
|
|
it != vals.end(); ++it) {
|
|
if ((*it)->Type == Sketcher::Vertical && (*it)->First == GeoId && (*it)->FirstPos == Sketcher::none){
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Double constraint"),
|
|
QObject::tr("The selected edge already has a vertical constraint!"));
|
|
return;
|
|
}
|
|
if ((*it)->Type == Sketcher::Horizontal && (*it)->First == GeoId && (*it)->FirstPos == Sketcher::none) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Impossible constraint"),
|
|
QObject::tr("The selected edge already has a horizontal constraint!"));
|
|
return;
|
|
}
|
|
// check if the edge already has a Block constraint
|
|
if ((*it)->Type == Sketcher::Block && (*it)->First == GeoId && (*it)->FirstPos == Sketcher::none) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Impossible constraint"),
|
|
QObject::tr("The selected edge already has a Block constraint!"));
|
|
return;
|
|
}
|
|
}
|
|
edgegeoids.push_back(GeoId);
|
|
}
|
|
else if(isVertex(GeoId,PosId)) {
|
|
// can be a point, a construction point, an external point, root or a blocked geometry
|
|
if(isPointOrSegmentFixed(Obj, GeoId))
|
|
fixedpoints++;
|
|
|
|
pointgeoids.push_back(GeoId);
|
|
pointpos.push_back(PosId);
|
|
}
|
|
}
|
|
|
|
if (edgegeoids.empty() && pointgeoids.empty()) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Impossible constraint"),
|
|
QObject::tr("The selected item(s) can't accept a vertical constraint!"));
|
|
return;
|
|
}
|
|
|
|
// if there is at least one edge selected, ignore the point alignment functionality
|
|
if (!edgegeoids.empty()) {
|
|
// undo command open
|
|
openCommand("add vertical constraint");
|
|
for (std::vector<int>::iterator it=edgegeoids.begin(); it != edgegeoids.end(); it++) {
|
|
// issue the actual commands to create the constraint
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),"addConstraint(Sketcher.Constraint('Vertical',%d)) ", *it);
|
|
}
|
|
}
|
|
else if (fixedpoints <= 1) { // vertex mode, maximum one fixed point
|
|
// undo command open
|
|
openCommand("add vertical alignment");
|
|
std::vector<int>::iterator it;
|
|
std::vector<Sketcher::PointPos>::iterator itp;
|
|
for (it=pointgeoids.begin(), itp=pointpos.begin(); it != std::prev(pointgeoids.end()) && itp != std::prev(pointpos.end()); it++,itp++) {
|
|
// issue the actual commands to create the constraint
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('Vertical',%d,%d,%d,%d)) "
|
|
,*it,*itp,*std::next(it),*std::next(itp));
|
|
}
|
|
}
|
|
else { // vertex mode, fixedpoints > 1
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Impossible constraint"),
|
|
QObject::tr("There are more than one fixed points selected. Select a maximum of one fixed point!"));
|
|
return;
|
|
}
|
|
|
|
// finish the transaction and update
|
|
commitCommand();
|
|
|
|
tryAutoRecompute(Obj);
|
|
|
|
// clear the selection (convenience)
|
|
getSelection().clearSelection();
|
|
}
|
|
|
|
void CmdSketcherConstrainVertical::applyConstraint(std::vector<SelIdPair> &selSeq, int seqIndex)
|
|
{
|
|
switch (seqIndex) {
|
|
case 0: // {Edge}
|
|
// create the constraint
|
|
SketcherGui::ViewProviderSketch* sketchgui = static_cast<SketcherGui::ViewProviderSketch*>(getActiveGuiDocument()->getInEdit());
|
|
Sketcher::SketchObject* Obj = sketchgui->getSketchObject();
|
|
|
|
const std::vector< Sketcher::Constraint * > &vals = Obj->Constraints.getValues();
|
|
|
|
int CrvId = selSeq.front().GeoId;
|
|
if (CrvId != -1) {
|
|
const Part::Geometry *geo = Obj->getGeometry(CrvId);
|
|
if (geo->getTypeId() != Part::GeomLineSegment::getClassTypeId()) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Impossible constraint"),
|
|
QObject::tr("The selected edge is not a line segment"));
|
|
return;
|
|
}
|
|
|
|
// check if the edge already has a Horizontal or Vertical constraint
|
|
for (std::vector< Sketcher::Constraint * >::const_iterator it= vals.begin();
|
|
it != vals.end(); ++it) {
|
|
if ((*it)->Type == Sketcher::Horizontal && (*it)->First == CrvId && (*it)->FirstPos == Sketcher::none){
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Impossible constraint"),
|
|
QObject::tr("The selected edge already has a horizontal constraint!"));
|
|
return;
|
|
}
|
|
if ((*it)->Type == Sketcher::Vertical && (*it)->First == CrvId && (*it)->FirstPos == Sketcher::none) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Double constraint"),
|
|
QObject::tr("The selected edge already has a vertical constraint!"));
|
|
return;
|
|
}
|
|
// check if the edge already has a Block constraint
|
|
if ((*it)->Type == Sketcher::Block && (*it)->First == CrvId && (*it)->FirstPos == Sketcher::none) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Impossible constraint"),
|
|
QObject::tr("The selected edge already has a Block constraint!"));
|
|
return;
|
|
}
|
|
}
|
|
|
|
// undo command open
|
|
Gui::Command::openCommand("add vertical constraint");
|
|
// issue the actual commands to create the constraint
|
|
Gui::cmdAppObjectArgs(sketchgui->getObject(), "addConstraint(Sketcher.Constraint('Vertical',%d)) ", CrvId);
|
|
// finish the transaction and update
|
|
Gui::Command::commitCommand();
|
|
tryAutoRecompute(Obj);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// ======================================================================================
|
|
|
|
/* XPM */
|
|
static const char *cursor_createlock[]={
|
|
"32 32 3 1",
|
|
"+ c white",
|
|
"# c red",
|
|
". c None",
|
|
"......+.........................",
|
|
"......+.........................",
|
|
"......+.........................",
|
|
"......+.........................",
|
|
"......+.........................",
|
|
"................................",
|
|
"+++++...+++++...................",
|
|
"................................",
|
|
"......+.........................",
|
|
"......+.........................",
|
|
"......+.........................",
|
|
"......+.........................",
|
|
"......+..........###............",
|
|
"................######..........",
|
|
"...............##....##.........",
|
|
"..............##......##........",
|
|
"..............##......##........",
|
|
".............############.......",
|
|
".............############.......",
|
|
".............############.......",
|
|
".............############.......",
|
|
".............############.......",
|
|
".............############.......",
|
|
".............############.......",
|
|
".............############.......",
|
|
"................................",
|
|
"................................",
|
|
"................................",
|
|
"................................",
|
|
"................................",
|
|
"................................",
|
|
"................................"};
|
|
|
|
class CmdSketcherConstrainLock : public CmdSketcherConstraint
|
|
{
|
|
public:
|
|
CmdSketcherConstrainLock();
|
|
virtual ~CmdSketcherConstrainLock(){}
|
|
virtual void updateAction(int mode);
|
|
virtual const char* className() const
|
|
{ return "CmdSketcherConstrainLock"; }
|
|
protected:
|
|
virtual void activated(int iMsg);
|
|
virtual void applyConstraint(std::vector<SelIdPair> &selSeq, int seqIndex);
|
|
};
|
|
|
|
CmdSketcherConstrainLock::CmdSketcherConstrainLock()
|
|
:CmdSketcherConstraint("Sketcher_ConstrainLock")
|
|
{
|
|
sAppModule = "Sketcher";
|
|
sGroup = QT_TR_NOOP("Sketcher");
|
|
sMenuText = QT_TR_NOOP("Constrain lock");
|
|
sToolTipText = QT_TR_NOOP("Create a lock constraint on the selected item");
|
|
sWhatsThis = "Sketcher_ConstrainLock";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "Sketcher_ConstrainLock";
|
|
eType = ForEdit;
|
|
|
|
allowedSelSequences = {{SelVertex}};
|
|
constraintCursor = cursor_createlock;
|
|
}
|
|
|
|
void CmdSketcherConstrainLock::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
|
|
// get the selection
|
|
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
|
|
|
|
// only one sketch with its subelements are allowed to be selected
|
|
if (selection.size() != 1 || !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
|
|
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
|
bool constraintMode = hGrp->GetBool("ContinuousConstraintMode", true);
|
|
|
|
if (constraintMode) {
|
|
ActivateHandler(getActiveGuiDocument(),
|
|
new DrawSketchHandlerGenConstraint(constraintCursor, this));
|
|
getSelection().clearSelection();
|
|
} else {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select vertices from the sketch."));
|
|
}
|
|
return;
|
|
}
|
|
|
|
// get the needed lists and objects
|
|
const std::vector<std::string> &SubNames = selection[0].getSubNames();
|
|
Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
|
|
|
|
std::vector<int> GeoId;
|
|
std::vector<Sketcher::PointPos> PosId;
|
|
|
|
for (std::vector<std::string>::const_iterator it = SubNames.begin(); it != SubNames.end(); ++it) {
|
|
int GeoIdt;
|
|
Sketcher::PointPos PosIdt;
|
|
getIdsFromName((*it), Obj, GeoIdt, PosIdt);
|
|
GeoId.push_back(GeoIdt);
|
|
PosId.push_back(PosIdt);
|
|
|
|
if ((it != std::prev(SubNames.end()) && (isEdge(GeoIdt,PosIdt) || (GeoIdt < 0 && GeoIdt >= Sketcher::GeoEnum::VAxis))) ||
|
|
(it == std::prev(SubNames.end()) && isEdge(GeoIdt,PosIdt)) ) {
|
|
if(selection.size() == 1) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select one vertex from the sketch other than the origin."));
|
|
}
|
|
else {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select only vertices from the sketch. The last selected vertex may be the origin."));
|
|
}
|
|
// clear the selection (convenience)
|
|
getSelection().clearSelection();
|
|
return;
|
|
}
|
|
}
|
|
|
|
int lastconstraintindex = Obj->Constraints.getSize()-1;
|
|
|
|
if( GeoId.size() == 1 ) { // absolute mode
|
|
// check if the edge already has a Block constraint
|
|
bool edgeisblocked = false;
|
|
|
|
if ( isPointOrSegmentFixed(Obj, GeoId[0])) {
|
|
edgeisblocked = true;
|
|
}
|
|
|
|
Base::Vector3d pnt = Obj->getPoint(GeoId[0],PosId[0]);
|
|
|
|
// undo command open
|
|
openCommand("add fixed constraint");
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('DistanceX',%d,%d,%f)) ",
|
|
GeoId[0],PosId[0],pnt.x);
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('DistanceY',%d,%d,%f)) ",
|
|
GeoId[0],PosId[0],pnt.y);
|
|
|
|
lastconstraintindex+=2;
|
|
|
|
if (edgeisblocked || GeoId[0] <= Sketcher::GeoEnum::RefExt || isConstructionPoint(Obj,GeoId[0]) || constraintCreationMode==Reference) {
|
|
// it is a constraint on a external line, make it non-driving
|
|
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "setDriving(%i,%s)",
|
|
lastconstraintindex-2,"False");
|
|
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "setDriving(%i,%s)",
|
|
lastconstraintindex,"False");
|
|
}
|
|
}
|
|
else {
|
|
std::vector<int>::const_iterator itg;
|
|
std::vector<Sketcher::PointPos>::const_iterator itp;
|
|
|
|
Base::Vector3d pntr = Obj->getPoint(GeoId.back(),PosId.back());
|
|
|
|
// check if the edge already has a Block constraint
|
|
bool refpointfixed = false;
|
|
|
|
if ( isPointOrSegmentFixed(Obj, GeoId.back()))
|
|
refpointfixed = true;
|
|
|
|
for (itg = GeoId.begin(), itp = PosId.begin(); itg != std::prev(GeoId.end()) && itp != std::prev(PosId.end()); ++itp, ++itg) {
|
|
bool pointfixed = false;
|
|
|
|
if ( isPointOrSegmentFixed(Obj, *itg))
|
|
pointfixed = true;
|
|
|
|
Base::Vector3d pnt = Obj->getPoint(*itg,*itp);
|
|
|
|
// undo command open
|
|
openCommand("add relative lock constraint");
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('DistanceX',%d,%d,%d,%d,%f)) ",
|
|
*itg,*itp,GeoId.back(),PosId.back(),pntr.x-pnt.x);
|
|
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('DistanceY',%d,%d,%d,%d,%f)) ",
|
|
*itg,*itp,GeoId.back(),PosId.back(),pntr.y-pnt.y);
|
|
lastconstraintindex+=2;
|
|
|
|
if ( (refpointfixed && pointfixed) || constraintCreationMode==Reference) {
|
|
// it is a constraint on a external line, make it non-driving
|
|
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "setDriving(%i,%s)",
|
|
lastconstraintindex-1,"False");
|
|
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "setDriving(%i,%s)",
|
|
lastconstraintindex,"False");
|
|
}
|
|
}
|
|
}
|
|
|
|
// finish the transaction and update
|
|
commitCommand();
|
|
tryAutoRecompute(Obj);
|
|
|
|
// clear the selection (convenience)
|
|
getSelection().clearSelection();
|
|
}
|
|
|
|
void CmdSketcherConstrainLock::applyConstraint(std::vector<SelIdPair> &selSeq, int seqIndex)
|
|
{
|
|
switch (seqIndex) {
|
|
case 0: // {Vertex}
|
|
// Create the constraints
|
|
SketcherGui::ViewProviderSketch* sketchgui = static_cast<SketcherGui::ViewProviderSketch*>(getActiveGuiDocument()->getInEdit());
|
|
Sketcher::SketchObject* Obj = sketchgui->getSketchObject();
|
|
|
|
// check if the edge already has a Block constraint
|
|
bool pointfixed = false;
|
|
|
|
if ( isPointOrSegmentFixed(Obj, selSeq.front().GeoId))
|
|
pointfixed = true;
|
|
|
|
Base::Vector3d pnt = Obj->getPoint(selSeq.front().GeoId, selSeq.front().PosId);
|
|
|
|
// undo command open
|
|
Gui::Command::openCommand("add fixed constraint");
|
|
Gui::cmdAppObjectArgs(sketchgui->getObject(), "addConstraint(Sketcher.Constraint('DistanceX', %d, %d, %f)) ",
|
|
selSeq.front().GeoId, selSeq.front().PosId, pnt.x);
|
|
Gui::cmdAppObjectArgs(sketchgui->getObject(), "addConstraint(Sketcher.Constraint('DistanceY', %d, %d, %f)) ",
|
|
selSeq.front().GeoId, selSeq.front().PosId, pnt.y);
|
|
|
|
if (pointfixed || constraintCreationMode==Reference) {
|
|
// it is a constraint on a external line, make it non-driving
|
|
const std::vector<Sketcher::Constraint *> &ConStr = Obj->Constraints.getValues();
|
|
|
|
Gui::cmdAppObjectArgs(sketchgui->getObject(), "setDriving(%i, %s)", ConStr.size()-2, "False");
|
|
|
|
Gui::cmdAppObjectArgs(sketchgui->getObject(), "setDriving(%i, %s)", ConStr.size()-1, "False");
|
|
}
|
|
|
|
// finish the transaction and update
|
|
Gui::Command::commitCommand();
|
|
|
|
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
|
bool autoRecompute = hGrp->GetBool("AutoRecompute", false);
|
|
|
|
if(autoRecompute)
|
|
Gui::Command::updateActive();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CmdSketcherConstrainLock::updateAction(int mode)
|
|
{
|
|
switch (mode) {
|
|
case Reference:
|
|
if (getAction())
|
|
getAction()->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_ConstrainLock_Driven"));
|
|
break;
|
|
case Driving:
|
|
if (getAction())
|
|
getAction()->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_ConstrainLock"));
|
|
break;
|
|
}
|
|
}
|
|
|
|
// ======================================================================================
|
|
|
|
/* XPM */
|
|
static const char *cursor_createblock[]={
|
|
"32 32 3 1",
|
|
"+ c white",
|
|
"# c red",
|
|
". c None",
|
|
"......+.........................",
|
|
"......+.........................",
|
|
"......+.........................",
|
|
"......+.........................",
|
|
"......+.........................",
|
|
"................................",
|
|
"+++++...+++++...................",
|
|
"................................",
|
|
"......+.........................",
|
|
"......+.........................",
|
|
"......+.........................",
|
|
"......+.........................",
|
|
"......+..........###............",
|
|
"....................##..........",
|
|
".....................##.........",
|
|
"......................##........",
|
|
"......................##........",
|
|
".............############.......",
|
|
".............###########........",
|
|
".............##########.........",
|
|
".............########...........",
|
|
".............#######............",
|
|
".............########...........",
|
|
".............##########.........",
|
|
".............###########........",
|
|
".............############.......",
|
|
"......................##........",
|
|
"......................##........",
|
|
".....................##.........",
|
|
"....................##..........",
|
|
".................###............",
|
|
"................................"};
|
|
|
|
class CmdSketcherConstrainBlock : public CmdSketcherConstraint
|
|
{
|
|
public:
|
|
CmdSketcherConstrainBlock();
|
|
virtual ~CmdSketcherConstrainBlock(){}
|
|
virtual const char* className() const
|
|
{ return "CmdSketcherConstrainBlock"; }
|
|
protected:
|
|
virtual void activated(int iMsg);
|
|
virtual void applyConstraint(std::vector<SelIdPair> &selSeq, int seqIndex);
|
|
};
|
|
|
|
CmdSketcherConstrainBlock::CmdSketcherConstrainBlock()
|
|
:CmdSketcherConstraint("Sketcher_ConstrainBlock")
|
|
{
|
|
sAppModule = "Sketcher";
|
|
sGroup = QT_TR_NOOP("Sketcher");
|
|
sMenuText = QT_TR_NOOP("Constrain Block");
|
|
sToolTipText = QT_TR_NOOP("Create a Block constraint on the selected item");
|
|
sWhatsThis = "Sketcher_ConstrainBlock";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "Sketcher_ConstrainBlock";
|
|
eType = ForEdit;
|
|
|
|
allowedSelSequences = {{SelEdge}};
|
|
constraintCursor = cursor_createblock;
|
|
}
|
|
|
|
void CmdSketcherConstrainBlock::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
|
|
// get the selection
|
|
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
|
|
|
|
// only one sketch with its subelements are allowed to be selected
|
|
if (selection.size() != 1 || !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
|
|
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
|
bool constraintMode = hGrp->GetBool("ContinuousConstraintMode", true);
|
|
|
|
if (constraintMode) {
|
|
ActivateHandler(getActiveGuiDocument(),
|
|
new DrawSketchHandlerGenConstraint(constraintCursor, this));
|
|
getSelection().clearSelection();
|
|
} else {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select vertices from the sketch."));
|
|
}
|
|
return;
|
|
}
|
|
|
|
// get the needed lists and objects
|
|
const std::vector<std::string> &SubNames = selection[0].getSubNames();
|
|
Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
|
|
|
|
// Check that the solver does not report redundant/conflicting constraints
|
|
if(Obj->getLastSolverStatus()!=GCS::Success || Obj->getLastHasConflicts() || Obj->getLastHasRedundancies()) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong solver status"),
|
|
QObject::tr("A Block constraint cannot be added if the sketch is unsolved or there are redundant and/or conflicting constraints."));
|
|
return;
|
|
}
|
|
|
|
std::vector<int> GeoId;
|
|
const std::vector< Sketcher::Constraint * > &vals = Obj->Constraints.getValues();
|
|
|
|
for (std::vector<std::string>::const_iterator it = SubNames.begin(); it != SubNames.end(); ++it) {
|
|
int GeoIdt;
|
|
Sketcher::PointPos PosIdt;
|
|
getIdsFromName((*it), Obj, GeoIdt, PosIdt);
|
|
|
|
if ( isVertex(GeoIdt,PosIdt) || GeoIdt < 0 ) {
|
|
if(selection.size() == 1) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select one edge from the sketch."));
|
|
}
|
|
else {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select only edges from the sketch."));
|
|
}
|
|
// clear the selection
|
|
getSelection().clearSelection();
|
|
return;
|
|
}
|
|
|
|
// check if the edge already has a Block constraint
|
|
if ( checkConstraint(vals, Sketcher::Block, GeoIdt, Sketcher::none)) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Double constraint"),
|
|
QObject::tr("The selected edge already has a Block constraint!"));
|
|
return;
|
|
}
|
|
|
|
GeoId.push_back(GeoIdt);
|
|
}
|
|
|
|
for (std::vector<int>::iterator itg = GeoId.begin(); itg != GeoId.end(); ++itg) {
|
|
// undo command open
|
|
openCommand("add block constraint");
|
|
|
|
try {
|
|
|
|
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Block',%d)) ", (*itg));
|
|
|
|
} catch (const Base::Exception& e) {
|
|
Base::Console().Error("%s\n", e.what());
|
|
QMessageBox::warning(Gui::getMainWindow(),
|
|
QObject::tr("Error"),
|
|
QString::fromLatin1(e.what()));
|
|
|
|
Gui::Command::abortCommand();
|
|
|
|
tryAutoRecompute(Obj);
|
|
return;
|
|
}
|
|
|
|
commitCommand();
|
|
tryAutoRecompute(Obj);
|
|
}
|
|
|
|
// clear the selection (convenience)
|
|
getSelection().clearSelection();
|
|
}
|
|
|
|
void CmdSketcherConstrainBlock::applyConstraint(std::vector<SelIdPair> &selSeq, int seqIndex)
|
|
{
|
|
switch (seqIndex) {
|
|
case 0: // {Edge}
|
|
{
|
|
// Create the constraints
|
|
SketcherGui::ViewProviderSketch* sketchgui = static_cast<SketcherGui::ViewProviderSketch*>(getActiveGuiDocument()->getInEdit());
|
|
|
|
// check if the edge already has a Block constraint
|
|
const std::vector< Sketcher::Constraint * > &vals = static_cast<Sketcher::SketchObject *>(sketchgui->getObject())->Constraints.getValues();
|
|
|
|
if ( checkConstraint(vals, Sketcher::Block, selSeq.front().GeoId, Sketcher::none)) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Double constraint"),
|
|
QObject::tr("The selected edge already has a Block constraint!"));
|
|
return;
|
|
}
|
|
|
|
// undo command open
|
|
openCommand("add block constraint");
|
|
|
|
try {
|
|
|
|
Gui::cmdAppObjectArgs(sketchgui->getObject(), "addConstraint(Sketcher.Constraint('Block',%d)) ",
|
|
selSeq.front().GeoId);
|
|
|
|
} catch (const Base::Exception& e) {
|
|
Base::Console().Error("%s\n", e.what());
|
|
QMessageBox::warning(Gui::getMainWindow(),
|
|
QObject::tr("Error"),
|
|
QString::fromLatin1(e.what()));
|
|
|
|
Gui::Command::abortCommand();
|
|
|
|
tryAutoRecompute(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
|
|
return;
|
|
}
|
|
|
|
commitCommand();
|
|
tryAutoRecompute(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
// ======================================================================================
|
|
|
|
/* XPM */
|
|
static const char *cursor_createcoincident[]={
|
|
"32 32 3 1",
|
|
"+ c white",
|
|
"# c red",
|
|
". c None",
|
|
"......+.........................",
|
|
"......+.........................",
|
|
"......+.........................",
|
|
"......+.........................",
|
|
"......+.........................",
|
|
"................................",
|
|
"+++++...+++++...................",
|
|
"................................",
|
|
"......+.........................",
|
|
"......+.........................",
|
|
"......+.........................",
|
|
"......+.........................",
|
|
"......+.........................",
|
|
"................................",
|
|
"................................",
|
|
"................................",
|
|
".................####...........",
|
|
"................######..........",
|
|
"...............########.........",
|
|
"...............########.........",
|
|
"...............########.........",
|
|
"...............########.........",
|
|
"................######..........",
|
|
".................####...........",
|
|
"................................",
|
|
"................................",
|
|
"................................",
|
|
"................................",
|
|
"................................",
|
|
"................................",
|
|
"................................",
|
|
"................................"};
|
|
|
|
class DrawSketchHandlerCoincident: public DrawSketchHandler
|
|
{
|
|
public:
|
|
DrawSketchHandlerCoincident()
|
|
{
|
|
GeoId1 = GeoId2 = Constraint::GeoUndef;
|
|
PosId1 = PosId2 = Sketcher::none;
|
|
}
|
|
virtual ~DrawSketchHandlerCoincident()
|
|
{
|
|
Gui::Selection().rmvSelectionGate();
|
|
}
|
|
|
|
virtual void activated(ViewProviderSketch *)
|
|
{
|
|
Gui::Selection().rmvSelectionGate();
|
|
GenericConstraintSelection* selFilterGate = new GenericConstraintSelection(sketchgui->getObject());
|
|
selFilterGate->setAllowedSelTypes(SelVertex|SelRoot);
|
|
Gui::Selection().addSelectionGate(selFilterGate);
|
|
setCursor(QPixmap(cursor_createcoincident), 7, 7);
|
|
}
|
|
|
|
virtual void mouseMove(Base::Vector2d onSketchPos) {Q_UNUSED(onSketchPos);}
|
|
|
|
virtual bool pressButton(Base::Vector2d onSketchPos)
|
|
{
|
|
Q_UNUSED(onSketchPos);
|
|
return true;
|
|
}
|
|
|
|
virtual bool releaseButton(Base::Vector2d onSketchPos)
|
|
{
|
|
int VtId = sketchgui->getPreselectPoint();
|
|
int CrsId = sketchgui->getPreselectCross();
|
|
std::stringstream ss;
|
|
int GeoId_temp;
|
|
Sketcher::PointPos PosId_temp;
|
|
|
|
if (VtId != -1) {
|
|
sketchgui->getSketchObject()->getGeoVertexIndex(VtId,GeoId_temp,PosId_temp);
|
|
ss << "Vertex" << VtId + 1;
|
|
}
|
|
else if (CrsId == 0){
|
|
GeoId_temp = Sketcher::GeoEnum::RtPnt;
|
|
PosId_temp = Sketcher::start;
|
|
ss << "RootPoint";
|
|
}
|
|
else {
|
|
GeoId1 = GeoId2 = Constraint::GeoUndef;
|
|
PosId1 = PosId2 = Sketcher::none;
|
|
Gui::Selection().clearSelection();
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
if (GeoId1 == Constraint::GeoUndef) {
|
|
GeoId1 = GeoId_temp;
|
|
PosId1 = PosId_temp;
|
|
Gui::Selection().addSelection(sketchgui->getSketchObject()->getDocument()->getName(),
|
|
sketchgui->getSketchObject()->getNameInDocument(),
|
|
ss.str().c_str(),
|
|
onSketchPos.x,
|
|
onSketchPos.y,
|
|
0.f);
|
|
}
|
|
else {
|
|
GeoId2 = GeoId_temp;
|
|
PosId2 = PosId_temp;
|
|
|
|
// Apply the constraint
|
|
Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(sketchgui->getObject());
|
|
|
|
// undo command open
|
|
Gui::Command::openCommand("add coincident constraint");
|
|
|
|
// check if this coincidence is already enforced (even indirectly)
|
|
bool constraintExists = Obj->arePointsCoincident(GeoId1,PosId1,GeoId2,PosId2);
|
|
if (!constraintExists && (GeoId1 != GeoId2)) {
|
|
Gui::cmdAppObjectArgs(sketchgui->getObject(), "addConstraint(Sketcher.Constraint('Coincident',%d,%d,%d,%d)) ",
|
|
GeoId1,PosId1,GeoId2,PosId2);
|
|
Gui::Command::commitCommand();
|
|
}
|
|
else {
|
|
Gui::Command::abortCommand();
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
protected:
|
|
int GeoId1, GeoId2;
|
|
Sketcher::PointPos PosId1, PosId2;
|
|
};
|
|
|
|
class CmdSketcherConstrainCoincident : public CmdSketcherConstraint
|
|
{
|
|
public:
|
|
CmdSketcherConstrainCoincident();
|
|
virtual ~CmdSketcherConstrainCoincident(){}
|
|
virtual const char* className() const
|
|
{ return "CmdSketcherConstrainCoincident"; }
|
|
protected:
|
|
virtual void activated(int iMsg);
|
|
virtual void applyConstraint(std::vector<SelIdPair> &selSeq, int seqIndex);
|
|
};
|
|
|
|
CmdSketcherConstrainCoincident::CmdSketcherConstrainCoincident()
|
|
:CmdSketcherConstraint("Sketcher_ConstrainCoincident")
|
|
{
|
|
sAppModule = "Sketcher";
|
|
sGroup = QT_TR_NOOP("Sketcher");
|
|
sMenuText = QT_TR_NOOP("Constrain coincident");
|
|
sToolTipText = QT_TR_NOOP("Create a coincident constraint on the selected item");
|
|
sWhatsThis = "Sketcher_ConstrainCoincident";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "Constraint_PointOnPoint";
|
|
sAccel = "C";
|
|
eType = ForEdit;
|
|
|
|
allowedSelSequences = {{SelVertex, SelVertexOrRoot}, {SelRoot, SelVertex}};
|
|
constraintCursor = cursor_createcoincident;
|
|
}
|
|
|
|
void CmdSketcherConstrainCoincident::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
|
|
// get the selection
|
|
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
|
|
|
|
// only one sketch with its subelements are allowed to be selected
|
|
if (selection.size() != 1 || !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
|
|
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
|
bool constraintMode = hGrp->GetBool("ContinuousConstraintMode", true);
|
|
|
|
if (constraintMode) {
|
|
ActivateHandler(getActiveGuiDocument(),
|
|
new DrawSketchHandlerGenConstraint(constraintCursor, this));
|
|
getSelection().clearSelection();
|
|
} else {
|
|
// TODO: Get the exact message from git history and put it here
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select two or more points from the sketch."));
|
|
}
|
|
return;
|
|
}
|
|
|
|
// get the needed lists and objects
|
|
const std::vector<std::string> &SubNames = selection[0].getSubNames();
|
|
Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
|
|
|
|
if (SubNames.size() < 2) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select two or more vertexes from the sketch."));
|
|
return;
|
|
}
|
|
|
|
for (std::vector<std::string>::const_iterator it = SubNames.begin(); it != SubNames.end(); ++it) {
|
|
int GeoId;
|
|
Sketcher::PointPos PosId;
|
|
getIdsFromName(*it, Obj, GeoId, PosId);
|
|
if (isEdge(GeoId,PosId)) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select two or more vertexes from the sketch."));
|
|
return;
|
|
}
|
|
}
|
|
|
|
int GeoId1, GeoId2;
|
|
Sketcher::PointPos PosId1, PosId2;
|
|
getIdsFromName(SubNames[0], Obj, GeoId1, PosId1);
|
|
|
|
// undo command open
|
|
bool constraintsAdded = false;
|
|
openCommand("add coincident constraint");
|
|
for (std::size_t i=1; i<SubNames.size(); i++) {
|
|
getIdsFromName(SubNames[i], Obj, GeoId2, PosId2);
|
|
|
|
// check if the edge already has a Block constraint
|
|
if ( areBothPointsOrSegmentsFixed(Obj,GeoId1,GeoId2) ) {
|
|
showNoConstraintBetweenFixedGeometry();
|
|
return;
|
|
}
|
|
|
|
// check if this coincidence is already enforced (even indirectly)
|
|
bool constraintExists=Obj->arePointsCoincident(GeoId1,PosId1,GeoId2,PosId2);
|
|
|
|
// check for a preexisting edge-to-edge tangency
|
|
const std::vector< Constraint * > &cvals = Obj->Constraints.getValues();
|
|
|
|
int j=0;
|
|
for (std::vector<Constraint *>::const_iterator it = cvals.begin(); it != cvals.end(); ++it,++j) {
|
|
if( (*it)->Type == Sketcher::Tangent &&
|
|
(*it)->FirstPos == Sketcher::none && (*it)->SecondPos == Sketcher::none &&
|
|
(*it)->Third == Constraint::GeoUndef &&
|
|
(((*it)->First == GeoId1 && (*it)->Second == GeoId2) ||
|
|
((*it)->Second == GeoId1 && (*it)->First == GeoId2)) ) {
|
|
|
|
Gui::Command::openCommand("swap edge tangency with ptp tangency");
|
|
|
|
if(constraintExists) {
|
|
// try to remove any pre-existing direct coincident constraints
|
|
Gui::cmdAppObjectArgs(Obj, "delConstraintOnPoint(%i,%i)", GeoId1, PosId1);
|
|
}
|
|
|
|
Gui::cmdAppObjectArgs(Obj, "delConstraint(%i)", j);
|
|
|
|
doEndpointTangency(Obj, selection[0], GeoId1, GeoId2, PosId1, PosId2);
|
|
|
|
commitCommand();
|
|
tryAutoRecomputeIfNotSolve(Obj);
|
|
|
|
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher/General");
|
|
|
|
if(hGrp->GetBool("NotifyConstraintSubstitutions", true)) {
|
|
QMessageBox::information(Gui::getMainWindow(), QObject::tr("Constraint Substitution"),
|
|
QObject::tr("Endpoint to endpoint tangency was applied instead."));
|
|
}
|
|
|
|
getSelection().clearSelection();
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!constraintExists) {
|
|
constraintsAdded = true;
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('Coincident',%d,%d,%d,%d)) ",
|
|
GeoId1,PosId1,GeoId2,PosId2);
|
|
}
|
|
}
|
|
|
|
// finish or abort the transaction and update
|
|
if (constraintsAdded)
|
|
commitCommand();
|
|
else
|
|
abortCommand();
|
|
|
|
tryAutoRecompute(Obj);
|
|
|
|
// clear the selection (convenience)
|
|
getSelection().clearSelection();
|
|
}
|
|
|
|
void CmdSketcherConstrainCoincident::applyConstraint(std::vector<SelIdPair> &selSeq, int seqIndex)
|
|
{
|
|
switch (seqIndex) {
|
|
case 0: // {SelVertex, SelVertexOrRoot}
|
|
case 1: // {SelRoot, SelVertex}
|
|
// create the constraint
|
|
SketcherGui::ViewProviderSketch* sketchgui = static_cast<SketcherGui::ViewProviderSketch*>(getActiveGuiDocument()->getInEdit());
|
|
Sketcher::SketchObject* Obj = sketchgui->getSketchObject();
|
|
|
|
int GeoId1 = selSeq.at(0).GeoId, GeoId2 = selSeq.at(1).GeoId;
|
|
Sketcher::PointPos PosId1 = selSeq.at(0).PosId, PosId2 = selSeq.at(1).PosId;
|
|
|
|
// check if the edge already has a Block constraint
|
|
if ( areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2) ) {
|
|
showNoConstraintBetweenFixedGeometry();
|
|
return;
|
|
}
|
|
|
|
// undo command open
|
|
Gui::Command::openCommand("add coincident constraint");
|
|
|
|
// check if this coincidence is already enforced (even indirectly)
|
|
bool constraintExists = Obj->arePointsCoincident(GeoId1, PosId1, GeoId2, PosId2);
|
|
if (!constraintExists && (GeoId1 != GeoId2)) {
|
|
Gui::cmdAppObjectArgs(sketchgui->getObject(), "addConstraint(Sketcher.Constraint('Coincident', %d, %d, %d, %d)) ",
|
|
GeoId1, PosId1, GeoId2, PosId2);
|
|
Gui::Command::commitCommand();
|
|
}
|
|
else {
|
|
Gui::Command::abortCommand();
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// ======================================================================================
|
|
|
|
class CmdSketcherConstrainDistance : public CmdSketcherConstraint
|
|
{
|
|
public:
|
|
CmdSketcherConstrainDistance();
|
|
virtual ~CmdSketcherConstrainDistance(){}
|
|
virtual void updateAction(int mode);
|
|
virtual const char* className() const
|
|
{ return "CmdSketcherConstrainDistance"; }
|
|
protected:
|
|
virtual void activated(int iMsg);
|
|
virtual void applyConstraint(std::vector<SelIdPair> &selSeq, int seqIndex);
|
|
};
|
|
|
|
CmdSketcherConstrainDistance::CmdSketcherConstrainDistance()
|
|
:CmdSketcherConstraint("Sketcher_ConstrainDistance")
|
|
{
|
|
sAppModule = "Sketcher";
|
|
sGroup = QT_TR_NOOP("Sketcher");
|
|
sMenuText = QT_TR_NOOP("Constrain distance");
|
|
sToolTipText = QT_TR_NOOP("Fix a length of a line or the distance between a line and a vertex");
|
|
sWhatsThis = "Sketcher_ConstrainDistance";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "Constraint_Length";
|
|
sAccel = "SHIFT+D";
|
|
eType = ForEdit;
|
|
|
|
allowedSelSequences = {{SelVertex, SelVertexOrRoot}, {SelRoot, SelVertex},
|
|
{SelEdge}, {SelExternalEdge},
|
|
{SelVertex, SelEdgeOrAxis}, {SelRoot, SelEdge},
|
|
{SelVertex, SelExternalEdge}, {SelRoot, SelExternalEdge}};
|
|
constraintCursor = cursor_genericconstraint;
|
|
}
|
|
|
|
void CmdSketcherConstrainDistance::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
// get the selection
|
|
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
|
|
|
|
// only one sketch with its subelements are allowed to be selected
|
|
if (selection.size() != 1 || !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
|
|
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
|
bool constraintMode = hGrp->GetBool("ContinuousConstraintMode", true);
|
|
|
|
if (constraintMode) {
|
|
ActivateHandler(getActiveGuiDocument(),
|
|
new DrawSketchHandlerGenConstraint(constraintCursor, this));
|
|
|
|
getSelection().clearSelection();
|
|
}
|
|
else {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select vertexes from the sketch."));
|
|
}
|
|
return;
|
|
}
|
|
|
|
// get the needed lists and objects
|
|
const std::vector<std::string> &SubNames = selection[0].getSubNames();
|
|
Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
|
|
|
|
if (SubNames.size() < 1 || SubNames.size() > 2) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select exactly one line or one point and one line or two points from the sketch."));
|
|
return;
|
|
}
|
|
|
|
int GeoId1, GeoId2=Constraint::GeoUndef;
|
|
Sketcher::PointPos PosId1, PosId2=Sketcher::none;
|
|
getIdsFromName(SubNames[0], Obj, GeoId1, PosId1);
|
|
if (SubNames.size() == 2)
|
|
getIdsFromName(SubNames[1], Obj, GeoId2, PosId2);
|
|
|
|
bool arebothpointsorsegmentsfixed=areBothPointsOrSegmentsFixed(Obj,GeoId1, GeoId2);
|
|
|
|
if (isVertex(GeoId1,PosId1) && (GeoId2 == Sketcher::GeoEnum::VAxis || GeoId2 == Sketcher::GeoEnum::HAxis)) {
|
|
std::swap(GeoId1,GeoId2);
|
|
std::swap(PosId1,PosId2);
|
|
}
|
|
|
|
if ((isVertex(GeoId1,PosId1) || GeoId1 == Sketcher::GeoEnum::VAxis || GeoId1 == Sketcher::GeoEnum::HAxis) &&
|
|
isVertex(GeoId2,PosId2)) { // point to point distance
|
|
|
|
Base::Vector3d pnt2 = Obj->getPoint(GeoId2,PosId2);
|
|
|
|
if (GeoId1 == Sketcher::GeoEnum::HAxis && PosId1 == Sketcher::none) {
|
|
PosId1 = Sketcher::start;
|
|
|
|
openCommand("add distance from horizontal axis constraint");
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),
|
|
"addConstraint(Sketcher.Constraint('DistanceY',%d,%d,%d,%d,%f)) ",
|
|
GeoId1,PosId1,GeoId2,PosId2,pnt2.y);
|
|
}
|
|
else if (GeoId1 == Sketcher::GeoEnum::VAxis && PosId1 == Sketcher::none) {
|
|
PosId1 = Sketcher::start;
|
|
|
|
openCommand("add distance from vertical axis constraint");
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),
|
|
"addConstraint(Sketcher.Constraint('DistanceX',%d,%d,%d,%d,%f)) ",
|
|
GeoId1,PosId1,GeoId2,PosId2,pnt2.x);
|
|
}
|
|
else {
|
|
Base::Vector3d pnt1 = Obj->getPoint(GeoId1,PosId1);
|
|
|
|
openCommand("add point to point distance constraint");
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),
|
|
"addConstraint(Sketcher.Constraint('Distance',%d,%d,%d,%d,%f)) ",
|
|
GeoId1,PosId1,GeoId2,PosId2,(pnt2-pnt1).Length());
|
|
}
|
|
|
|
if (arebothpointsorsegmentsfixed || constraintCreationMode==Reference) { // it is a constraint on a external line, make it non-driving
|
|
const std::vector<Sketcher::Constraint *> &ConStr = Obj->Constraints.getValues();
|
|
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),
|
|
"setDriving(%i,%s)",
|
|
ConStr.size()-1,"False");
|
|
finishDistanceConstraint(this, Obj,false);
|
|
}
|
|
else
|
|
finishDistanceConstraint(this, Obj,true);
|
|
return;
|
|
}
|
|
else if ((isVertex(GeoId1,PosId1) && isEdge(GeoId2,PosId2)) ||
|
|
(isEdge(GeoId1,PosId1) && isVertex(GeoId2,PosId2))) { // point to line distance
|
|
if (isVertex(GeoId2,PosId2)) {
|
|
std::swap(GeoId1,GeoId2);
|
|
std::swap(PosId1,PosId2);
|
|
}
|
|
Base::Vector3d pnt = Obj->getPoint(GeoId1,PosId1);
|
|
const Part::Geometry *geom = Obj->getGeometry(GeoId2);
|
|
if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
|
|
const Part::GeomLineSegment *lineSeg;
|
|
lineSeg = static_cast<const Part::GeomLineSegment*>(geom);
|
|
Base::Vector3d pnt1 = lineSeg->getStartPoint();
|
|
Base::Vector3d pnt2 = lineSeg->getEndPoint();
|
|
Base::Vector3d d = pnt2-pnt1;
|
|
double ActDist = std::abs(-pnt.x*d.y+pnt.y*d.x+pnt1.x*pnt2.y-pnt2.x*pnt1.y) / d.Length();
|
|
|
|
openCommand("add point to line Distance constraint");
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),
|
|
"addConstraint(Sketcher.Constraint('Distance',%d,%d,%d,%f)) ",
|
|
GeoId1,PosId1,GeoId2,ActDist);
|
|
|
|
if (arebothpointsorsegmentsfixed || constraintCreationMode==Reference) { // it is a constraint on a external line, make it non-driving
|
|
const std::vector<Sketcher::Constraint *> &ConStr = Obj->Constraints.getValues();
|
|
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),
|
|
"setDriving(%i,%s)",
|
|
ConStr.size()-1,"False");
|
|
finishDistanceConstraint(this, Obj,false);
|
|
}
|
|
else
|
|
finishDistanceConstraint(this, Obj,true);
|
|
|
|
return;
|
|
}
|
|
}
|
|
else if (isEdge(GeoId1,PosId1)) { // line length
|
|
if (GeoId1 < 0 && GeoId1 >= Sketcher::GeoEnum::VAxis) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Cannot add a length constraint on an axis!"));
|
|
return;
|
|
}
|
|
|
|
arebothpointsorsegmentsfixed=isPointOrSegmentFixed(Obj,GeoId1);
|
|
|
|
const Part::Geometry *geom = Obj->getGeometry(GeoId1);
|
|
if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
|
|
const Part::GeomLineSegment *lineSeg;
|
|
lineSeg = static_cast<const Part::GeomLineSegment*>(geom);
|
|
double ActLength = (lineSeg->getEndPoint()-lineSeg->getStartPoint()).Length();
|
|
|
|
openCommand("add length constraint");
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('Distance',%d,%f)) ",
|
|
GeoId1,ActLength);
|
|
|
|
// it is a constraint on a external line, make it non-driving
|
|
if (arebothpointsorsegmentsfixed || GeoId1 <= Sketcher::GeoEnum::RefExt ||
|
|
isConstructionPoint(Obj,GeoId1) || constraintCreationMode==Reference) {
|
|
const std::vector<Sketcher::Constraint *> &ConStr = Obj->Constraints.getValues();
|
|
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "setDriving(%i,%s)",
|
|
ConStr.size()-1,"False");
|
|
finishDistanceConstraint(this, Obj,false);
|
|
}
|
|
else
|
|
finishDistanceConstraint(this, Obj,true);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select exactly one line or one point and one line or two points from the sketch."));
|
|
return;
|
|
}
|
|
|
|
void CmdSketcherConstrainDistance::applyConstraint(std::vector<SelIdPair> &selSeq, int seqIndex)
|
|
{
|
|
SketcherGui::ViewProviderSketch* sketchgui = static_cast<SketcherGui::ViewProviderSketch*>(getActiveGuiDocument()->getInEdit());
|
|
Sketcher::SketchObject* Obj = sketchgui->getSketchObject();
|
|
|
|
int GeoId1 = Constraint::GeoUndef, GeoId2 = Constraint::GeoUndef;
|
|
Sketcher::PointPos PosId1 = Sketcher::none, PosId2 = Sketcher::none;
|
|
|
|
bool arebothpointsorsegmentsfixed=areBothPointsOrSegmentsFixed(Obj,GeoId1, GeoId2);
|
|
|
|
switch (seqIndex) {
|
|
case 0: // {SelVertex, SelVertexOrRoot}
|
|
case 1: // {SelRoot, SelVertex}
|
|
{
|
|
GeoId1 = selSeq.at(0).GeoId; GeoId2 = selSeq.at(1).GeoId;
|
|
PosId1 = selSeq.at(0).PosId; PosId2 = selSeq.at(1).PosId;
|
|
|
|
Base::Vector3d pnt2 = Obj->getPoint(GeoId2,PosId2);
|
|
|
|
if (GeoId1 == Sketcher::GeoEnum::HAxis && PosId1 == Sketcher::none) {
|
|
PosId1 = Sketcher::start;
|
|
|
|
openCommand("add distance from horizontal axis constraint");
|
|
Gui::cmdAppObjectArgs(Obj,"addConstraint(Sketcher.Constraint('DistanceY',%d,%d,%d,%d,%f)) ",
|
|
GeoId1,PosId1,GeoId2,PosId2,pnt2.y);
|
|
}
|
|
else if (GeoId1 == Sketcher::GeoEnum::VAxis && PosId1 == Sketcher::none) {
|
|
PosId1 = Sketcher::start;
|
|
|
|
openCommand("add distance from vertical axis constraint");
|
|
Gui::cmdAppObjectArgs(Obj,"addConstraint(Sketcher.Constraint('DistanceX',%d,%d,%d,%d,%f)) ",
|
|
GeoId1,PosId1,GeoId2,PosId2,pnt2.x);
|
|
}
|
|
else {
|
|
Base::Vector3d pnt1 = Obj->getPoint(GeoId1,PosId1);
|
|
|
|
openCommand("add point to point distance constraint");
|
|
Gui::cmdAppObjectArgs(Obj,"addConstraint(Sketcher.Constraint('Distance',%d,%d,%d,%d,%f)) ",
|
|
GeoId1,PosId1,GeoId2,PosId2,(pnt2-pnt1).Length());
|
|
}
|
|
|
|
if (arebothpointsorsegmentsfixed || constraintCreationMode==Reference) { // it is a constraint on a external line, make it non-driving
|
|
const std::vector<Sketcher::Constraint *> &ConStr = Obj->Constraints.getValues();
|
|
|
|
Gui::cmdAppObjectArgs(Obj,"setDriving(%i,%s)",
|
|
ConStr.size()-1,"False");
|
|
finishDistanceConstraint(this, Obj,false);
|
|
}
|
|
else
|
|
finishDistanceConstraint(this, Obj,true);
|
|
|
|
return;
|
|
}
|
|
case 2: // {SelEdge}
|
|
case 3: // {SelExternalEdge}
|
|
{
|
|
GeoId1 = GeoId2 = selSeq.at(0).GeoId;
|
|
PosId1 = Sketcher::start; PosId2 = Sketcher::end;
|
|
|
|
arebothpointsorsegmentsfixed=isPointOrSegmentFixed(Obj,GeoId1);
|
|
|
|
const Part::Geometry *geom = Obj->getGeometry(GeoId1);
|
|
if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
|
|
const Part::GeomLineSegment *lineSeg;
|
|
lineSeg = static_cast<const Part::GeomLineSegment*>(geom);
|
|
double ActLength = (lineSeg->getEndPoint()-lineSeg->getStartPoint()).Length();
|
|
|
|
openCommand("add length constraint");
|
|
Gui::cmdAppObjectArgs(Obj,"addConstraint(Sketcher.Constraint('Distance',%d,%f)) ",
|
|
GeoId1,ActLength);
|
|
|
|
if (arebothpointsorsegmentsfixed || GeoId1 <= Sketcher::GeoEnum::RefExt || isConstructionPoint(Obj,GeoId1) || constraintCreationMode==Reference) { // it is a constraint on a external line, make it non-driving
|
|
const std::vector<Sketcher::Constraint *> &ConStr = Obj->Constraints.getValues();
|
|
|
|
Gui::cmdAppObjectArgs(Obj, "setDriving(%i,%s)",
|
|
ConStr.size()-1,"False");
|
|
finishDistanceConstraint(this, Obj,false);
|
|
}
|
|
else
|
|
finishDistanceConstraint(this, Obj,true);
|
|
}
|
|
else {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("This constraint does not make sense for non-linear curves"));
|
|
}
|
|
|
|
return;
|
|
}
|
|
case 4: // {SelVertex, SelEdgeOrAxis}
|
|
case 5: // {SelRoot, SelEdge}
|
|
case 6: // {SelVertex, SelExternalEdge}
|
|
case 7: // {SelRoot, SelExternalEdge}
|
|
{
|
|
GeoId1 = selSeq.at(0).GeoId; GeoId2 = selSeq.at(1).GeoId;
|
|
PosId1 = selSeq.at(0).PosId; PosId2 = selSeq.at(1).PosId;
|
|
|
|
Base::Vector3d pnt = Obj->getPoint(GeoId1,PosId1);
|
|
const Part::Geometry *geom = Obj->getGeometry(GeoId2);
|
|
if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
|
|
const Part::GeomLineSegment *lineSeg;
|
|
lineSeg = static_cast<const Part::GeomLineSegment*>(geom);
|
|
Base::Vector3d pnt1 = lineSeg->getStartPoint();
|
|
Base::Vector3d pnt2 = lineSeg->getEndPoint();
|
|
Base::Vector3d d = pnt2-pnt1;
|
|
double ActDist = std::abs(-pnt.x*d.y+pnt.y*d.x+pnt1.x*pnt2.y-pnt2.x*pnt1.y) / d.Length();
|
|
|
|
openCommand("add point to line Distance constraint");
|
|
Gui::cmdAppObjectArgs(Obj,"addConstraint(Sketcher.Constraint('Distance',%d,%d,%d,%f)) ",
|
|
GeoId1,PosId1,GeoId2,ActDist);
|
|
|
|
if (arebothpointsorsegmentsfixed || constraintCreationMode==Reference) { // it is a constraint on a external line, make it non-driving
|
|
const std::vector<Sketcher::Constraint *> &ConStr = Obj->Constraints.getValues();
|
|
|
|
Gui::cmdAppObjectArgs(Obj,"setDriving(%i,%s)",
|
|
ConStr.size()-1,"False");
|
|
finishDistanceConstraint(this, Obj,false);
|
|
}
|
|
else
|
|
finishDistanceConstraint(this, Obj,true);
|
|
}
|
|
|
|
return;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CmdSketcherConstrainDistance::updateAction(int mode)
|
|
{
|
|
switch (mode) {
|
|
case Reference:
|
|
if (getAction())
|
|
getAction()->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Length_Driven"));
|
|
break;
|
|
case Driving:
|
|
if (getAction())
|
|
getAction()->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Length"));
|
|
break;
|
|
}
|
|
}
|
|
|
|
// ======================================================================================
|
|
|
|
/* XPM */
|
|
static const char * cursor_createpointonobj[] = {
|
|
"32 32 3 1",
|
|
" c None",
|
|
". c #FFFFFF",
|
|
"+ c #FF0000",
|
|
" . ",
|
|
" . ++++",
|
|
" . ++++++",
|
|
" . +++++++ ",
|
|
" . ++++++ ",
|
|
" ++++++ ",
|
|
"..... ..... +++++ ",
|
|
" +++++ ",
|
|
" . +++ ++++ ",
|
|
" . +++++++++ ",
|
|
" . ++++++++ ",
|
|
" . +++++++++ ",
|
|
" . +++++++++ ",
|
|
" +++++++++ ",
|
|
" +++++++ ",
|
|
" ++++++++ ",
|
|
" +++++++ ",
|
|
" +++ ",
|
|
" +++ ",
|
|
" +++ ",
|
|
" +++ ",
|
|
" +++ ",
|
|
" +++ ",
|
|
" +++ ",
|
|
" +++ ",
|
|
" ++ ",
|
|
" +++ ",
|
|
" ++ ",
|
|
" +++ ",
|
|
" +++ ",
|
|
" ++ ",
|
|
" ++ "};
|
|
|
|
class CmdSketcherConstrainPointOnObject : public CmdSketcherConstraint
|
|
{
|
|
public:
|
|
CmdSketcherConstrainPointOnObject();
|
|
virtual ~CmdSketcherConstrainPointOnObject(){}
|
|
virtual const char* className() const
|
|
{ return "CmdSketcherConstrainPointOnObject"; }
|
|
protected:
|
|
virtual void activated(int iMsg);
|
|
virtual void applyConstraint(std::vector<SelIdPair> &selSeq, int seqIndex);
|
|
};
|
|
|
|
//DEF_STD_CMD_A(CmdSketcherConstrainPointOnObject);
|
|
|
|
CmdSketcherConstrainPointOnObject::CmdSketcherConstrainPointOnObject()
|
|
:CmdSketcherConstraint("Sketcher_ConstrainPointOnObject")
|
|
{
|
|
sAppModule = "Sketcher";
|
|
sGroup = QT_TR_NOOP("Sketcher");
|
|
sMenuText = QT_TR_NOOP("Constrain point onto object");
|
|
sToolTipText = QT_TR_NOOP("Fix a point onto an object");
|
|
sWhatsThis = "Sketcher_ConstrainPointOnObject";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "Constraint_PointOnObject";
|
|
sAccel = "SHIFT+O";
|
|
eType = ForEdit;
|
|
|
|
allowedSelSequences = {{SelVertex, SelEdgeOrAxis}, {SelRoot, SelEdge},
|
|
{SelVertex, SelExternalEdge},
|
|
{SelEdge, SelVertexOrRoot}, {SelEdgeOrAxis, SelVertex},
|
|
{SelExternalEdge, SelVertex}};
|
|
constraintCursor = cursor_createpointonobj;
|
|
|
|
}
|
|
|
|
void CmdSketcherConstrainPointOnObject::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
// get the selection
|
|
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
|
|
|
|
// only one sketch with its subelements are allowed to be selected
|
|
if (selection.size() != 1 || !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
|
|
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
|
bool constraintMode = hGrp->GetBool("ContinuousConstraintMode", true);
|
|
|
|
if (constraintMode) {
|
|
ActivateHandler(getActiveGuiDocument(),
|
|
new DrawSketchHandlerGenConstraint(constraintCursor, this));
|
|
getSelection().clearSelection();
|
|
} else {
|
|
// TODO: Get the exact message from git history and put it here
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select the right things from the sketch."));
|
|
}
|
|
return;
|
|
}
|
|
|
|
// get the needed lists and objects
|
|
const std::vector<std::string> &SubNames = selection[0].getSubNames();
|
|
Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
|
|
|
|
//count curves and points
|
|
std::vector<SelIdPair> points;
|
|
std::vector<SelIdPair> curves;
|
|
for (std::size_t i = 0 ; i < SubNames.size() ; i++){
|
|
SelIdPair id;
|
|
getIdsFromName(SubNames[i], Obj, id.GeoId, id.PosId);
|
|
if (isEdge(id.GeoId, id.PosId))
|
|
curves.push_back(id);
|
|
if (isVertex(id.GeoId, id.PosId))
|
|
points.push_back(id);
|
|
}
|
|
|
|
if ((points.size() == 1 && curves.size() >= 1) ||
|
|
(points.size() >= 1 && curves.size() == 1)) {
|
|
|
|
openCommand("add point on object constraint");
|
|
int cnt = 0;
|
|
for (std::size_t iPnt = 0; iPnt < points.size(); iPnt++) {
|
|
for (std::size_t iCrv = 0; iCrv < curves.size(); iCrv++) {
|
|
if (areBothPointsOrSegmentsFixed(Obj, points[iPnt].GeoId, curves[iCrv].GeoId)){
|
|
showNoConstraintBetweenFixedGeometry();
|
|
continue;
|
|
}
|
|
if (points[iPnt].GeoId == curves[iCrv].GeoId)
|
|
continue; //constraining a point of an element onto the element is a bad idea...
|
|
|
|
const Part::Geometry *geom = Obj->getGeometry(curves[iCrv].GeoId);
|
|
|
|
if( geom && geom->getTypeId() == Part::GeomBSplineCurve::getClassTypeId() ){
|
|
// unsupported until normal to B-spline at any point implemented.
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Point on B-spline edge currently unsupported."));
|
|
continue;
|
|
}
|
|
|
|
cnt++;
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
|
points[iPnt].GeoId, points[iPnt].PosId, curves[iCrv].GeoId);
|
|
}
|
|
}
|
|
if (cnt) {
|
|
commitCommand();
|
|
getSelection().clearSelection();
|
|
} else {
|
|
abortCommand();
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("None of the selected points were constrained onto the respective curves, either "
|
|
"because they are parts of the same element, or because they are both external geometry."));
|
|
}
|
|
return;
|
|
}
|
|
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select either one point and several curves, or one curve and several points. "
|
|
"You have selected %1 curves and %2 points.").arg(curves.size()).arg(points.size()));
|
|
return;
|
|
}
|
|
|
|
void CmdSketcherConstrainPointOnObject::applyConstraint(std::vector<SelIdPair> &selSeq, int seqIndex)
|
|
{
|
|
int GeoIdVt, GeoIdCrv;
|
|
Sketcher::PointPos PosIdVt;
|
|
|
|
switch (seqIndex) {
|
|
case 0: // {SelVertex, SelEdgeOrAxis}
|
|
case 1: // {SelRoot, SelEdge}
|
|
case 2: // {SelVertex, SelExternalEdge}
|
|
GeoIdVt = selSeq.at(0).GeoId; GeoIdCrv = selSeq.at(1).GeoId;
|
|
PosIdVt = selSeq.at(0).PosId;
|
|
|
|
break;
|
|
case 3: // {SelEdge, SelVertexOrRoot}
|
|
case 4: // {SelEdgeOrAxis, SelVertex}
|
|
case 5: // {SelExternalEdge, SelVertex}
|
|
GeoIdVt = selSeq.at(1).GeoId; GeoIdCrv = selSeq.at(0).GeoId;
|
|
PosIdVt = selSeq.at(1).PosId;
|
|
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
SketcherGui::ViewProviderSketch* sketchgui = static_cast<SketcherGui::ViewProviderSketch*>(getActiveGuiDocument()->getInEdit());
|
|
Sketcher::SketchObject* Obj = sketchgui->getSketchObject();
|
|
|
|
openCommand("add point on object constraint");
|
|
bool allOK = true;
|
|
|
|
if (areBothPointsOrSegmentsFixed(Obj, GeoIdVt, GeoIdCrv)){
|
|
showNoConstraintBetweenFixedGeometry();
|
|
allOK = false;
|
|
}
|
|
if (GeoIdVt == GeoIdCrv)
|
|
allOK = false; //constraining a point of an element onto the element is a bad idea...
|
|
|
|
const Part::Geometry *geom = Obj->getGeometry(GeoIdCrv);
|
|
|
|
if( geom && geom->getTypeId() == Part::GeomBSplineCurve::getClassTypeId() ){
|
|
// unsupported until normal to B-spline at any point implemented.
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Point on B-spline edge currently unsupported."));
|
|
abortCommand();
|
|
|
|
return;
|
|
}
|
|
|
|
if (allOK) {
|
|
Gui::cmdAppObjectArgs(sketchgui->getObject(), "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
|
GeoIdVt, PosIdVt, GeoIdCrv);
|
|
|
|
commitCommand();
|
|
tryAutoRecompute(Obj);
|
|
}
|
|
else {
|
|
abortCommand();
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("None of the selected points were constrained onto the respective curves, either "
|
|
"because they are parts of the same element, or because they are both external geometry."));
|
|
}
|
|
return;
|
|
}
|
|
|
|
// ======================================================================================
|
|
|
|
class CmdSketcherConstrainDistanceX : public CmdSketcherConstraint
|
|
{
|
|
public:
|
|
CmdSketcherConstrainDistanceX();
|
|
virtual ~CmdSketcherConstrainDistanceX(){}
|
|
virtual void updateAction(int mode);
|
|
virtual const char* className() const
|
|
{ return "CmdSketcherConstrainDistanceX"; }
|
|
protected:
|
|
virtual void activated(int iMsg);
|
|
virtual void applyConstraint(std::vector<SelIdPair> &selSeq, int seqIndex);
|
|
};
|
|
|
|
CmdSketcherConstrainDistanceX::CmdSketcherConstrainDistanceX()
|
|
:CmdSketcherConstraint("Sketcher_ConstrainDistanceX")
|
|
{
|
|
sAppModule = "Sketcher";
|
|
sGroup = QT_TR_NOOP("Sketcher");
|
|
sMenuText = QT_TR_NOOP("Constrain horizontal distance");
|
|
sToolTipText = QT_TR_NOOP("Fix the horizontal distance between two points or line ends");
|
|
sWhatsThis = "Sketcher_ConstrainDistanceX";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "Constraint_HorizontalDistance";
|
|
sAccel = "SHIFT+H";
|
|
eType = ForEdit;
|
|
|
|
allowedSelSequences = {{SelVertex, SelVertexOrRoot}, {SelRoot, SelVertex},
|
|
{SelEdge}, {SelExternalEdge}}; // Can't do single vertex because its a prefix for 2 vertices
|
|
constraintCursor = cursor_genericconstraint;
|
|
}
|
|
|
|
void CmdSketcherConstrainDistanceX::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
// get the selection
|
|
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
|
|
|
|
// only one sketch with its subelements are allowed to be selected
|
|
if (selection.size() != 1 || !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
|
|
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
|
bool constraintMode = hGrp->GetBool("ContinuousConstraintMode", true);
|
|
|
|
if (constraintMode) {
|
|
ActivateHandler(getActiveGuiDocument(),
|
|
new DrawSketchHandlerGenConstraint(constraintCursor, this));
|
|
getSelection().clearSelection();
|
|
}
|
|
else {
|
|
// TODO: Get the exact message from git history and put it here
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select the right things from the sketch."));
|
|
}
|
|
return;
|
|
}
|
|
|
|
// get the needed lists and objects
|
|
const std::vector<std::string> &SubNames = selection[0].getSubNames();
|
|
Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
|
|
|
|
if (SubNames.size() < 1 || SubNames.size() > 2) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select exactly one line or up to two points from the sketch."));
|
|
return;
|
|
}
|
|
|
|
int GeoId1, GeoId2=Constraint::GeoUndef;
|
|
Sketcher::PointPos PosId1, PosId2=Sketcher::none;
|
|
getIdsFromName(SubNames[0], Obj, GeoId1, PosId1);
|
|
if (SubNames.size() == 2)
|
|
getIdsFromName(SubNames[1], Obj, GeoId2, PosId2);
|
|
|
|
bool arebothpointsorsegmentsfixed=areBothPointsOrSegmentsFixed(Obj,GeoId1, GeoId2);
|
|
|
|
if (GeoId2 == Sketcher::GeoEnum::HAxis || GeoId2 == Sketcher::GeoEnum::VAxis) {
|
|
std::swap(GeoId1,GeoId2);
|
|
std::swap(PosId1,PosId2);
|
|
}
|
|
|
|
if (GeoId1 == Sketcher::GeoEnum::HAxis && PosId1 == Sketcher::none) // reject horizontal axis from selection
|
|
GeoId1 = Constraint::GeoUndef;
|
|
else if (GeoId1 == Sketcher::GeoEnum::VAxis && PosId1 == Sketcher::none) {
|
|
GeoId1 = Sketcher::GeoEnum::HAxis;
|
|
PosId1 = Sketcher::start;
|
|
}
|
|
|
|
if (isEdge(GeoId1,PosId1) && GeoId2 == Constraint::GeoUndef) { // horizontal length of a line
|
|
if (GeoId1 < 0 && GeoId1 >= Sketcher::GeoEnum::VAxis) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Cannot add a horizontal length constraint on an axis!"));
|
|
return;
|
|
}
|
|
|
|
arebothpointsorsegmentsfixed = isPointOrSegmentFixed(Obj,GeoId1);
|
|
|
|
const Part::Geometry *geom = Obj->getGeometry(GeoId1);
|
|
if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
|
|
//convert to as if two endpoints of the line have been selected
|
|
PosId1 = Sketcher::start;
|
|
GeoId2 = GeoId1;
|
|
PosId2 = Sketcher::end;
|
|
}
|
|
}
|
|
if (isVertex(GeoId1,PosId1) && isVertex(GeoId2,PosId2)) { // point to point horizontal distance
|
|
|
|
Base::Vector3d pnt1 = Obj->getPoint(GeoId1,PosId1);
|
|
Base::Vector3d pnt2 = Obj->getPoint(GeoId2,PosId2);
|
|
double ActLength = pnt2.x-pnt1.x;
|
|
|
|
//negative sign avoidance: swap the points to make value positive
|
|
if (ActLength < -Precision::Confusion()) {
|
|
std::swap(GeoId1,GeoId2);
|
|
std::swap(PosId1,PosId2);
|
|
std::swap(pnt1, pnt2);
|
|
ActLength = -ActLength;
|
|
}
|
|
|
|
openCommand("add point to point horizontal distance constraint");
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('DistanceX',%d,%d,%d,%d,%f)) ",
|
|
GeoId1,PosId1,GeoId2,PosId2,ActLength);
|
|
|
|
if (arebothpointsorsegmentsfixed || constraintCreationMode==Reference) { // it is a constraint on a external line, make it non-driving
|
|
const std::vector<Sketcher::Constraint *> &ConStr = Obj->Constraints.getValues();
|
|
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "setDriving(%i,%s)",
|
|
ConStr.size()-1,"False");
|
|
finishDistanceConstraint(this, Obj,false);
|
|
}
|
|
else
|
|
finishDistanceConstraint(this, Obj,true);
|
|
|
|
return;
|
|
}
|
|
else if (isVertex(GeoId1,PosId1) && GeoId2 == Constraint::GeoUndef) { // point on fixed x-coordinate
|
|
|
|
if (GeoId1 < 0 && GeoId1 >= Sketcher::GeoEnum::VAxis) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Cannot add a fixed x-coordinate constraint on the origin point!"));
|
|
return;
|
|
}
|
|
|
|
Base::Vector3d pnt = Obj->getPoint(GeoId1,PosId1);
|
|
double ActX = pnt.x;
|
|
|
|
arebothpointsorsegmentsfixed=isPointOrSegmentFixed(Obj,GeoId1);
|
|
|
|
openCommand("add fixed x-coordinate constraint");
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('DistanceX',%d,%d,%f)) ",
|
|
GeoId1,PosId1,ActX);
|
|
|
|
|
|
if (arebothpointsorsegmentsfixed || constraintCreationMode==Reference) { // it is a constraint on a external line, make it non-driving
|
|
const std::vector<Sketcher::Constraint *> &ConStr = Obj->Constraints.getValues();
|
|
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),"setDriving(%i,%s)",
|
|
ConStr.size()-1,"False");
|
|
finishDistanceConstraint(this, Obj,false);
|
|
}
|
|
else
|
|
finishDistanceConstraint(this, Obj,true);
|
|
|
|
return;
|
|
}
|
|
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select exactly one line or up to two points from the sketch."));
|
|
return;
|
|
}
|
|
|
|
void CmdSketcherConstrainDistanceX::applyConstraint(std::vector<SelIdPair> &selSeq, int seqIndex)
|
|
{
|
|
SketcherGui::ViewProviderSketch* sketchgui = static_cast<SketcherGui::ViewProviderSketch*>(getActiveGuiDocument()->getInEdit());
|
|
Sketcher::SketchObject* Obj = sketchgui->getSketchObject();
|
|
|
|
int GeoId1 = Constraint::GeoUndef, GeoId2 = Constraint::GeoUndef;
|
|
Sketcher::PointPos PosId1 = Sketcher::none, PosId2 = Sketcher::none;
|
|
|
|
switch (seqIndex) {
|
|
case 0: // {SelVertex, SelVertexOrRoot}
|
|
case 1: // {SelRoot, SelVertex}
|
|
{
|
|
GeoId1 = selSeq.at(0).GeoId; GeoId2 = selSeq.at(1).GeoId;
|
|
PosId1 = selSeq.at(0).PosId; PosId2 = selSeq.at(1).PosId;
|
|
break;
|
|
}
|
|
case 2: // {SelEdge}
|
|
case 4: // {SelExternalEdge}
|
|
{
|
|
GeoId1 = GeoId2 = selSeq.at(0).GeoId;
|
|
PosId1 = Sketcher::start; PosId2 = Sketcher::end;
|
|
|
|
const Part::Geometry *geom = Obj->getGeometry(GeoId1);
|
|
if (geom->getTypeId() != Part::GeomLineSegment::getClassTypeId()) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("This constraint only makes sense on a line segment or a pair of points"));
|
|
return;
|
|
}
|
|
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
Base::Vector3d pnt1 = Obj->getPoint(GeoId1,PosId1);
|
|
Base::Vector3d pnt2 = Obj->getPoint(GeoId2,PosId2);
|
|
double ActLength = pnt2.x-pnt1.x;
|
|
|
|
//negative sign avoidance: swap the points to make value positive
|
|
if (ActLength < -Precision::Confusion()) {
|
|
std::swap(GeoId1,GeoId2);
|
|
std::swap(PosId1,PosId2);
|
|
std::swap(pnt1, pnt2);
|
|
ActLength = -ActLength;
|
|
}
|
|
|
|
openCommand("add point to point horizontal distance constraint");
|
|
Gui::cmdAppObjectArgs(Obj,"addConstraint(Sketcher.Constraint('DistanceX',%d,%d,%d,%d,%f)) ",
|
|
GeoId1,PosId1,GeoId2,PosId2,ActLength);
|
|
|
|
if (areBothPointsOrSegmentsFixed(Obj,GeoId1, GeoId2) || constraintCreationMode==Reference) { // it is a constraint on a external line, make it non-driving
|
|
const std::vector<Sketcher::Constraint *> &ConStr = Obj->Constraints.getValues();
|
|
|
|
Gui::cmdAppObjectArgs(Obj,"setDriving(%i,%s)",
|
|
ConStr.size()-1,"False");
|
|
finishDistanceConstraint(this, Obj, false);
|
|
}
|
|
else
|
|
finishDistanceConstraint(this, Obj, true);
|
|
}
|
|
|
|
void CmdSketcherConstrainDistanceX::updateAction(int mode)
|
|
{
|
|
switch (mode) {
|
|
case Reference:
|
|
if (getAction())
|
|
getAction()->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_HorizontalDistance_Driven"));
|
|
break;
|
|
case Driving:
|
|
if (getAction())
|
|
getAction()->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_HorizontalDistance"));
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
// ======================================================================================
|
|
|
|
class CmdSketcherConstrainDistanceY : public CmdSketcherConstraint
|
|
{
|
|
public:
|
|
CmdSketcherConstrainDistanceY();
|
|
virtual ~CmdSketcherConstrainDistanceY(){}
|
|
virtual void updateAction(int mode);
|
|
virtual const char* className() const
|
|
{ return "CmdSketcherConstrainDistanceY"; }
|
|
protected:
|
|
virtual void activated(int iMsg);
|
|
virtual void applyConstraint(std::vector<SelIdPair> &selSeq, int seqIndex);
|
|
};
|
|
|
|
CmdSketcherConstrainDistanceY::CmdSketcherConstrainDistanceY()
|
|
:CmdSketcherConstraint("Sketcher_ConstrainDistanceY")
|
|
{
|
|
sAppModule = "Sketcher";
|
|
sGroup = QT_TR_NOOP("Sketcher");
|
|
sMenuText = QT_TR_NOOP("Constrain vertical distance");
|
|
sToolTipText = QT_TR_NOOP("Fix the vertical distance between two points or line ends");
|
|
sWhatsThis = "Sketcher_ConstrainDistanceY";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "Constraint_VerticalDistance";
|
|
sAccel = "SHIFT+V";
|
|
eType = ForEdit;
|
|
|
|
allowedSelSequences = {{SelVertex, SelVertexOrRoot}, {SelRoot, SelVertex},
|
|
{SelEdge}, {SelExternalEdge}}; // Can't do single vertex because its a prefix for 2 vertices
|
|
constraintCursor = cursor_genericconstraint;
|
|
}
|
|
|
|
void CmdSketcherConstrainDistanceY::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
// get the selection
|
|
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
|
|
|
|
// only one sketch with its subelements are allowed to be selected
|
|
if (selection.size() != 1 || !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
|
|
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
|
bool constraintMode = hGrp->GetBool("ContinuousConstraintMode", true);
|
|
|
|
if (constraintMode) {
|
|
ActivateHandler(getActiveGuiDocument(),
|
|
new DrawSketchHandlerGenConstraint(constraintCursor, this));
|
|
getSelection().clearSelection();
|
|
} else {
|
|
// TODO: Get the exact message from git history and put it here
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select the right things from the sketch."));
|
|
}
|
|
return;
|
|
}
|
|
|
|
// get the needed lists and objects
|
|
const std::vector<std::string> &SubNames = selection[0].getSubNames();
|
|
Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
|
|
|
|
if (SubNames.size() < 1 || SubNames.size() > 2) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select exactly one line or up to two points from the sketch."));
|
|
return;
|
|
}
|
|
|
|
int GeoId1, GeoId2=Constraint::GeoUndef;
|
|
Sketcher::PointPos PosId1, PosId2=Sketcher::none;
|
|
getIdsFromName(SubNames[0], Obj, GeoId1, PosId1);
|
|
if (SubNames.size() == 2)
|
|
getIdsFromName(SubNames[1], Obj, GeoId2, PosId2);
|
|
|
|
bool arebothpointsorsegmentsfixed=areBothPointsOrSegmentsFixed(Obj,GeoId1, GeoId2);
|
|
|
|
if (GeoId2 == Sketcher::GeoEnum::HAxis || GeoId2 == Sketcher::GeoEnum::VAxis) {
|
|
std::swap(GeoId1,GeoId2);
|
|
std::swap(PosId1,PosId2);
|
|
}
|
|
|
|
if (GeoId1 == Sketcher::GeoEnum::VAxis && PosId1 == Sketcher::none) // reject vertical axis from selection
|
|
GeoId1 = Constraint::GeoUndef;
|
|
else if (GeoId1 == Sketcher::GeoEnum::HAxis && PosId1 == Sketcher::none)
|
|
PosId1 = Sketcher::start;
|
|
|
|
if (isEdge(GeoId1,PosId1) && GeoId2 == Constraint::GeoUndef) { // vertical length of a line
|
|
if (GeoId1 < 0 && GeoId1 >= Sketcher::GeoEnum::VAxis) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Cannot add a vertical length constraint on an axis!"));
|
|
return;
|
|
}
|
|
|
|
arebothpointsorsegmentsfixed = isPointOrSegmentFixed(Obj,GeoId1);
|
|
|
|
const Part::Geometry *geom = Obj->getGeometry(GeoId1);
|
|
if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
|
|
//convert to as if two endpoints of the line have been selected
|
|
PosId1 = Sketcher::start;
|
|
GeoId2 = GeoId1;
|
|
PosId2 = Sketcher::end;
|
|
}
|
|
}
|
|
|
|
if (isVertex(GeoId1,PosId1) && isVertex(GeoId2,PosId2)) { // point to point vertical distance
|
|
|
|
Base::Vector3d pnt1 = Obj->getPoint(GeoId1,PosId1);
|
|
Base::Vector3d pnt2 = Obj->getPoint(GeoId2,PosId2);
|
|
double ActLength = pnt2.y-pnt1.y;
|
|
|
|
//negative sign avoidance: swap the points to make value positive
|
|
if (ActLength < -Precision::Confusion()) {
|
|
std::swap(GeoId1,GeoId2);
|
|
std::swap(PosId1,PosId2);
|
|
std::swap(pnt1, pnt2);
|
|
ActLength = -ActLength;
|
|
}
|
|
|
|
openCommand("add point to point vertical distance constraint");
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),"addConstraint(Sketcher.Constraint('DistanceY',%d,%d,%d,%d,%f)) ",
|
|
GeoId1,PosId1,GeoId2,PosId2,ActLength);
|
|
|
|
if (arebothpointsorsegmentsfixed || constraintCreationMode==Reference) { // it is a constraint on a external line, make it non-driving
|
|
const std::vector<Sketcher::Constraint *> &ConStr = Obj->Constraints.getValues();
|
|
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "setDriving(%i,%s)",
|
|
ConStr.size()-1,"False");
|
|
finishDistanceConstraint(this, Obj,false);
|
|
}
|
|
else
|
|
finishDistanceConstraint(this, Obj,true);
|
|
|
|
return;
|
|
}
|
|
else if (isVertex(GeoId1,PosId1) && GeoId2 == Constraint::GeoUndef) { // point on fixed y-coordinate
|
|
|
|
if (GeoId1 < 0 && GeoId1 >= Sketcher::GeoEnum::VAxis) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Cannot add a fixed y-coordinate constraint on the origin point!"));
|
|
return;
|
|
}
|
|
|
|
Base::Vector3d pnt = Obj->getPoint(GeoId1,PosId1);
|
|
double ActY = pnt.y;
|
|
|
|
arebothpointsorsegmentsfixed=isPointOrSegmentFixed(Obj,GeoId1);
|
|
|
|
openCommand("add fixed y-coordinate constraint");
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('DistanceY',%d,%d,%f)) ",
|
|
GeoId1,PosId1,ActY);
|
|
|
|
if (GeoId1 <= Sketcher::GeoEnum::RefExt || isConstructionPoint(Obj,GeoId1) || constraintCreationMode==Reference) { // it is a constraint on a external line, make it non-driving
|
|
const std::vector<Sketcher::Constraint *> &ConStr = Obj->Constraints.getValues();
|
|
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "setDriving(%i,%s)",
|
|
ConStr.size()-1,"False");
|
|
finishDistanceConstraint(this, Obj,false);
|
|
}
|
|
else
|
|
finishDistanceConstraint(this, Obj,true);
|
|
|
|
return;
|
|
}
|
|
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select exactly one line or up to two points from the sketch."));
|
|
return;
|
|
}
|
|
|
|
void CmdSketcherConstrainDistanceY::applyConstraint(std::vector<SelIdPair> &selSeq, int seqIndex)
|
|
{
|
|
SketcherGui::ViewProviderSketch* sketchgui = static_cast<SketcherGui::ViewProviderSketch*>(getActiveGuiDocument()->getInEdit());
|
|
Sketcher::SketchObject* Obj = sketchgui->getSketchObject();
|
|
|
|
int GeoId1 = Constraint::GeoUndef, GeoId2 = Constraint::GeoUndef;
|
|
Sketcher::PointPos PosId1 = Sketcher::none, PosId2 = Sketcher::none;
|
|
|
|
switch (seqIndex) {
|
|
case 0: // {SelVertex, SelVertexOrRoot}
|
|
case 1: // {SelRoot, SelVertex}
|
|
{
|
|
GeoId1 = selSeq.at(0).GeoId; GeoId2 = selSeq.at(1).GeoId;
|
|
PosId1 = selSeq.at(0).PosId; PosId2 = selSeq.at(1).PosId;
|
|
break;
|
|
}
|
|
case 2: // {SelEdge}
|
|
case 3: // {SelExternalEdge}
|
|
{
|
|
GeoId1 = GeoId2 = selSeq.at(0).GeoId;
|
|
PosId1 = Sketcher::start; PosId2 = Sketcher::end;
|
|
|
|
const Part::Geometry *geom = Obj->getGeometry(GeoId1);
|
|
if (geom->getTypeId() != Part::GeomLineSegment::getClassTypeId()) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("This constraint only makes sense on a line segment or a pair of points"));
|
|
return;
|
|
}
|
|
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
Base::Vector3d pnt1 = Obj->getPoint(GeoId1,PosId1);
|
|
Base::Vector3d pnt2 = Obj->getPoint(GeoId2,PosId2);
|
|
double ActLength = pnt2.y-pnt1.y;
|
|
|
|
//negative sign avoidance: swap the points to make value positive
|
|
if (ActLength < -Precision::Confusion()) {
|
|
std::swap(GeoId1,GeoId2);
|
|
std::swap(PosId1,PosId2);
|
|
std::swap(pnt1, pnt2);
|
|
ActLength = -ActLength;
|
|
}
|
|
|
|
openCommand("add point to point vertical distance constraint");
|
|
Gui::cmdAppObjectArgs(Obj,"addConstraint(Sketcher.Constraint('DistanceY',%d,%d,%d,%d,%f)) ",
|
|
GeoId1,PosId1,GeoId2,PosId2,ActLength);
|
|
|
|
if (areBothPointsOrSegmentsFixed(Obj,GeoId1, GeoId2) || constraintCreationMode==Reference) { // it is a constraint on a external line, make it non-driving
|
|
const std::vector<Sketcher::Constraint *> &ConStr = Obj->Constraints.getValues();
|
|
|
|
Gui::cmdAppObjectArgs(Obj, "setDriving(%i,%s)",
|
|
ConStr.size()-1,"False");
|
|
finishDistanceConstraint(this, Obj,false);
|
|
}
|
|
else
|
|
finishDistanceConstraint(this, Obj,true);
|
|
}
|
|
|
|
void CmdSketcherConstrainDistanceY::updateAction(int mode)
|
|
{
|
|
switch (mode) {
|
|
case Reference:
|
|
if (getAction())
|
|
getAction()->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_VerticalDistance_Driven"));
|
|
break;
|
|
case Driving:
|
|
if (getAction())
|
|
getAction()->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_VerticalDistance"));
|
|
break;
|
|
}
|
|
}
|
|
|
|
//=================================================================================
|
|
|
|
/* XPM */
|
|
static const char *cursor_createparallel[]={
|
|
"32 32 3 1",
|
|
" c None",
|
|
". c #FFFFFF",
|
|
"+ c #FF0000",
|
|
" . ",
|
|
" . ",
|
|
" . ",
|
|
" . ",
|
|
" . ",
|
|
" ",
|
|
"..... ..... ",
|
|
" ",
|
|
" . ",
|
|
" . ",
|
|
" . + + ",
|
|
" . ++ ++ ",
|
|
" . + + ",
|
|
" ++ ++ ",
|
|
" + + ",
|
|
" ++ ++ ",
|
|
" + + ",
|
|
" ++ ++ ",
|
|
" + + ",
|
|
" ++ ++ ",
|
|
" + + ",
|
|
" ++ ++ ",
|
|
" + + ",
|
|
" ++ ++ ",
|
|
" + + ",
|
|
" ++ ++ ",
|
|
" + + ",
|
|
" ++ ++ ",
|
|
" + + ",
|
|
" ",
|
|
" ",
|
|
" "};
|
|
|
|
class CmdSketcherConstrainParallel : public CmdSketcherConstraint
|
|
{
|
|
public:
|
|
CmdSketcherConstrainParallel();
|
|
virtual ~CmdSketcherConstrainParallel(){}
|
|
virtual const char* className() const
|
|
{ return "CmdSketcherConstrainParallel"; }
|
|
protected:
|
|
virtual void activated(int iMsg);
|
|
virtual void applyConstraint(std::vector<SelIdPair> &selSeq, int seqIndex);
|
|
};
|
|
|
|
CmdSketcherConstrainParallel::CmdSketcherConstrainParallel()
|
|
:CmdSketcherConstraint("Sketcher_ConstrainParallel")
|
|
{
|
|
sAppModule = "Sketcher";
|
|
sGroup = QT_TR_NOOP("Sketcher");
|
|
sMenuText = QT_TR_NOOP("Constrain parallel");
|
|
sToolTipText = QT_TR_NOOP("Create a parallel constraint between two lines");
|
|
sWhatsThis = "Sketcher_ConstrainParallel";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "Constraint_Parallel";
|
|
sAccel = "SHIFT+P";
|
|
eType = ForEdit;
|
|
|
|
// TODO: Also needed: ExternalEdges
|
|
allowedSelSequences = {{SelEdge, SelEdgeOrAxis}, {SelEdgeOrAxis, SelEdge},
|
|
{SelEdge, SelExternalEdge}, {SelExternalEdge, SelEdge}};
|
|
constraintCursor = cursor_createparallel;
|
|
}
|
|
|
|
void CmdSketcherConstrainParallel::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
// get the selection
|
|
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
|
|
|
|
// only one sketch with its subelements are allowed to be selected
|
|
if (selection.size() != 1 || !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
|
|
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
|
bool constraintMode = hGrp->GetBool("ContinuousConstraintMode", true);
|
|
|
|
if (constraintMode) {
|
|
ActivateHandler(getActiveGuiDocument(),
|
|
new DrawSketchHandlerGenConstraint(constraintCursor, this));
|
|
getSelection().clearSelection();
|
|
} else {
|
|
// TODO: Get the exact message from git history and put it here
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select two or more lines from the sketch."));
|
|
}
|
|
return;
|
|
}
|
|
|
|
// get the needed lists and objects
|
|
const std::vector<std::string> &SubNames = selection[0].getSubNames();
|
|
Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
|
|
|
|
// go through the selected subelements
|
|
|
|
if (SubNames.size() < 2) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select at least two lines from the sketch."));
|
|
return;
|
|
}
|
|
|
|
std::vector<int> ids;
|
|
bool hasAlreadyExternal=false;
|
|
for (std::vector<std::string>::const_iterator it=SubNames.begin();it!=SubNames.end();++it) {
|
|
|
|
int GeoId;
|
|
Sketcher::PointPos PosId;
|
|
getIdsFromName(*it, Obj, GeoId, PosId);
|
|
|
|
if (!isEdge(GeoId,PosId)) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select a valid line"));
|
|
return;
|
|
}
|
|
else if (isPointOrSegmentFixed(Obj,GeoId)) {
|
|
if (hasAlreadyExternal) {
|
|
showNoConstraintBetweenFixedGeometry();
|
|
return;
|
|
}
|
|
else
|
|
hasAlreadyExternal = true;
|
|
}
|
|
|
|
// Check that the curve is a line segment
|
|
const Part::Geometry *geo = Obj->getGeometry(GeoId);
|
|
if (geo->getTypeId() != Part::GeomLineSegment::getClassTypeId()) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("The selected edge is not a valid line"));
|
|
return;
|
|
}
|
|
ids.push_back(GeoId);
|
|
}
|
|
|
|
// undo command open
|
|
openCommand("add parallel constraint");
|
|
for (int i=0; i < int(ids.size()-1); i++) {
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('Parallel',%d,%d)) ",
|
|
ids[i],ids[i+1]);
|
|
}
|
|
// finish the transaction and update
|
|
commitCommand();
|
|
|
|
tryAutoRecompute(Obj);
|
|
|
|
// clear the selection (convenience)
|
|
getSelection().clearSelection();
|
|
}
|
|
|
|
void CmdSketcherConstrainParallel::applyConstraint(std::vector<SelIdPair> &selSeq, int seqIndex)
|
|
{
|
|
switch (seqIndex) {
|
|
case 0: // {SelEdge, SelEdgeOrAxis}
|
|
case 1: // {SelEdgeOrAxis, SelEdge}
|
|
case 2: // {SelEdge, SelExternalEdge}
|
|
case 3: // {SelExternalEdge, SelEdge}
|
|
// create the constraint
|
|
SketcherGui::ViewProviderSketch* sketchgui = static_cast<SketcherGui::ViewProviderSketch*>(getActiveGuiDocument()->getInEdit());
|
|
Sketcher::SketchObject* Obj = sketchgui->getSketchObject();
|
|
|
|
int GeoId1 = selSeq.at(0).GeoId, GeoId2 = selSeq.at(1).GeoId;
|
|
|
|
// Check that the curves are line segments
|
|
if ( Obj->getGeometry(GeoId1)->getTypeId() != Part::GeomLineSegment::getClassTypeId() ||
|
|
Obj->getGeometry(GeoId2)->getTypeId() != Part::GeomLineSegment::getClassTypeId()) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("The selected edge is not a valid line"));
|
|
return;
|
|
}
|
|
|
|
if( areBothPointsOrSegmentsFixed(Obj,GeoId1, GeoId2)) {
|
|
showNoConstraintBetweenFixedGeometry();
|
|
return;
|
|
}
|
|
|
|
// undo command open
|
|
openCommand("add parallel constraint");
|
|
Gui::cmdAppObjectArgs(sketchgui->getObject(), "addConstraint(Sketcher.Constraint('Parallel',%d,%d)) ",
|
|
GeoId1, GeoId2);
|
|
// finish the transaction and update
|
|
commitCommand();
|
|
tryAutoRecompute(Obj);
|
|
}
|
|
}
|
|
|
|
// ======================================================================================
|
|
|
|
/* XPM */
|
|
static const char *cursor_createperpconstraint[] = {
|
|
"32 32 3 1",
|
|
" c None",
|
|
". c #FFFFFF",
|
|
"+ c #FF0000",
|
|
" . ",
|
|
" . ",
|
|
" . ",
|
|
" . ",
|
|
" . ",
|
|
" ",
|
|
"..... ..... ",
|
|
" ",
|
|
" . ",
|
|
" . ",
|
|
" . ",
|
|
" . ++ ",
|
|
" . ++ ",
|
|
" ++ ",
|
|
" ++ ",
|
|
" ++ ",
|
|
" ++ ",
|
|
" ++ ",
|
|
" ++ ",
|
|
" ++ ",
|
|
" ++ ",
|
|
" ++ ",
|
|
" ++ ",
|
|
" ++ ",
|
|
" ++ ",
|
|
" ++ ",
|
|
" ++ ",
|
|
" ++ ",
|
|
" ++++++++++++++++++++ ",
|
|
" ++++++++++++++++++++ ",
|
|
" ",
|
|
" "};
|
|
|
|
class CmdSketcherConstrainPerpendicular : public CmdSketcherConstraint
|
|
{
|
|
public:
|
|
CmdSketcherConstrainPerpendicular();
|
|
virtual ~CmdSketcherConstrainPerpendicular(){}
|
|
virtual const char* className() const
|
|
{ return "CmdSketcherConstrainPerpendicular"; }
|
|
protected:
|
|
virtual void activated(int iMsg);
|
|
virtual void applyConstraint(std::vector<SelIdPair> &selSeq, int seqIndex);
|
|
};
|
|
|
|
CmdSketcherConstrainPerpendicular::CmdSketcherConstrainPerpendicular()
|
|
:CmdSketcherConstraint("Sketcher_ConstrainPerpendicular")
|
|
{
|
|
sAppModule = "Sketcher";
|
|
sGroup = QT_TR_NOOP("Sketcher");
|
|
sMenuText = QT_TR_NOOP("Constrain perpendicular");
|
|
sToolTipText = QT_TR_NOOP("Create a perpendicular constraint between two lines");
|
|
sWhatsThis = "Sketcher_ConstrainPerpendicular";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "Constraint_Perpendicular";
|
|
sAccel = "N";
|
|
eType = ForEdit;
|
|
|
|
// TODO: there are two more combos: endpoint then curve and endpoint then endpoint
|
|
allowedSelSequences = {{SelEdge, SelEdgeOrAxis}, {SelEdgeOrAxis, SelEdge},
|
|
{SelEdge, SelExternalEdge}, {SelExternalEdge, SelEdge},
|
|
{SelVertexOrRoot, SelEdge, SelEdgeOrAxis},
|
|
{SelVertexOrRoot, SelEdgeOrAxis, SelEdge},
|
|
{SelVertexOrRoot, SelEdge, SelExternalEdge},
|
|
{SelVertexOrRoot, SelExternalEdge, SelEdge},
|
|
{SelEdge, SelVertexOrRoot, SelEdgeOrAxis},
|
|
{SelEdgeOrAxis, SelVertexOrRoot, SelEdge},
|
|
{SelEdge, SelVertexOrRoot, SelExternalEdge},
|
|
{SelExternalEdge, SelVertexOrRoot, SelEdge}};
|
|
;
|
|
constraintCursor = cursor_createperpconstraint;
|
|
}
|
|
|
|
void CmdSketcherConstrainPerpendicular::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
QString strBasicHelp =
|
|
QObject::tr(
|
|
"There is a number of ways this constraint can be applied.\n\n"
|
|
"Accepted combinations: two curves; an endpoint and a curve; two endpoints; two curves and a point.",
|
|
/*disambig.:*/ "perpendicular constraint");
|
|
QString strError;
|
|
|
|
// get the selection
|
|
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
|
|
|
|
// only one sketch with its subelements are allowed to be selected
|
|
if (selection.size() != 1 || !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
|
|
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
|
bool constraintMode = hGrp->GetBool("ContinuousConstraintMode", true);
|
|
|
|
if (constraintMode) {
|
|
ActivateHandler(getActiveGuiDocument(),
|
|
new DrawSketchHandlerGenConstraint(constraintCursor, this));
|
|
getSelection().clearSelection();
|
|
} else {
|
|
// TODO: Get the exact message from git history and put it here
|
|
strError = QObject::tr("Select some geometry from the sketch.", "perpendicular constraint");
|
|
if (!strError.isEmpty()) strError.append(QString::fromLatin1("\n\n"));
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
strError+strBasicHelp);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// get the needed lists and objects
|
|
const std::vector<std::string> &SubNames = selection[0].getSubNames();
|
|
Sketcher::SketchObject* Obj = dynamic_cast<Sketcher::SketchObject*>(selection[0].getObject());
|
|
|
|
if (!Obj || (SubNames.size() != 2 && SubNames.size() != 3)) {
|
|
strError = QObject::tr("Wrong number of selected objects!","perpendicular constraint");
|
|
if (!strError.isEmpty()) strError.append(QString::fromLatin1("\n\n"));
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
strError+strBasicHelp);
|
|
return;
|
|
}
|
|
|
|
int GeoId1, GeoId2, GeoId3;
|
|
Sketcher::PointPos PosId1, PosId2, PosId3;
|
|
getIdsFromName(SubNames[0], Obj, GeoId1, PosId1);
|
|
getIdsFromName(SubNames[1], Obj, GeoId2, PosId2);
|
|
|
|
if (areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2)) { //checkBothExternal displays error message
|
|
showNoConstraintBetweenFixedGeometry();
|
|
return;
|
|
}
|
|
|
|
if (SubNames.size() == 3) { //perpendicular via point
|
|
getIdsFromName(SubNames[2], Obj, GeoId3, PosId3);
|
|
//let's sink the point to be GeoId3. We want to keep the order the two curves have been selected in.
|
|
if ( isVertex(GeoId1, PosId1) ){
|
|
std::swap(GeoId1,GeoId2);
|
|
std::swap(PosId1,PosId2);
|
|
};
|
|
if ( isVertex(GeoId2, PosId2) ){
|
|
std::swap(GeoId2,GeoId3);
|
|
std::swap(PosId2,PosId3);
|
|
};
|
|
|
|
if (isEdge(GeoId1, PosId1) && isEdge(GeoId2, PosId2) && isVertex(GeoId3, PosId3)) {
|
|
|
|
openCommand("add perpendicular constraint");
|
|
|
|
try{
|
|
//add missing point-on-object constraints
|
|
if(! IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)){
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
|
GeoId3,PosId3,GeoId1);
|
|
};
|
|
|
|
if(! IsPointAlreadyOnCurve(GeoId2, GeoId3, PosId3, Obj)){
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
|
GeoId3,PosId3,GeoId2);
|
|
};
|
|
|
|
if(! IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)){//FIXME: it's a good idea to add a check if the sketch is solved
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
|
GeoId3,PosId3,GeoId1);
|
|
};
|
|
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('PerpendicularViaPoint',%d,%d,%d,%d)) ",
|
|
GeoId1,GeoId2,GeoId3,PosId3);
|
|
} catch (const Base::Exception& e) {
|
|
Base::Console().Error("%s\n", e.what());
|
|
QMessageBox::warning(Gui::getMainWindow(),
|
|
QObject::tr("Error"),
|
|
QString::fromLatin1(e.what()));
|
|
Gui::Command::abortCommand();
|
|
|
|
tryAutoRecompute(Obj);
|
|
return;
|
|
}
|
|
|
|
commitCommand();
|
|
tryAutoRecompute(Obj);
|
|
|
|
getSelection().clearSelection();
|
|
|
|
return;
|
|
|
|
};
|
|
strError = QObject::tr("With 3 objects, there must be 2 curves and 1 point.", "tangent constraint");
|
|
|
|
} else if (SubNames.size() == 2) {
|
|
|
|
if (isVertex(GeoId1,PosId1) && isVertex(GeoId2,PosId2)) { //endpoint-to-endpoint perpendicularity
|
|
|
|
if (isSimpleVertex(Obj, GeoId1, PosId1) ||
|
|
isSimpleVertex(Obj, GeoId2, PosId2)) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Cannot add a perpendicularity constraint at an unconnected point!"));
|
|
return;
|
|
}
|
|
|
|
// This code supports simple B-spline endpoint perp to any other geometric curve
|
|
const Part::Geometry *geom1 = Obj->getGeometry(GeoId1);
|
|
const Part::Geometry *geom2 = Obj->getGeometry(GeoId2);
|
|
|
|
if( geom1 && geom2 &&
|
|
( geom1->getTypeId() == Part::GeomBSplineCurve::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomBSplineCurve::getClassTypeId() )){
|
|
|
|
if(geom1->getTypeId() != Part::GeomBSplineCurve::getClassTypeId()) {
|
|
std::swap(GeoId1,GeoId2);
|
|
std::swap(PosId1,PosId2);
|
|
}
|
|
// GeoId1 is the B-spline now
|
|
} // end of code supports simple B-spline endpoint tangency
|
|
|
|
openCommand("add perpendicular constraint");
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('Perpendicular',%d,%d,%d,%d)) ",
|
|
GeoId1,PosId1,GeoId2,PosId2);
|
|
commitCommand();
|
|
tryAutoRecompute(Obj);
|
|
|
|
getSelection().clearSelection();
|
|
return;
|
|
}
|
|
else if ((isVertex(GeoId1,PosId1) && isEdge(GeoId2,PosId2)) ||
|
|
(isEdge(GeoId1,PosId1) && isVertex(GeoId2,PosId2))) { // endpoint-to-curve
|
|
if (isVertex(GeoId2,PosId2)) {
|
|
std::swap(GeoId1,GeoId2);
|
|
std::swap(PosId1,PosId2);
|
|
}
|
|
|
|
if (isSimpleVertex(Obj, GeoId1, PosId1)) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Cannot add a perpendicularity constraint at an unconnected point!"));
|
|
return;
|
|
}
|
|
|
|
const Part::Geometry *geom2 = Obj->getGeometry(GeoId2);
|
|
|
|
if( geom2 && geom2->getTypeId() == Part::GeomBSplineCurve::getClassTypeId() ){
|
|
// unsupported until normal to B-spline at any point implemented.
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Perpendicular to B-spline edge currently unsupported."));
|
|
return;
|
|
}
|
|
|
|
openCommand("add perpendicularity constraint");
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('Perpendicular',%d,%d,%d)) ",
|
|
GeoId1,PosId1,GeoId2);
|
|
commitCommand();
|
|
tryAutoRecompute(Obj);
|
|
|
|
getSelection().clearSelection();
|
|
return;
|
|
}
|
|
else if (isEdge(GeoId1,PosId1) && isEdge(GeoId2,PosId2)) { // simple perpendicularity between GeoId1 and GeoId2
|
|
|
|
const Part::Geometry *geo1 = Obj->getGeometry(GeoId1);
|
|
const Part::Geometry *geo2 = Obj->getGeometry(GeoId2);
|
|
if (!geo1 || !geo2) {
|
|
return;
|
|
}
|
|
|
|
if (geo1->getTypeId() != Part::GeomLineSegment::getClassTypeId() &&
|
|
geo2->getTypeId() != Part::GeomLineSegment::getClassTypeId()) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("One of the selected edges should be a line."));
|
|
return;
|
|
}
|
|
|
|
if (geo1->getTypeId() == Part::GeomBSplineCurve::getClassTypeId() ||
|
|
geo2->getTypeId() == Part::GeomBSplineCurve::getClassTypeId()){
|
|
|
|
// unsupported until tangent to B-spline at any point implemented.
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Perpendicular to B-spline edge currently unsupported."));
|
|
return;
|
|
}
|
|
|
|
if (geo1->getTypeId() == Part::GeomLineSegment::getClassTypeId())
|
|
std::swap(GeoId1,GeoId2);
|
|
|
|
// GeoId2 is the line
|
|
geo1 = Obj->getGeometry(GeoId1);
|
|
geo2 = Obj->getGeometry(GeoId2);
|
|
|
|
if (geo1->getTypeId() == Part::GeomEllipse::getClassTypeId() ||
|
|
geo1->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId() ||
|
|
geo1->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId() ||
|
|
geo1->getTypeId() == Part::GeomArcOfParabola::getClassTypeId()) {
|
|
|
|
Base::Vector3d center;
|
|
Base::Vector3d majdir;
|
|
Base::Vector3d focus;
|
|
double majord = 0;
|
|
double minord = 0;
|
|
double phi = 0;
|
|
|
|
if( geo1->getTypeId() == Part::GeomEllipse::getClassTypeId() ){
|
|
const Part::GeomEllipse *ellipse = static_cast<const Part::GeomEllipse *>(geo1);
|
|
|
|
center=ellipse->getCenter();
|
|
majord=ellipse->getMajorRadius();
|
|
minord=ellipse->getMinorRadius();
|
|
majdir=ellipse->getMajorAxisDir();
|
|
phi=atan2(majdir.y, majdir.x);
|
|
}
|
|
else if( geo1->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId() ){
|
|
const Part::GeomArcOfEllipse *aoe = static_cast<const Part::GeomArcOfEllipse *>(geo1);
|
|
|
|
center=aoe->getCenter();
|
|
majord=aoe->getMajorRadius();
|
|
minord=aoe->getMinorRadius();
|
|
majdir=aoe->getMajorAxisDir();
|
|
phi=atan2(majdir.y, majdir.x);
|
|
}
|
|
else if( geo1->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId() ){
|
|
const Part::GeomArcOfHyperbola *aoh = static_cast<const Part::GeomArcOfHyperbola *>(geo1);
|
|
|
|
center=aoh->getCenter();
|
|
majord=aoh->getMajorRadius();
|
|
minord=aoh->getMinorRadius();
|
|
majdir=aoh->getMajorAxisDir();
|
|
phi=atan2(majdir.y, majdir.x);
|
|
}
|
|
else if( geo1->getTypeId() == Part::GeomArcOfParabola::getClassTypeId() ){
|
|
const Part::GeomArcOfParabola *aop = static_cast<const Part::GeomArcOfParabola *>(geo1);
|
|
|
|
center=aop->getCenter();
|
|
focus=aop->getFocus();
|
|
}
|
|
|
|
const Part::GeomLineSegment *line = static_cast<const Part::GeomLineSegment *>(geo2);
|
|
|
|
Base::Vector3d point1=line->getStartPoint();
|
|
Base::Vector3d PoO;
|
|
|
|
if( geo1->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId() ) {
|
|
double df=sqrt(majord*majord+minord*minord);
|
|
Base::Vector3d direction=point1-(center+majdir*df); // towards the focus
|
|
double tapprox=atan2(direction.y,direction.x)-phi;
|
|
|
|
PoO = Base::Vector3d(center.x+majord*cosh(tapprox)*cos(phi)-minord*sinh(tapprox)*sin(phi),
|
|
center.y+majord*cosh(tapprox)*sin(phi)+minord*sinh(tapprox)*cos(phi), 0);
|
|
}
|
|
else if( geo1->getTypeId() == Part::GeomArcOfParabola::getClassTypeId() ) {
|
|
Base::Vector3d direction=point1-focus; // towards the focus
|
|
|
|
PoO = point1 + direction / 2;
|
|
}
|
|
else {
|
|
Base::Vector3d direction=point1-center;
|
|
double tapprox=atan2(direction.y,direction.x)-phi; // we approximate the eccentric anomaly by the polar
|
|
|
|
PoO = Base::Vector3d(center.x+majord*cos(tapprox)*cos(phi)-minord*sin(tapprox)*sin(phi),
|
|
center.y+majord*cos(tapprox)*sin(phi)+minord*sin(tapprox)*cos(phi), 0);
|
|
}
|
|
openCommand("add perpendicular constraint");
|
|
|
|
try {
|
|
// Add a point
|
|
Gui::cmdAppObjectArgs(Obj, "addGeometry(Part.Point(App.Vector(%f,%f,0)))",
|
|
PoO.x,PoO.y);
|
|
int GeoIdPoint = Obj->getHighestCurveIndex();
|
|
|
|
// Point on first object (ellipse, arc of ellipse)
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
|
GeoIdPoint,Sketcher::start,GeoId1);
|
|
// Point on second object
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
|
GeoIdPoint,Sketcher::start,GeoId2);
|
|
|
|
// add constraint: Perpendicular-via-point
|
|
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('PerpendicularViaPoint',%d,%d,%d,%d))",
|
|
GeoId1, GeoId2 ,GeoIdPoint, Sketcher::start);
|
|
|
|
}
|
|
catch (const Base::Exception& e) {
|
|
Base::Console().Error("%s\n", e.what());
|
|
Gui::Command::abortCommand();
|
|
|
|
tryAutoRecompute(Obj);
|
|
return;
|
|
}
|
|
|
|
commitCommand();
|
|
tryAutoRecompute(Obj);
|
|
|
|
getSelection().clearSelection();
|
|
return;
|
|
|
|
}
|
|
|
|
openCommand("add perpendicular constraint");
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('Perpendicular',%d,%d)) ",
|
|
GeoId1,GeoId2);
|
|
commitCommand();
|
|
tryAutoRecompute(Obj);
|
|
|
|
getSelection().clearSelection();
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!strError.isEmpty()) strError.append(QString::fromLatin1("\n\n"));
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
strError+strBasicHelp);
|
|
return;
|
|
}
|
|
|
|
void CmdSketcherConstrainPerpendicular::applyConstraint(std::vector<SelIdPair> &selSeq, int seqIndex)
|
|
{
|
|
SketcherGui::ViewProviderSketch* sketchgui = static_cast<SketcherGui::ViewProviderSketch*>(getActiveGuiDocument()->getInEdit());
|
|
Sketcher::SketchObject* Obj = sketchgui->getSketchObject();
|
|
|
|
int GeoId1 = Constraint::GeoUndef, GeoId2 = Constraint::GeoUndef, GeoId3 = Constraint::GeoUndef;
|
|
Sketcher::PointPos PosId1 = Sketcher::none, PosId2 = Sketcher::none, PosId3 = Sketcher::none;
|
|
|
|
// check if the edge already has a Block constraint
|
|
if ( areBothPointsOrSegmentsFixed(Obj,GeoId1,GeoId2) ) {
|
|
showNoConstraintBetweenFixedGeometry();
|
|
return;
|
|
}
|
|
|
|
switch (seqIndex) {
|
|
case 0: // {SelEdge, SelEdgeOrAxis}
|
|
case 1: // {SelEdgeOrAxis, SelEdge}
|
|
case 2: // {SelEdge, SelExternalEdge}
|
|
case 3: // {SelExternalEdge, SelEdge}
|
|
{
|
|
GeoId1 = selSeq.at(0).GeoId; GeoId2 = selSeq.at(1).GeoId;
|
|
|
|
const Part::Geometry *geo1 = Obj->getGeometry(GeoId1);
|
|
const Part::Geometry *geo2 = Obj->getGeometry(GeoId2);
|
|
if (!geo1 || !geo2) {
|
|
return;
|
|
}
|
|
|
|
if (geo1->getTypeId() != Part::GeomLineSegment::getClassTypeId() &&
|
|
geo2->getTypeId() != Part::GeomLineSegment::getClassTypeId()) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("One of the selected edges should be a line."));
|
|
return;
|
|
}
|
|
|
|
if (geo1->getTypeId() == Part::GeomBSplineCurve::getClassTypeId() ||
|
|
geo2->getTypeId() == Part::GeomBSplineCurve::getClassTypeId()){
|
|
|
|
// unsupported until tangent to B-spline at any point implemented.
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Perpendicular to B-spline edge currently unsupported."));
|
|
return;
|
|
}
|
|
|
|
if (geo1->getTypeId() == Part::GeomLineSegment::getClassTypeId())
|
|
std::swap(GeoId1,GeoId2);
|
|
|
|
// GeoId2 is the line
|
|
geo1 = Obj->getGeometry(GeoId1);
|
|
geo2 = Obj->getGeometry(GeoId2);
|
|
|
|
if( geo1->getTypeId() == Part::GeomEllipse::getClassTypeId() ||
|
|
geo1->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId() ||
|
|
geo1->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId() ||
|
|
geo1->getTypeId() == Part::GeomArcOfParabola::getClassTypeId() ) {
|
|
|
|
Base::Vector3d center;
|
|
Base::Vector3d majdir;
|
|
Base::Vector3d focus;
|
|
double majord = 0;
|
|
double minord = 0;
|
|
double phi = 0;
|
|
|
|
if( geo1->getTypeId() == Part::GeomEllipse::getClassTypeId() ){
|
|
const Part::GeomEllipse *ellipse = static_cast<const Part::GeomEllipse *>(geo1);
|
|
|
|
center=ellipse->getCenter();
|
|
majord=ellipse->getMajorRadius();
|
|
minord=ellipse->getMinorRadius();
|
|
majdir=ellipse->getMajorAxisDir();
|
|
phi=atan2(majdir.y, majdir.x);
|
|
}
|
|
else if( geo1->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId() ){
|
|
const Part::GeomArcOfEllipse *aoe = static_cast<const Part::GeomArcOfEllipse *>(geo1);
|
|
|
|
center=aoe->getCenter();
|
|
majord=aoe->getMajorRadius();
|
|
minord=aoe->getMinorRadius();
|
|
majdir=aoe->getMajorAxisDir();
|
|
phi=atan2(majdir.y, majdir.x);
|
|
}
|
|
else if( geo1->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId() ){
|
|
const Part::GeomArcOfHyperbola *aoh = static_cast<const Part::GeomArcOfHyperbola *>(geo1);
|
|
|
|
center=aoh->getCenter();
|
|
majord=aoh->getMajorRadius();
|
|
minord=aoh->getMinorRadius();
|
|
majdir=aoh->getMajorAxisDir();
|
|
phi=atan2(majdir.y, majdir.x);
|
|
}
|
|
else if( geo1->getTypeId() == Part::GeomArcOfParabola::getClassTypeId() ){
|
|
const Part::GeomArcOfParabola *aop = static_cast<const Part::GeomArcOfParabola *>(geo1);
|
|
|
|
center=aop->getCenter();
|
|
focus=aop->getFocus();
|
|
}
|
|
|
|
const Part::GeomLineSegment *line = static_cast<const Part::GeomLineSegment *>(geo2);
|
|
|
|
Base::Vector3d point1=line->getStartPoint();
|
|
Base::Vector3d PoO;
|
|
|
|
if( geo1->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId() ) {
|
|
double df=sqrt(majord*majord+minord*minord);
|
|
Base::Vector3d direction=point1-(center+majdir*df); // towards the focus
|
|
double tapprox=atan2(direction.y,direction.x)-phi;
|
|
|
|
PoO = Base::Vector3d(center.x+majord*cosh(tapprox)*cos(phi)-minord*sinh(tapprox)*sin(phi),
|
|
center.y+majord*cosh(tapprox)*sin(phi)+minord*sinh(tapprox)*cos(phi), 0);
|
|
}
|
|
else if( geo1->getTypeId() == Part::GeomArcOfParabola::getClassTypeId() ) {
|
|
Base::Vector3d direction=point1-focus; // towards the focus
|
|
|
|
PoO = point1 + direction / 2;
|
|
}
|
|
else {
|
|
Base::Vector3d direction=point1-center;
|
|
double tapprox=atan2(direction.y,direction.x)-phi; // we approximate the eccentric anomaly by the polar
|
|
|
|
PoO = Base::Vector3d(center.x+majord*cos(tapprox)*cos(phi)-minord*sin(tapprox)*sin(phi),
|
|
center.y+majord*cos(tapprox)*sin(phi)+minord*sin(tapprox)*cos(phi), 0);
|
|
}
|
|
openCommand("add perpendicular constraint");
|
|
|
|
try {
|
|
// Add a point
|
|
Gui::cmdAppObjectArgs(Obj, "addGeometry(Part.Point(App.Vector(%f,%f,0)))",
|
|
PoO.x,PoO.y);
|
|
int GeoIdPoint = Obj->getHighestCurveIndex();
|
|
|
|
// Point on first object (ellipse, arc of ellipse)
|
|
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
|
GeoIdPoint,Sketcher::start,GeoId1);
|
|
// Point on second object
|
|
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
|
GeoIdPoint,Sketcher::start,GeoId2);
|
|
|
|
// add constraint: Perpendicular-via-point
|
|
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('PerpendicularViaPoint',%d,%d,%d,%d))",
|
|
GeoId1, GeoId2 ,GeoIdPoint, Sketcher::start);
|
|
|
|
commitCommand();
|
|
}
|
|
catch (const Base::Exception& e) {
|
|
Base::Console().Error("%s\n", e.what());
|
|
Gui::Command::abortCommand();
|
|
}
|
|
|
|
|
|
tryAutoRecompute(Obj);
|
|
|
|
getSelection().clearSelection();
|
|
return;
|
|
|
|
}
|
|
|
|
openCommand("add perpendicular constraint");
|
|
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Perpendicular',%d,%d)) ",
|
|
GeoId1,GeoId2);
|
|
commitCommand();
|
|
|
|
tryAutoRecompute(Obj);
|
|
return;
|
|
}
|
|
case 4: // {SelVertexOrRoot, SelEdge, SelEdgeOrAxis}
|
|
case 5: // {SelVertexOrRoot, SelEdgeOrAxis, SelEdge}
|
|
case 6: // {SelVertexOrRoot, SelEdge, SelExternalEdge}
|
|
case 7: // {SelVertexOrRoot, SelExternalEdge, SelEdge}
|
|
{
|
|
//let's sink the point to be GeoId3.
|
|
GeoId1 = selSeq.at(1).GeoId; GeoId2 = selSeq.at(2).GeoId; GeoId3 = selSeq.at(0).GeoId;
|
|
PosId3 = selSeq.at(0).PosId;
|
|
|
|
break;
|
|
}
|
|
case 8: // {SelEdge, SelVertexOrRoot, SelEdgeOrAxis}
|
|
case 9: // {SelEdgeOrAxis, SelVertexOrRoot, SelEdge}
|
|
case 10: // {SelEdge, SelVertexOrRoot, SelExternalEdge}
|
|
case 11: // {SelExternalEdge, SelVertexOrRoot, SelEdge}
|
|
{
|
|
//let's sink the point to be GeoId3.
|
|
GeoId1 = selSeq.at(0).GeoId; GeoId2 = selSeq.at(2).GeoId; GeoId3 = selSeq.at(1).GeoId;
|
|
PosId3 = selSeq.at(1).PosId;
|
|
|
|
break;
|
|
}
|
|
default:
|
|
return;
|
|
}
|
|
|
|
if (isEdge(GeoId1, PosId1) && isEdge(GeoId2, PosId2) && isVertex(GeoId3, PosId3)) {
|
|
|
|
openCommand("add perpendicular constraint");
|
|
|
|
try{
|
|
//add missing point-on-object constraints
|
|
if(! IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)){
|
|
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
|
GeoId3,PosId3,GeoId1);
|
|
};
|
|
|
|
if(! IsPointAlreadyOnCurve(GeoId2, GeoId3, PosId3, Obj)){
|
|
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
|
GeoId3,PosId3,GeoId2);
|
|
};
|
|
|
|
if(! IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)){//FIXME: it's a good idea to add a check if the sketch is solved
|
|
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
|
GeoId3,PosId3,GeoId1);
|
|
};
|
|
|
|
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('PerpendicularViaPoint',%d,%d,%d,%d)) ",
|
|
GeoId1,GeoId2,GeoId3,PosId3);
|
|
} catch (const Base::Exception& e) {
|
|
Base::Console().Error("%s\n", e.what());
|
|
QMessageBox::warning(Gui::getMainWindow(),
|
|
QObject::tr("Error"),
|
|
QString::fromLatin1(e.what()));
|
|
Gui::Command::abortCommand();
|
|
|
|
tryAutoRecompute(Obj);
|
|
return;
|
|
}
|
|
|
|
commitCommand();
|
|
tryAutoRecompute(Obj);
|
|
|
|
getSelection().clearSelection();
|
|
|
|
return;
|
|
|
|
};
|
|
}
|
|
|
|
// ======================================================================================
|
|
|
|
class CmdSketcherConstrainTangent : public CmdSketcherConstraint
|
|
{
|
|
public:
|
|
CmdSketcherConstrainTangent();
|
|
virtual ~CmdSketcherConstrainTangent(){}
|
|
virtual const char* className() const
|
|
{ return "CmdSketcherConstrainTangent"; }
|
|
protected:
|
|
virtual void activated(int iMsg);
|
|
virtual void applyConstraint(std::vector<SelIdPair> &selSeq, int seqIndex);
|
|
};
|
|
|
|
CmdSketcherConstrainTangent::CmdSketcherConstrainTangent()
|
|
:CmdSketcherConstraint("Sketcher_ConstrainTangent")
|
|
{
|
|
sAppModule = "Sketcher";
|
|
sGroup = QT_TR_NOOP("Sketcher");
|
|
sMenuText = QT_TR_NOOP("Constrain tangent");
|
|
sToolTipText = QT_TR_NOOP("Create a tangent constraint between two entities");
|
|
sWhatsThis = "Sketcher_ConstrainTangent";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "Constraint_Tangent";
|
|
sAccel = "T";
|
|
eType = ForEdit;
|
|
|
|
allowedSelSequences = {{SelEdge, SelEdgeOrAxis}, {SelEdgeOrAxis, SelEdge},
|
|
{SelEdge, SelExternalEdge}, {SelExternalEdge, SelEdge},/* Two Curves */
|
|
{SelVertexOrRoot, SelEdge, SelEdgeOrAxis},
|
|
{SelVertexOrRoot, SelEdgeOrAxis, SelEdge},
|
|
{SelVertexOrRoot, SelEdge, SelExternalEdge},
|
|
{SelVertexOrRoot, SelExternalEdge, SelEdge},
|
|
{SelEdge, SelVertexOrRoot, SelEdgeOrAxis},
|
|
{SelEdgeOrAxis, SelVertexOrRoot, SelEdge},
|
|
{SelEdge, SelVertexOrRoot, SelExternalEdge},
|
|
{SelExternalEdge, SelVertexOrRoot, SelEdge}, /* Two Curves and a Point */
|
|
{SelVertexOrRoot, SelVertex} /*Two Endpoints*/ /*No Place for One Endpoint and One Curve*/};
|
|
constraintCursor = cursor_genericconstraint;
|
|
}
|
|
|
|
void CmdSketcherConstrainTangent::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
QString strBasicHelp =
|
|
QObject::tr(
|
|
"There are a number of ways this constraint can be applied.\n\n"
|
|
"Accepted combinations: two curves; an endpoint and a curve; two endpoints; two curves and a point.",
|
|
/*disambig.:*/ "tangent constraint");
|
|
|
|
QString strError;
|
|
// get the selection
|
|
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
|
|
|
|
// only one sketch with its subelements are allowed to be selected
|
|
if (selection.size() != 1 || !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
|
|
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
|
bool constraintMode = hGrp->GetBool("ContinuousConstraintMode", true);
|
|
|
|
if (constraintMode) {
|
|
ActivateHandler(getActiveGuiDocument(),
|
|
new DrawSketchHandlerGenConstraint(constraintCursor, this));
|
|
getSelection().clearSelection();
|
|
} else {
|
|
strError = QObject::tr("Select some geometry from the sketch.", "tangent constraint");
|
|
if (!strError.isEmpty()) strError.append(QString::fromLatin1("\n\n"));
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
strError+strBasicHelp);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// get the needed lists and objects
|
|
const std::vector<std::string> &SubNames = selection[0].getSubNames();
|
|
Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
|
|
|
|
if (SubNames.size() != 2 && SubNames.size() != 3){
|
|
strError = QObject::tr("Wrong number of selected objects!","tangent constraint");
|
|
if (!strError.isEmpty()) strError.append(QString::fromLatin1("\n\n"));
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
strError+strBasicHelp);
|
|
return;
|
|
|
|
}
|
|
|
|
int GeoId1, GeoId2, GeoId3;
|
|
Sketcher::PointPos PosId1, PosId2, PosId3;
|
|
getIdsFromName(SubNames[0], Obj, GeoId1, PosId1);
|
|
getIdsFromName(SubNames[1], Obj, GeoId2, PosId2);
|
|
|
|
if (areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2)){ //checkBothExternal displays error message
|
|
showNoConstraintBetweenFixedGeometry();
|
|
return;
|
|
}
|
|
if (SubNames.size() == 3) { //tangent via point
|
|
getIdsFromName(SubNames[2], Obj, GeoId3, PosId3);
|
|
//let's sink the point to be GeoId3. We want to keep the order the two curves have been selected in.
|
|
if ( isVertex(GeoId1, PosId1) ){
|
|
std::swap(GeoId1,GeoId2);
|
|
std::swap(PosId1,PosId2);
|
|
};
|
|
if ( isVertex(GeoId2, PosId2) ){
|
|
std::swap(GeoId2,GeoId3);
|
|
std::swap(PosId2,PosId3);
|
|
};
|
|
|
|
if (isEdge(GeoId1, PosId1) && isEdge(GeoId2, PosId2) && isVertex(GeoId3, PosId3)) {
|
|
|
|
openCommand("add tangent constraint");
|
|
|
|
try{
|
|
//add missing point-on-object constraints
|
|
if(! IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)){
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
|
GeoId3,PosId3,GeoId1);
|
|
};
|
|
|
|
if(! IsPointAlreadyOnCurve(GeoId2, GeoId3, PosId3, Obj)){
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
|
GeoId3,PosId3,GeoId2);
|
|
};
|
|
|
|
if(! IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)){//FIXME: it's a good idea to add a check if the sketch is solved
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
|
GeoId3,PosId3,GeoId1);
|
|
};
|
|
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('TangentViaPoint',%d,%d,%d,%d)) ",
|
|
GeoId1,GeoId2,GeoId3,PosId3);
|
|
} catch (const Base::Exception& e) {
|
|
Base::Console().Error("%s\n", e.what());
|
|
QMessageBox::warning(Gui::getMainWindow(),
|
|
QObject::tr("Error"),
|
|
QString::fromLatin1(e.what()));
|
|
Gui::Command::abortCommand();
|
|
tryAutoRecompute(Obj);
|
|
return;
|
|
}
|
|
|
|
commitCommand();
|
|
tryAutoRecompute(Obj);
|
|
|
|
getSelection().clearSelection();
|
|
|
|
return;
|
|
|
|
};
|
|
strError = QObject::tr("With 3 objects, there must be 2 curves and 1 point.", "tangent constraint");
|
|
|
|
} else if (SubNames.size() == 2) {
|
|
|
|
if (isVertex(GeoId1,PosId1) && isVertex(GeoId2,PosId2)) { // endpoint-to-endpoint tangency
|
|
|
|
if (isSimpleVertex(Obj, GeoId1, PosId1) ||
|
|
isSimpleVertex(Obj, GeoId2, PosId2)) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Cannot add a tangency constraint at an unconnected point!"));
|
|
return;
|
|
}
|
|
|
|
openCommand("add tangent constraint");
|
|
doEndpointTangency(Obj, selection[0], GeoId1, GeoId2, PosId1, PosId2);
|
|
commitCommand();
|
|
tryAutoRecompute(Obj);
|
|
|
|
getSelection().clearSelection();
|
|
return;
|
|
}
|
|
else if ((isVertex(GeoId1,PosId1) && isEdge(GeoId2,PosId2)) ||
|
|
(isEdge(GeoId1,PosId1) && isVertex(GeoId2,PosId2))) { // endpoint-to-curve tangency
|
|
if (isVertex(GeoId2,PosId2)) {
|
|
std::swap(GeoId1,GeoId2);
|
|
std::swap(PosId1,PosId2);
|
|
}
|
|
|
|
if (isSimpleVertex(Obj, GeoId1, PosId1)) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Cannot add a tangency constraint at an unconnected point!"));
|
|
return;
|
|
}
|
|
|
|
const Part::Geometry *geom2 = Obj->getGeometry(GeoId2);
|
|
|
|
if( geom2 && geom2->getTypeId() == Part::GeomBSplineCurve::getClassTypeId() ){
|
|
// unsupported until tangent to B-spline at any point implemented.
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Tangency to B-spline edge currently unsupported."));
|
|
return;
|
|
}
|
|
|
|
openCommand("add tangent constraint");
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('Tangent',%d,%d,%d)) ",
|
|
GeoId1,PosId1,GeoId2);
|
|
commitCommand();
|
|
tryAutoRecompute(Obj);
|
|
|
|
getSelection().clearSelection();
|
|
return;
|
|
}
|
|
else if (isEdge(GeoId1,PosId1) && isEdge(GeoId2,PosId2)) { // simple tangency between GeoId1 and GeoId2
|
|
|
|
const Part::Geometry *geom1 = Obj->getGeometry(GeoId1);
|
|
const Part::Geometry *geom2 = Obj->getGeometry(GeoId2);
|
|
|
|
if( geom1 && geom2 &&
|
|
( geom1->getTypeId() == Part::GeomBSplineCurve::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomBSplineCurve::getClassTypeId() )){
|
|
|
|
// unsupported until tangent to B-spline at any point implemented.
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Tangency to B-spline edge currently unsupported."));
|
|
return;
|
|
}
|
|
|
|
// check if there is a coincidence constraint on GeoId1, GeoId2
|
|
const std::vector< Constraint * > &cvals = Obj->Constraints.getValues();
|
|
|
|
for (std::vector<Constraint *>::const_iterator it = cvals.begin(); it != cvals.end(); ++it) {
|
|
if( (*it)->Type == Sketcher::Coincident &&
|
|
(((*it)->First == GeoId1 && (*it)->Second == GeoId2) ||
|
|
((*it)->Second == GeoId1 && (*it)->First == GeoId2)) ) {
|
|
|
|
// save values because 'doEndpointTangency' changes the
|
|
// constraint property and thus invalidates this iterator
|
|
int first = (*it)->First;
|
|
int firstpos = static_cast<int>((*it)->FirstPos);
|
|
|
|
Gui::Command::openCommand("swap coincident+tangency with ptp tangency");
|
|
|
|
doEndpointTangency(Obj, selection[0], (*it)->First, (*it)->Second, (*it)->FirstPos, (*it)->SecondPos);
|
|
|
|
Gui::cmdAppObjectArgs(Obj, "delConstraintOnPoint(%i,%i)", first, firstpos);
|
|
|
|
commitCommand();
|
|
tryAutoRecomputeIfNotSolve(Obj);
|
|
|
|
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher/General");
|
|
|
|
if(hGrp->GetBool("NotifyConstraintSubstitutions", true)) {
|
|
QMessageBox::information(Gui::getMainWindow(), QObject::tr("Constraint Substitution"),
|
|
QObject::tr("Endpoint to endpoint tangency was applied. The coincident constraint was deleted."));
|
|
}
|
|
getSelection().clearSelection();
|
|
return;
|
|
}
|
|
}
|
|
|
|
if( geom1 && geom2 &&
|
|
( geom1->getTypeId() == Part::GeomEllipse::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomEllipse::getClassTypeId() )){
|
|
|
|
if(geom1->getTypeId() != Part::GeomEllipse::getClassTypeId())
|
|
std::swap(GeoId1,GeoId2);
|
|
|
|
// GeoId1 is the ellipse
|
|
geom1 = Obj->getGeometry(GeoId1);
|
|
geom2 = Obj->getGeometry(GeoId2);
|
|
|
|
if( geom2->getTypeId() == Part::GeomEllipse::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomCircle::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomArcOfCircle::getClassTypeId() ) {
|
|
|
|
Gui::Command::openCommand("add tangent constraint point");
|
|
makeTangentToEllipseviaNewPoint(Obj,static_cast<const Part::GeomEllipse *>(geom1), geom2,
|
|
GeoId1, GeoId2);
|
|
getSelection().clearSelection();
|
|
return;
|
|
}
|
|
else if( geom2->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId() ) {
|
|
Gui::Command::openCommand("add tangent constraint point");
|
|
makeTangentToArcOfHyperbolaviaNewPoint(Obj, static_cast<const Part::GeomArcOfHyperbola *>(geom2),
|
|
geom1, GeoId2, GeoId1);
|
|
getSelection().clearSelection();
|
|
return;
|
|
}
|
|
else if( geom2->getTypeId() == Part::GeomArcOfParabola::getClassTypeId() ) {
|
|
Gui::Command::openCommand("add tangent constraint point");
|
|
makeTangentToArcOfParabolaviaNewPoint(Obj,static_cast<const Part::GeomArcOfParabola *>(geom2),
|
|
geom1, GeoId2, GeoId1);
|
|
getSelection().clearSelection();
|
|
return;
|
|
}
|
|
}
|
|
else if( geom1 && geom2 &&
|
|
( geom1->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId() )){
|
|
|
|
if(geom1->getTypeId() != Part::GeomArcOfEllipse::getClassTypeId())
|
|
std::swap(GeoId1,GeoId2);
|
|
|
|
// GeoId1 is the arc of ellipse
|
|
geom1 = Obj->getGeometry(GeoId1);
|
|
geom2 = Obj->getGeometry(GeoId2);
|
|
|
|
if( geom2->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomCircle::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomArcOfCircle::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomLineSegment::getClassTypeId() ) {
|
|
|
|
Gui::Command::openCommand("add tangent constraint point");
|
|
makeTangentToArcOfEllipseviaNewPoint(Obj,
|
|
static_cast<const Part::GeomArcOfEllipse *>(geom1), geom2, GeoId1, GeoId2);
|
|
|
|
getSelection().clearSelection();
|
|
return;
|
|
}
|
|
else if( geom2->getTypeId() == Part::GeomArcOfParabola::getClassTypeId() ) {
|
|
Gui::Command::openCommand("add tangent constraint point");
|
|
makeTangentToArcOfParabolaviaNewPoint(Obj,
|
|
static_cast<const Part::GeomArcOfParabola *>(geom2),
|
|
geom1, GeoId2, GeoId1);
|
|
getSelection().clearSelection();
|
|
return;
|
|
}
|
|
}
|
|
else if( geom1 && geom2 &&
|
|
( geom1->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId() )){
|
|
|
|
if(geom1->getTypeId() != Part::GeomArcOfHyperbola::getClassTypeId())
|
|
std::swap(GeoId1,GeoId2);
|
|
|
|
// GeoId1 is the arc of hyperbola
|
|
geom1 = Obj->getGeometry(GeoId1);
|
|
geom2 = Obj->getGeometry(GeoId2);
|
|
|
|
if( geom2->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomCircle::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomArcOfCircle::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomLineSegment::getClassTypeId() ) {
|
|
|
|
Gui::Command::openCommand("add tangent constraint point");
|
|
makeTangentToArcOfHyperbolaviaNewPoint(Obj,
|
|
static_cast<const Part::GeomArcOfHyperbola *>(geom1),
|
|
geom2, GeoId1, GeoId2);
|
|
getSelection().clearSelection();
|
|
return;
|
|
}
|
|
else if( geom2->getTypeId() == Part::GeomArcOfParabola::getClassTypeId() ) {
|
|
Gui::Command::openCommand("add tangent constraint point");
|
|
makeTangentToArcOfParabolaviaNewPoint(Obj,
|
|
static_cast<const Part::GeomArcOfParabola *>(geom2),
|
|
geom1, GeoId2, GeoId1);
|
|
getSelection().clearSelection();
|
|
return;
|
|
}
|
|
|
|
}
|
|
else if( geom1 && geom2 &&
|
|
( geom1->getTypeId() == Part::GeomArcOfParabola::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomArcOfParabola::getClassTypeId() )){
|
|
|
|
if(geom1->getTypeId() != Part::GeomArcOfParabola::getClassTypeId())
|
|
std::swap(GeoId1,GeoId2);
|
|
|
|
// GeoId1 is the arc of hyperbola
|
|
geom1 = Obj->getGeometry(GeoId1);
|
|
geom2 = Obj->getGeometry(GeoId2);
|
|
|
|
if (geom2->getTypeId() == Part::GeomArcOfParabola::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomCircle::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomArcOfCircle::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomLineSegment::getClassTypeId() ) {
|
|
|
|
Gui::Command::openCommand("add tangent constraint point");
|
|
makeTangentToArcOfParabolaviaNewPoint(Obj, static_cast<const Part::GeomArcOfParabola *>(geom1),
|
|
geom2, GeoId1, GeoId2);
|
|
getSelection().clearSelection();
|
|
return;
|
|
}
|
|
}
|
|
|
|
openCommand("add tangent constraint");
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),
|
|
"addConstraint(Sketcher.Constraint('Tangent',%d,%d)) ",
|
|
GeoId1,GeoId2);
|
|
commitCommand();
|
|
tryAutoRecompute(Obj);
|
|
|
|
getSelection().clearSelection();
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
if (!strError.isEmpty()) strError.append(QString::fromLatin1("\n\n"));
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
strError+strBasicHelp);
|
|
return;
|
|
}
|
|
|
|
void CmdSketcherConstrainTangent::applyConstraint(std::vector<SelIdPair> &selSeq, int seqIndex)
|
|
{
|
|
SketcherGui::ViewProviderSketch* sketchgui = static_cast<SketcherGui::ViewProviderSketch*>(getActiveGuiDocument()->getInEdit());
|
|
Sketcher::SketchObject* Obj = sketchgui->getSketchObject();
|
|
QString strError;
|
|
|
|
int GeoId1 = Constraint::GeoUndef, GeoId2 = Constraint::GeoUndef, GeoId3 = Constraint::GeoUndef;
|
|
Sketcher::PointPos PosId1 = Sketcher::none, PosId2 = Sketcher::none, PosId3 = Sketcher::none;
|
|
|
|
// check if the edge already has a Block constraint
|
|
if ( areBothPointsOrSegmentsFixed(Obj,GeoId1,GeoId2) ) {
|
|
showNoConstraintBetweenFixedGeometry();
|
|
return;
|
|
}
|
|
|
|
switch (seqIndex) {
|
|
case 0: // {SelEdge, SelEdgeOrAxis}
|
|
case 1: // {SelEdgeOrAxis, SelEdge}
|
|
case 2: // {SelEdge, SelExternalEdge}
|
|
case 3: // {SelExternalEdge, SelEdge}
|
|
{
|
|
GeoId1 = selSeq.at(0).GeoId; GeoId2 = selSeq.at(1).GeoId;
|
|
|
|
const Part::Geometry *geom1 = Obj->getGeometry(GeoId1);
|
|
const Part::Geometry *geom2 = Obj->getGeometry(GeoId2);
|
|
|
|
if( geom1 && geom2 &&
|
|
( geom1->getTypeId() == Part::GeomBSplineCurve::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomBSplineCurve::getClassTypeId() )){
|
|
|
|
// unsupported until tangent to B-spline at any point implemented.
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Tangency to B-spline edge currently unsupported."));
|
|
return;
|
|
}
|
|
|
|
|
|
if( geom1 && geom2 &&
|
|
( geom1->getTypeId() == Part::GeomEllipse::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomEllipse::getClassTypeId() )){
|
|
|
|
if(geom1->getTypeId() != Part::GeomEllipse::getClassTypeId())
|
|
std::swap(GeoId1,GeoId2);
|
|
|
|
// GeoId1 is the ellipse
|
|
geom1 = Obj->getGeometry(GeoId1);
|
|
geom2 = Obj->getGeometry(GeoId2);
|
|
|
|
if( geom2->getTypeId() == Part::GeomEllipse::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomCircle::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomArcOfCircle::getClassTypeId() ) {
|
|
|
|
Gui::Command::openCommand("add tangent constraint point");
|
|
makeTangentToEllipseviaNewPoint(Obj, static_cast<const Part::GeomEllipse *>(geom1),
|
|
geom2, GeoId1, GeoId2);
|
|
getSelection().clearSelection();
|
|
return;
|
|
}
|
|
else if( geom2->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId() ) {
|
|
Gui::Command::openCommand("add tangent constraint point");
|
|
makeTangentToArcOfHyperbolaviaNewPoint(Obj, static_cast<const Part::GeomArcOfHyperbola *>(geom2),
|
|
geom1, GeoId2, GeoId1);
|
|
getSelection().clearSelection();
|
|
return;
|
|
}
|
|
else if( geom2->getTypeId() == Part::GeomArcOfParabola::getClassTypeId() ) {
|
|
Gui::Command::openCommand("add tangent constraint point");
|
|
makeTangentToArcOfParabolaviaNewPoint(Obj, static_cast<const Part::GeomArcOfParabola *>(geom2),
|
|
geom1, GeoId2, GeoId1);
|
|
getSelection().clearSelection();
|
|
return;
|
|
}
|
|
}
|
|
else if( geom1 && geom2 &&
|
|
( geom1->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId() )){
|
|
|
|
if(geom1->getTypeId() != Part::GeomArcOfHyperbola::getClassTypeId())
|
|
std::swap(GeoId1,GeoId2);
|
|
|
|
// GeoId1 is the arc of hyperbola
|
|
geom1 = Obj->getGeometry(GeoId1);
|
|
geom2 = Obj->getGeometry(GeoId2);
|
|
|
|
if( geom2->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomCircle::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomArcOfCircle::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomLineSegment::getClassTypeId() ) {
|
|
|
|
Gui::Command::openCommand("add tangent constraint point");
|
|
makeTangentToArcOfHyperbolaviaNewPoint(Obj, static_cast<const Part::GeomArcOfHyperbola *>(geom1),
|
|
geom2, GeoId1, GeoId2);
|
|
getSelection().clearSelection();
|
|
return;
|
|
}
|
|
else if( geom2->getTypeId() == Part::GeomArcOfParabola::getClassTypeId() ) {
|
|
Gui::Command::openCommand("add tangent constraint point");
|
|
makeTangentToArcOfParabolaviaNewPoint(Obj, static_cast<const Part::GeomArcOfParabola *>(geom2),
|
|
geom1, GeoId2, GeoId1);
|
|
getSelection().clearSelection();
|
|
return;
|
|
}
|
|
|
|
}
|
|
else if( geom1 && geom2 &&
|
|
( geom1->getTypeId() == Part::GeomArcOfParabola::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomArcOfParabola::getClassTypeId() )){
|
|
|
|
if(geom1->getTypeId() != Part::GeomArcOfParabola::getClassTypeId())
|
|
std::swap(GeoId1,GeoId2);
|
|
|
|
// GeoId1 is the arc of hyperbola
|
|
geom1 = Obj->getGeometry(GeoId1);
|
|
geom2 = Obj->getGeometry(GeoId2);
|
|
|
|
if (geom2->getTypeId() == Part::GeomArcOfParabola::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomCircle::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomArcOfCircle::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomLineSegment::getClassTypeId() ) {
|
|
|
|
Gui::Command::openCommand("add tangent constraint point");
|
|
makeTangentToArcOfParabolaviaNewPoint(Obj, static_cast<const Part::GeomArcOfParabola *>(geom1),
|
|
geom2, GeoId1, GeoId2);
|
|
getSelection().clearSelection();
|
|
return;
|
|
}
|
|
}
|
|
|
|
openCommand("add tangent constraint");
|
|
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Tangent',%d,%d)) ",
|
|
GeoId1,GeoId2);
|
|
commitCommand();
|
|
tryAutoRecompute(Obj);
|
|
|
|
return;
|
|
}
|
|
case 4: // {SelVertexOrRoot, SelEdge, SelEdgeOrAxis}
|
|
case 5: // {SelVertexOrRoot, SelEdgeOrAxis, SelEdge}
|
|
case 6: // {SelVertexOrRoot, SelEdge, SelExternalEdge}
|
|
case 7: // {SelVertexOrRoot, SelExternalEdge, SelEdge}
|
|
{
|
|
//let's sink the point to be GeoId3.
|
|
GeoId1 = selSeq.at(1).GeoId; GeoId2 = selSeq.at(2).GeoId; GeoId3 = selSeq.at(0).GeoId;
|
|
PosId3 = selSeq.at(0).PosId;
|
|
|
|
break;
|
|
}
|
|
case 8: // {SelEdge, SelVertexOrRoot, SelEdgeOrAxis}
|
|
case 9: // {SelEdgeOrAxis, SelVertexOrRoot, SelEdge}
|
|
case 10: // {SelEdge, SelVertexOrRoot, SelExternalEdge}
|
|
case 11: // {SelExternalEdge, SelVertexOrRoot, SelEdge}
|
|
{
|
|
//let's sink the point to be GeoId3.
|
|
GeoId1 = selSeq.at(0).GeoId; GeoId2 = selSeq.at(2).GeoId; GeoId3 = selSeq.at(1).GeoId;
|
|
PosId3 = selSeq.at(1).PosId;
|
|
|
|
break;
|
|
}
|
|
case 12: // {SelVertexOrRoot, SelVertex}
|
|
{
|
|
// Different notation than the previous places
|
|
GeoId1 = selSeq.at(0).GeoId; GeoId2 = selSeq.at(1).GeoId;
|
|
PosId1 = selSeq.at(0).PosId; PosId2 = selSeq.at(1).PosId;
|
|
|
|
if (isSimpleVertex(Obj, GeoId1, PosId1) ||
|
|
isSimpleVertex(Obj, GeoId2, PosId2)) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Cannot add a tangency constraint at an unconnected point!"));
|
|
return;
|
|
}
|
|
|
|
// This code supports simple B-spline endpoint tangency to any other geometric curve
|
|
const Part::Geometry *geom1 = Obj->getGeometry(GeoId1);
|
|
const Part::Geometry *geom2 = Obj->getGeometry(GeoId2);
|
|
|
|
if( geom1 && geom2 &&
|
|
( geom1->getTypeId() == Part::GeomBSplineCurve::getClassTypeId() ||
|
|
geom2->getTypeId() == Part::GeomBSplineCurve::getClassTypeId() )){
|
|
|
|
if(geom1->getTypeId() != Part::GeomBSplineCurve::getClassTypeId()) {
|
|
std::swap(GeoId1,GeoId2);
|
|
std::swap(PosId1,PosId2);
|
|
}
|
|
// GeoId1 is the B-spline now
|
|
} // end of code supports simple B-spline endpoint tangency
|
|
|
|
openCommand("add tangent constraint");
|
|
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Tangent',%d,%d,%d,%d)) ",
|
|
GeoId1,PosId1,GeoId2,PosId2);
|
|
commitCommand();
|
|
tryAutoRecompute(Obj);
|
|
|
|
getSelection().clearSelection();
|
|
return;
|
|
}
|
|
default:
|
|
return;
|
|
}
|
|
|
|
if (isEdge(GeoId1, PosId1) && isEdge(GeoId2, PosId2) && isVertex(GeoId3, PosId3)) {
|
|
|
|
openCommand("add tangent constraint");
|
|
|
|
try{
|
|
//add missing point-on-object constraints
|
|
if(! IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)){
|
|
Gui::cmdAppObjectArgs(Obj,"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
|
GeoId3,PosId3,GeoId1);
|
|
};
|
|
|
|
if(! IsPointAlreadyOnCurve(GeoId2, GeoId3, PosId3, Obj)){
|
|
Gui::cmdAppObjectArgs(Obj,"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
|
GeoId3,PosId3,GeoId2);
|
|
};
|
|
|
|
if(! IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)){//FIXME: it's a good idea to add a check if the sketch is solved
|
|
Gui::cmdAppObjectArgs(Obj,"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
|
GeoId3,PosId3,GeoId1);
|
|
};
|
|
|
|
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('TangentViaPoint',%d,%d,%d,%d)) ",
|
|
GeoId1,GeoId2,GeoId3,PosId3);
|
|
} catch (const Base::Exception& e) {
|
|
Base::Console().Error("%s\n", e.what());
|
|
QMessageBox::warning(Gui::getMainWindow(),
|
|
QObject::tr("Error"),
|
|
QString::fromLatin1(e.what()));
|
|
Gui::Command::abortCommand();
|
|
|
|
tryAutoRecompute(Obj);
|
|
return;
|
|
}
|
|
|
|
commitCommand();
|
|
tryAutoRecompute(Obj);
|
|
|
|
getSelection().clearSelection();
|
|
|
|
return;
|
|
};
|
|
}
|
|
|
|
// ======================================================================================
|
|
|
|
class CmdSketcherConstrainRadius : public CmdSketcherConstraint
|
|
{
|
|
public:
|
|
CmdSketcherConstrainRadius();
|
|
virtual ~CmdSketcherConstrainRadius(){}
|
|
virtual void updateAction(int mode);
|
|
virtual const char* className() const
|
|
{ return "CmdSketcherConstrainRadius"; }
|
|
protected:
|
|
virtual void activated(int iMsg);
|
|
virtual void applyConstraint(std::vector<SelIdPair> &selSeq, int seqIndex);
|
|
};
|
|
|
|
CmdSketcherConstrainRadius::CmdSketcherConstrainRadius()
|
|
:CmdSketcherConstraint("Sketcher_ConstrainRadius")
|
|
{
|
|
sAppModule = "Sketcher";
|
|
sGroup = QT_TR_NOOP("Sketcher");
|
|
sMenuText = QT_TR_NOOP("Constrain radius");
|
|
sToolTipText = QT_TR_NOOP("Fix the radius of a circle or an arc");
|
|
sWhatsThis = "Sketcher_ConstrainRadius";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "Constraint_Radius";
|
|
sAccel = "";
|
|
eType = ForEdit;
|
|
|
|
allowedSelSequences = {{SelEdge}, {SelExternalEdge}};
|
|
constraintCursor = cursor_genericconstraint;
|
|
}
|
|
|
|
void CmdSketcherConstrainRadius::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
// get the selection
|
|
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
|
|
|
|
// only one sketch with its subelements are allowed to be selected
|
|
if (selection.size() != 1 || !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
|
|
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
|
bool constraintMode = hGrp->GetBool("ContinuousConstraintMode", true);
|
|
|
|
if (constraintMode) {
|
|
ActivateHandler(getActiveGuiDocument(),
|
|
new DrawSketchHandlerGenConstraint(constraintCursor, this));
|
|
getSelection().clearSelection();
|
|
} else {
|
|
// TODO: Get the exact message from git history and put it here
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select the right things from the sketch."));
|
|
}
|
|
return;
|
|
}
|
|
|
|
// get the needed lists and objects
|
|
const std::vector<std::string> &SubNames = selection[0].getSubNames();
|
|
Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
|
|
|
|
if (SubNames.empty()) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select one or more arcs or circles from the sketch."));
|
|
return;
|
|
}
|
|
|
|
// check for which selected geometry the constraint can be applied
|
|
std::vector< std::pair<int, double> > geoIdRadiusMap;
|
|
std::vector< std::pair<int, double> > externalGeoIdRadiusMap;
|
|
|
|
for (std::vector<std::string>::const_iterator it = SubNames.begin(); it != SubNames.end(); ++it) {
|
|
bool issegmentfixed = false;
|
|
int GeoId;
|
|
|
|
if (it->size() > 4 && it->substr(0,4) == "Edge") {
|
|
GeoId = std::atoi(it->substr(4,4000).c_str()) - 1;
|
|
issegmentfixed = isPointOrSegmentFixed(Obj,GeoId);
|
|
}
|
|
else if (it->size() > 4 && it->substr(0,12) == "ExternalEdge") {
|
|
GeoId = -std::atoi(it->substr(12,4000).c_str()) - 2;
|
|
issegmentfixed = true;
|
|
}
|
|
else
|
|
continue;
|
|
|
|
const Part::Geometry *geom = Obj->getGeometry(GeoId);
|
|
|
|
if (geom && geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
|
|
const Part::GeomArcOfCircle *arc = static_cast<const Part::GeomArcOfCircle *>(geom);
|
|
double radius = arc->getRadius();
|
|
|
|
if(issegmentfixed) {
|
|
externalGeoIdRadiusMap.push_back(std::make_pair(GeoId, radius));
|
|
}
|
|
else {
|
|
geoIdRadiusMap.push_back(std::make_pair(GeoId, radius));
|
|
}
|
|
}
|
|
else if (geom && geom->getTypeId() == Part::GeomCircle::getClassTypeId()) {
|
|
const Part::GeomCircle *circle = static_cast<const Part::GeomCircle *>(geom);
|
|
double radius = circle->getRadius();
|
|
|
|
if(issegmentfixed) {
|
|
externalGeoIdRadiusMap.push_back(std::make_pair(GeoId, radius));
|
|
}
|
|
else {
|
|
geoIdRadiusMap.push_back(std::make_pair(GeoId, radius));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (geoIdRadiusMap.empty() && externalGeoIdRadiusMap.empty()) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select one or more arcs or circles from the sketch."));
|
|
}
|
|
|
|
bool commitNeeded=false;
|
|
bool updateNeeded=false;
|
|
bool commandopened=false;
|
|
|
|
if(!externalGeoIdRadiusMap.empty()) {
|
|
// Create the non-driving radius constraints now
|
|
openCommand("Add radius constraint");
|
|
commandopened=true;
|
|
unsigned int constrSize = 0;
|
|
|
|
for (std::vector< std::pair<int, double> >::iterator it = externalGeoIdRadiusMap.begin(); it != externalGeoIdRadiusMap.end(); ++it) {
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),"addConstraint(Sketcher.Constraint('Radius',%d,%f)) ",
|
|
it->first,it->second);
|
|
|
|
const std::vector<Sketcher::Constraint *> &ConStr = Obj->Constraints.getValues();
|
|
|
|
constrSize=ConStr.size();
|
|
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),"setDriving(%i,%s)", constrSize-1,"False");
|
|
}
|
|
|
|
const std::vector<Sketcher::Constraint *> &ConStr = Obj->Constraints.getValues();
|
|
|
|
std::size_t indexConstr = constrSize - externalGeoIdRadiusMap.size();
|
|
|
|
// Guess some reasonable distance for placing the datum text
|
|
Gui::Document *doc = getActiveGuiDocument();
|
|
float sf = 1.f;
|
|
if (doc && doc->getInEdit() && doc->getInEdit()->isDerivedFrom(SketcherGui::ViewProviderSketch::getClassTypeId())) {
|
|
SketcherGui::ViewProviderSketch *vp = static_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit());
|
|
sf = vp->getScaleFactor();
|
|
|
|
for (std::size_t i=0; i<externalGeoIdRadiusMap.size();i++) {
|
|
Sketcher::Constraint *constr = ConStr[indexConstr + i];
|
|
constr->LabelDistance = 2. * sf;
|
|
}
|
|
vp->draw(false,false); // Redraw
|
|
}
|
|
|
|
commitNeeded=true;
|
|
updateNeeded=true;
|
|
}
|
|
|
|
if(!geoIdRadiusMap.empty())
|
|
{
|
|
bool constrainEqual = false;
|
|
if (geoIdRadiusMap.size() > 1 && constraintCreationMode==Driving) {
|
|
int ret = QMessageBox::question(Gui::getMainWindow(), QObject::tr("Constrain equal"),
|
|
QObject::tr("Do you want to share the same radius for all selected elements?"),
|
|
QMessageBox::Yes, QMessageBox::No, QMessageBox::Cancel);
|
|
// use an equality constraint
|
|
if (ret == QMessageBox::Yes) {
|
|
constrainEqual = true;
|
|
}
|
|
else if (ret == QMessageBox::Cancel) {
|
|
// do nothing
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (constrainEqual) {
|
|
// Create the one radius constraint now
|
|
int refGeoId = geoIdRadiusMap.front().first;
|
|
double radius = geoIdRadiusMap.front().second;
|
|
|
|
if(!commandopened)
|
|
openCommand("Add radius constraint");
|
|
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('Radius',%d,%f)) ",
|
|
refGeoId,radius);
|
|
|
|
// Add the equality constraints
|
|
for (std::vector< std::pair<int, double> >::iterator it = geoIdRadiusMap.begin()+1; it != geoIdRadiusMap.end(); ++it) {
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),
|
|
"addConstraint(Sketcher.Constraint('Equal',%d,%d)) ",
|
|
refGeoId,it->first);
|
|
}
|
|
}
|
|
else {
|
|
// Create the radius constraints now
|
|
if(!commandopened)
|
|
openCommand("Add radius constraint");
|
|
for (std::vector< std::pair<int, double> >::iterator it = geoIdRadiusMap.begin(); it != geoIdRadiusMap.end(); ++it) {
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('Radius',%d,%f)) ",
|
|
it->first,it->second);
|
|
|
|
if (constraintCreationMode==Reference) {
|
|
const std::vector<Sketcher::Constraint *> &ConStr = Obj->Constraints.getValues();
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "setDriving(%i,%s)", ConStr.size()-1,"False");
|
|
}
|
|
}
|
|
}
|
|
|
|
const std::vector<Sketcher::Constraint *> &ConStr = Obj->Constraints.getValues();
|
|
std::size_t indexConstr = ConStr.size() - geoIdRadiusMap.size();
|
|
|
|
// Guess some reasonable distance for placing the datum text
|
|
Gui::Document *doc = getActiveGuiDocument();
|
|
float sf = 1.f;
|
|
if (doc && doc->getInEdit() && doc->getInEdit()->isDerivedFrom(SketcherGui::ViewProviderSketch::getClassTypeId())) {
|
|
SketcherGui::ViewProviderSketch *vp = static_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit());
|
|
sf = vp->getScaleFactor();
|
|
|
|
for (std::size_t i=0; i<geoIdRadiusMap.size();i++) {
|
|
Sketcher::Constraint *constr = ConStr[indexConstr + i];
|
|
constr->LabelDistance = 2. * sf;
|
|
}
|
|
vp->draw(false,false); // Redraw
|
|
}
|
|
|
|
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
|
bool show = hGrp->GetBool("ShowDialogOnDistanceConstraint", true);
|
|
// Ask for the value of the radius immediately
|
|
if (show && constraintCreationMode==Driving) {
|
|
QDialog dlg(Gui::getMainWindow());
|
|
Ui::InsertDatum ui_Datum;
|
|
ui_Datum.setupUi(&dlg);
|
|
dlg.setWindowTitle(EditDatumDialog::tr("Change radius"));
|
|
ui_Datum.label->setText(EditDatumDialog::tr("Radius:"));
|
|
Base::Quantity init_val;
|
|
init_val.setUnit(Base::Unit::Length);
|
|
init_val.setValue(geoIdRadiusMap.front().second);
|
|
|
|
ui_Datum.labelEdit->setValue(init_val);
|
|
ui_Datum.labelEdit->selectNumber();
|
|
if (constrainEqual || geoIdRadiusMap.size() == 1)
|
|
ui_Datum.labelEdit->bind(Obj->Constraints.createPath(indexConstr));
|
|
else
|
|
ui_Datum.name->setDisabled(true);
|
|
|
|
if (dlg.exec() == QDialog::Accepted) {
|
|
Base::Quantity newQuant = ui_Datum.labelEdit->value();
|
|
double newRadius = newQuant.getValue();
|
|
|
|
try {
|
|
if (constrainEqual || geoIdRadiusMap.size() == 1) {
|
|
Gui::cmdAppObjectArgs(Obj, "setDatum(%i,App.Units.Quantity('%f %s'))",
|
|
indexConstr, newRadius, (const char*)newQuant.getUnit().getString().toUtf8());
|
|
|
|
QString constraintName = ui_Datum.name->text().trimmed();
|
|
if (Base::Tools::toStdString(constraintName) != Obj->Constraints[indexConstr]->Name) {
|
|
std::string escapedstr = Base::Tools::escapedUnicodeFromUtf8(constraintName.toUtf8().constData());
|
|
Gui::cmdAppObjectArgs(Obj, "renameConstraint(%d, u'%s')",
|
|
indexConstr, escapedstr.c_str());
|
|
}
|
|
}
|
|
else {
|
|
for (std::size_t i=0; i<geoIdRadiusMap.size();i++) {
|
|
Gui::cmdAppObjectArgs(Obj, "setDatum(%i,App.Units.Quantity('%f %s'))",
|
|
indexConstr+i, newRadius, (const char*)newQuant.getUnit().getString().toUtf8());
|
|
}
|
|
}
|
|
|
|
commitCommand();
|
|
|
|
if (Obj->noRecomputes && Obj->ExpressionEngine.depsAreTouched()) {
|
|
Obj->ExpressionEngine.execute();
|
|
Obj->solve();
|
|
}
|
|
|
|
tryAutoRecompute(Obj);
|
|
|
|
commitNeeded=false;
|
|
updateNeeded=false;
|
|
}
|
|
catch (const Base::Exception& e) {
|
|
QMessageBox::critical(qApp->activeWindow(), QObject::tr("Dimensional constraint"), QString::fromUtf8(e.what()));
|
|
abortCommand();
|
|
|
|
tryAutoRecomputeIfNotSolve(Obj); // we have to update the solver after this aborted addition.
|
|
}
|
|
}
|
|
else {
|
|
// command canceled
|
|
abortCommand();
|
|
|
|
updateNeeded=true;
|
|
}
|
|
}
|
|
else {
|
|
// now dialog was shown so commit the command
|
|
commitCommand();
|
|
commitNeeded=false;
|
|
}
|
|
//updateActive();
|
|
getSelection().clearSelection();
|
|
}
|
|
|
|
if (commitNeeded)
|
|
commitCommand();
|
|
|
|
if(updateNeeded) {
|
|
tryAutoRecomputeIfNotSolve(Obj); // we have to update the solver after this aborted addition.
|
|
}
|
|
}
|
|
|
|
void CmdSketcherConstrainRadius::applyConstraint(std::vector<SelIdPair> &selSeq, int seqIndex)
|
|
{
|
|
SketcherGui::ViewProviderSketch* sketchgui = static_cast<SketcherGui::ViewProviderSketch*>(getActiveGuiDocument()->getInEdit());
|
|
Sketcher::SketchObject* Obj = sketchgui->getSketchObject();
|
|
|
|
int GeoId = selSeq.at(0).GeoId;
|
|
double radius = 0.0;
|
|
|
|
bool commitNeeded=false;
|
|
bool updateNeeded=false;
|
|
|
|
switch (seqIndex) {
|
|
case 0: // {SelEdge}
|
|
case 1: // {SelExternalEdge}
|
|
{
|
|
const Part::Geometry *geom = Obj->getGeometry(GeoId);
|
|
if (geom && geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
|
|
const Part::GeomArcOfCircle *arc = static_cast<const Part::GeomArcOfCircle *>(geom);
|
|
radius = arc->getRadius();
|
|
}
|
|
else if (geom && geom->getTypeId() == Part::GeomCircle::getClassTypeId()) {
|
|
const Part::GeomCircle *circle = static_cast<const Part::GeomCircle *>(geom);
|
|
radius = circle->getRadius();
|
|
}
|
|
else {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Constraint only applies to arcs or circles."));
|
|
return;
|
|
}
|
|
|
|
// Create the radius constraint now
|
|
openCommand("Add radius constraint");
|
|
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Radius',%d,%f)) ",
|
|
GeoId, radius);
|
|
|
|
const std::vector<Sketcher::Constraint *> &ConStr = Obj->Constraints.getValues();
|
|
|
|
int indexConstr = ConStr.size() - 1;
|
|
bool fixed = isPointOrSegmentFixed(Obj,GeoId);
|
|
if(fixed || constraintCreationMode==Reference) {
|
|
Gui::cmdAppObjectArgs(Obj, "setDriving(%i,%s)",
|
|
ConStr.size()-1, "False");
|
|
|
|
updateNeeded=true; // We do need to update the solver DoF after setting the constraint driving.
|
|
}
|
|
|
|
// Guess some reasonable distance for placing the datum text
|
|
Gui::Document *doc = getActiveGuiDocument();
|
|
float sf = 1.f;
|
|
if (doc && doc->getInEdit() && doc->getInEdit()->isDerivedFrom(SketcherGui::ViewProviderSketch::getClassTypeId())) {
|
|
SketcherGui::ViewProviderSketch *vp = static_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit());
|
|
sf = vp->getScaleFactor();
|
|
|
|
Sketcher::Constraint *constr = ConStr[ConStr.size()-1];
|
|
constr->LabelDistance = 2. * sf;
|
|
vp->draw(); // Redraw
|
|
}
|
|
|
|
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
|
bool show = hGrp->GetBool("ShowDialogOnDistanceConstraint", true);
|
|
// Ask for the value of the radius immediately
|
|
if (show && constraintCreationMode==Driving && !fixed) {
|
|
QDialog dlg(Gui::getMainWindow());
|
|
Ui::InsertDatum ui_Datum;
|
|
ui_Datum.setupUi(&dlg);
|
|
dlg.setWindowTitle(EditDatumDialog::tr("Change radius"));
|
|
ui_Datum.label->setText(EditDatumDialog::tr("Radius:"));
|
|
Base::Quantity init_val;
|
|
init_val.setUnit(Base::Unit::Length);
|
|
init_val.setValue(radius);
|
|
|
|
ui_Datum.labelEdit->setValue(init_val);
|
|
ui_Datum.labelEdit->selectNumber();
|
|
ui_Datum.labelEdit->bind(Obj->Constraints.createPath(indexConstr));
|
|
|
|
if (dlg.exec() == QDialog::Accepted) {
|
|
Base::Quantity newQuant = ui_Datum.labelEdit->value();
|
|
double newRadius = newQuant.getValue();
|
|
|
|
try {
|
|
Gui::cmdAppObjectArgs(Obj, "setDatum(%i,App.Units.Quantity('%f %s'))",
|
|
indexConstr, newRadius, (const char*)newQuant.getUnit().getString().toUtf8());
|
|
|
|
QString constraintName = ui_Datum.name->text().trimmed();
|
|
if (Base::Tools::toStdString(constraintName) != Obj->Constraints[indexConstr]->Name) {
|
|
std::string escapedstr = Base::Tools::escapedUnicodeFromUtf8(constraintName.toUtf8().constData());
|
|
Gui::cmdAppObjectArgs(Obj, "renameConstraint(%d, u'%s')",
|
|
indexConstr, escapedstr.c_str());
|
|
}
|
|
|
|
commitCommand();
|
|
|
|
if (Obj->noRecomputes && Obj->ExpressionEngine.depsAreTouched()) {
|
|
Obj->ExpressionEngine.execute();
|
|
Obj->solve();
|
|
}
|
|
|
|
tryAutoRecompute(Obj);
|
|
|
|
commitNeeded=false;
|
|
updateNeeded=false;
|
|
}
|
|
catch (const Base::Exception& e) {
|
|
QMessageBox::critical(qApp->activeWindow(), QObject::tr("Dimensional constraint"), QString::fromUtf8(e.what()));
|
|
abortCommand();
|
|
|
|
tryAutoRecomputeIfNotSolve(Obj); // we have to update the solver after this aborted addition.
|
|
}
|
|
}
|
|
else {
|
|
// command canceled
|
|
abortCommand();
|
|
|
|
updateNeeded=true;
|
|
}
|
|
}
|
|
else {
|
|
// now dialog was shown so commit the command
|
|
commitCommand();
|
|
commitNeeded=false;
|
|
}
|
|
//updateActive();
|
|
getSelection().clearSelection();
|
|
|
|
if(commitNeeded)
|
|
commitCommand();
|
|
|
|
if(updateNeeded) {
|
|
tryAutoRecomputeIfNotSolve(Obj); // we have to update the solver after this aborted addition.
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CmdSketcherConstrainRadius::updateAction(int mode)
|
|
{
|
|
switch (mode) {
|
|
case Reference:
|
|
if (getAction())
|
|
getAction()->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Radius_Driven"));
|
|
break;
|
|
case Driving:
|
|
if (getAction())
|
|
getAction()->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Radius"));
|
|
break;
|
|
}
|
|
}
|
|
|
|
// ======================================================================================
|
|
|
|
class CmdSketcherConstrainDiameter : public CmdSketcherConstraint
|
|
{
|
|
public:
|
|
CmdSketcherConstrainDiameter();
|
|
virtual ~CmdSketcherConstrainDiameter(){}
|
|
virtual void updateAction(int mode);
|
|
virtual const char* className() const
|
|
{ return "CmdSketcherConstrainDiameter"; }
|
|
protected:
|
|
virtual void activated(int iMsg);
|
|
virtual void applyConstraint(std::vector<SelIdPair> &selSeq, int seqIndex);
|
|
};
|
|
|
|
CmdSketcherConstrainDiameter::CmdSketcherConstrainDiameter()
|
|
:CmdSketcherConstraint("Sketcher_ConstrainDiameter")
|
|
{
|
|
sAppModule = "Sketcher";
|
|
sGroup = QT_TR_NOOP("Sketcher");
|
|
sMenuText = QT_TR_NOOP("Constrain diameter");
|
|
sToolTipText = QT_TR_NOOP("Fix the diameter of a circle or an arc");
|
|
sWhatsThis = "Sketcher_ConstrainDiameter";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "Constraint_Diameter";
|
|
sAccel = "";
|
|
eType = ForEdit;
|
|
|
|
allowedSelSequences = {{SelEdge}, {SelExternalEdge}};
|
|
constraintCursor = cursor_genericconstraint;
|
|
}
|
|
|
|
void CmdSketcherConstrainDiameter::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
// get the selection
|
|
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
|
|
|
|
// only one sketch with its subelements are allowed to be selected
|
|
if (selection.size() != 1 || !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
|
|
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
|
bool constraintMode = hGrp->GetBool("ContinuousConstraintMode", true);
|
|
|
|
if (constraintMode) {
|
|
ActivateHandler(getActiveGuiDocument(),
|
|
new DrawSketchHandlerGenConstraint(constraintCursor, this));
|
|
getSelection().clearSelection();
|
|
} else {
|
|
// TODO: Get the exact message from git history and put it here
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select the right things from the sketch."));
|
|
}
|
|
return;
|
|
}
|
|
|
|
// get the needed lists and objects
|
|
const std::vector<std::string> &SubNames = selection[0].getSubNames();
|
|
Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
|
|
|
|
if (SubNames.empty()) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select one or more arcs or circles from the sketch."));
|
|
return;
|
|
}
|
|
|
|
// check for which selected geometry the constraint can be applied
|
|
std::vector< std::pair<int, double> > geoIdDiameterMap;
|
|
std::vector< std::pair<int, double> > externalGeoIdDiameterMap;
|
|
|
|
for (std::vector<std::string>::const_iterator it = SubNames.begin(); it != SubNames.end(); ++it) {
|
|
bool issegmentfixed = false;
|
|
int GeoId;
|
|
|
|
if (it->size() > 4 && it->substr(0,4) == "Edge") {
|
|
GeoId = std::atoi(it->substr(4,4000).c_str()) - 1;
|
|
issegmentfixed = isPointOrSegmentFixed(Obj,GeoId);
|
|
}
|
|
else if (it->size() > 4 && it->substr(0,12) == "ExternalEdge") {
|
|
GeoId = -std::atoi(it->substr(12,4000).c_str()) - 2;
|
|
issegmentfixed = true;
|
|
}
|
|
else
|
|
continue;
|
|
|
|
const Part::Geometry *geom = Obj->getGeometry(GeoId);
|
|
|
|
if (geom && geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
|
|
const Part::GeomArcOfCircle *arc = static_cast<const Part::GeomArcOfCircle *>(geom);
|
|
double radius = arc->getRadius();
|
|
|
|
if(issegmentfixed) {
|
|
externalGeoIdDiameterMap.push_back(std::make_pair(GeoId, 2*radius));
|
|
}
|
|
else {
|
|
geoIdDiameterMap.push_back(std::make_pair(GeoId, 2*radius));
|
|
}
|
|
}
|
|
else if (geom && geom->getTypeId() == Part::GeomCircle::getClassTypeId()) {
|
|
const Part::GeomCircle *circle = static_cast<const Part::GeomCircle *>(geom);
|
|
double radius = circle->getRadius();
|
|
|
|
if(issegmentfixed) {
|
|
externalGeoIdDiameterMap.push_back(std::make_pair(GeoId, 2*radius));
|
|
}
|
|
else {
|
|
geoIdDiameterMap.push_back(std::make_pair(GeoId, 2*radius));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (geoIdDiameterMap.empty() && externalGeoIdDiameterMap.empty()) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select one or more arcs or circles from the sketch."));
|
|
}
|
|
|
|
bool commitNeeded=false;
|
|
bool updateNeeded=false;
|
|
bool commandopened=false;
|
|
|
|
if(!externalGeoIdDiameterMap.empty()) {
|
|
// Create the non-driving radius constraints now
|
|
openCommand("Add diameter constraint");
|
|
commandopened=true;
|
|
unsigned int constrSize = 0;
|
|
|
|
for (std::vector< std::pair<int, double> >::iterator it = externalGeoIdDiameterMap.begin(); it != externalGeoIdDiameterMap.end(); ++it) {
|
|
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Diameter',%d,%f)) ", it->first,it->second);
|
|
|
|
const std::vector<Sketcher::Constraint *> &ConStr = Obj->Constraints.getValues();
|
|
|
|
constrSize=ConStr.size();
|
|
|
|
Gui::cmdAppObjectArgs(Obj,"setDriving(%i,%s)",constrSize-1,"False");
|
|
}
|
|
|
|
const std::vector<Sketcher::Constraint *> &ConStr = Obj->Constraints.getValues();
|
|
|
|
std::size_t indexConstr = constrSize - externalGeoIdDiameterMap.size();
|
|
|
|
// Guess some reasonable distance for placing the datum text
|
|
Gui::Document *doc = getActiveGuiDocument();
|
|
float sf = 1.f;
|
|
if (doc && doc->getInEdit() && doc->getInEdit()->isDerivedFrom(SketcherGui::ViewProviderSketch::getClassTypeId())) {
|
|
SketcherGui::ViewProviderSketch *vp = static_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit());
|
|
sf = vp->getScaleFactor();
|
|
|
|
for (std::size_t i=0; i<externalGeoIdDiameterMap.size();i++) {
|
|
Sketcher::Constraint *constr = ConStr[indexConstr + i];
|
|
constr->LabelDistance = 2. * sf;
|
|
}
|
|
vp->draw(false,false); // Redraw
|
|
}
|
|
|
|
commitNeeded=true;
|
|
updateNeeded=true;
|
|
}
|
|
|
|
if(!geoIdDiameterMap.empty())
|
|
{
|
|
bool constrainEqual = false;
|
|
if (geoIdDiameterMap.size() > 1 && constraintCreationMode==Driving) {
|
|
int ret = QMessageBox::question(Gui::getMainWindow(), QObject::tr("Constrain equal"),
|
|
QObject::tr("Do you want to share the same diameter for all selected elements?"),
|
|
QMessageBox::Yes, QMessageBox::No, QMessageBox::Cancel);
|
|
// use an equality constraint
|
|
if (ret == QMessageBox::Yes) {
|
|
constrainEqual = true;
|
|
}
|
|
else if (ret == QMessageBox::Cancel) {
|
|
// do nothing
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (constrainEqual) {
|
|
// Create the one radius constraint now
|
|
int refGeoId = geoIdDiameterMap.front().first;
|
|
double diameter = geoIdDiameterMap.front().second;
|
|
|
|
if(!commandopened)
|
|
openCommand("Add diameter constraint");
|
|
|
|
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Diameter',%d,%f)) ", refGeoId,diameter);
|
|
|
|
// Add the equality constraints
|
|
for (std::vector< std::pair<int, double> >::iterator it = geoIdDiameterMap.begin()+1; it != geoIdDiameterMap.end(); ++it) {
|
|
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Equal',%d,%d)) ", refGeoId,it->first);
|
|
}
|
|
}
|
|
else {
|
|
// Create the diameter constraints now
|
|
if(!commandopened)
|
|
openCommand("Add diameter constraint");
|
|
for (std::vector< std::pair<int, double> >::iterator it = geoIdDiameterMap.begin(); it != geoIdDiameterMap.end(); ++it) {
|
|
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Diameter',%d,%f)) ",
|
|
it->first,it->second);
|
|
|
|
if(constraintCreationMode==Reference) {
|
|
|
|
const std::vector<Sketcher::Constraint *> &ConStr = Obj->Constraints.getValues();
|
|
|
|
Gui::cmdAppObjectArgs(Obj, "setDriving(%i,%s)", ConStr.size()-1,"False");
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
const std::vector<Sketcher::Constraint *> &ConStr = Obj->Constraints.getValues();
|
|
std::size_t indexConstr = ConStr.size() - geoIdDiameterMap.size();
|
|
|
|
// Guess some reasonable distance for placing the datum text
|
|
Gui::Document *doc = getActiveGuiDocument();
|
|
float sf = 1.f;
|
|
if (doc && doc->getInEdit() && doc->getInEdit()->isDerivedFrom(SketcherGui::ViewProviderSketch::getClassTypeId())) {
|
|
SketcherGui::ViewProviderSketch *vp = static_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit());
|
|
sf = vp->getScaleFactor();
|
|
|
|
for (std::size_t i=0; i<geoIdDiameterMap.size();i++) {
|
|
Sketcher::Constraint *constr = ConStr[indexConstr + i];
|
|
constr->LabelDistance = 2. * sf;
|
|
}
|
|
vp->draw(false,false); // Redraw
|
|
}
|
|
|
|
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
|
bool show = hGrp->GetBool("ShowDialogOnDistanceConstraint", true);
|
|
// Ask for the value of the diameter immediately
|
|
if (show && constraintCreationMode==Driving) {
|
|
QDialog dlg(Gui::getMainWindow());
|
|
Ui::InsertDatum ui_Datum;
|
|
ui_Datum.setupUi(&dlg);
|
|
dlg.setWindowTitle(EditDatumDialog::tr("Change diameter"));
|
|
ui_Datum.label->setText(EditDatumDialog::tr("Diameter:"));
|
|
Base::Quantity init_val;
|
|
init_val.setUnit(Base::Unit::Length);
|
|
init_val.setValue(geoIdDiameterMap.front().second);
|
|
|
|
ui_Datum.labelEdit->setValue(init_val);
|
|
ui_Datum.labelEdit->selectNumber();
|
|
if (constrainEqual || geoIdDiameterMap.size() == 1)
|
|
ui_Datum.labelEdit->bind(Obj->Constraints.createPath(indexConstr));
|
|
else
|
|
ui_Datum.name->setDisabled(true);
|
|
|
|
if (dlg.exec() == QDialog::Accepted) {
|
|
Base::Quantity newQuant = ui_Datum.labelEdit->value();
|
|
double newDiameter = newQuant.getValue();
|
|
|
|
try {
|
|
if (constrainEqual || geoIdDiameterMap.size() == 1) {
|
|
Gui::cmdAppObjectArgs(Obj, "setDatum(%i,App.Units.Quantity('%f %s'))",
|
|
indexConstr, newDiameter, (const char*)newQuant.getUnit().getString().toUtf8());
|
|
|
|
QString constraintName = ui_Datum.name->text().trimmed();
|
|
if (Base::Tools::toStdString(constraintName) != Obj->Constraints[indexConstr]->Name) {
|
|
std::string escapedstr = Base::Tools::escapedUnicodeFromUtf8(constraintName.toUtf8().constData());
|
|
Gui::cmdAppObjectArgs(Obj, "renameConstraint(%d, u'%s')", indexConstr, escapedstr.c_str());
|
|
}
|
|
}
|
|
else {
|
|
for (std::size_t i=0; i<geoIdDiameterMap.size();i++) {
|
|
Gui::cmdAppObjectArgs(Obj, "setDatum(%i,App.Units.Quantity('%f %s'))",
|
|
indexConstr+i, newDiameter, (const char*)newQuant.getUnit().getString().toUtf8());
|
|
}
|
|
}
|
|
|
|
commitCommand();
|
|
|
|
if (Obj->noRecomputes && Obj->ExpressionEngine.depsAreTouched()) {
|
|
Obj->ExpressionEngine.execute();
|
|
Obj->solve();
|
|
}
|
|
|
|
tryAutoRecompute(Obj);
|
|
|
|
commitNeeded=false;
|
|
updateNeeded=false;
|
|
}
|
|
catch (const Base::Exception& e) {
|
|
QMessageBox::critical(qApp->activeWindow(), QObject::tr("Dimensional constraint"), QString::fromUtf8(e.what()));
|
|
abortCommand();
|
|
|
|
tryAutoRecomputeIfNotSolve(Obj); // we have to update the solver after this aborted addition.
|
|
}
|
|
}
|
|
else {
|
|
// command canceled
|
|
abortCommand();
|
|
|
|
updateNeeded=true;
|
|
}
|
|
}
|
|
else {
|
|
// now dialog was shown so commit the command
|
|
commitCommand();
|
|
commitNeeded=false;
|
|
}
|
|
//updateActive();
|
|
getSelection().clearSelection();
|
|
}
|
|
|
|
if (commitNeeded)
|
|
commitCommand();
|
|
|
|
if(updateNeeded) {
|
|
tryAutoRecomputeIfNotSolve(Obj); // we have to update the solver after this aborted addition.
|
|
}
|
|
}
|
|
|
|
void CmdSketcherConstrainDiameter::applyConstraint(std::vector<SelIdPair> &selSeq, int seqIndex)
|
|
{
|
|
SketcherGui::ViewProviderSketch* sketchgui = static_cast<SketcherGui::ViewProviderSketch*>(getActiveGuiDocument()->getInEdit());
|
|
Sketcher::SketchObject* Obj = sketchgui->getSketchObject();
|
|
|
|
int GeoId = selSeq.at(0).GeoId;
|
|
double diameter = 0.0;
|
|
|
|
bool commitNeeded=false;
|
|
bool updateNeeded=false;
|
|
|
|
switch (seqIndex) {
|
|
case 0: // {SelEdge}
|
|
case 1: // {SelExternalEdge}
|
|
{
|
|
const Part::Geometry *geom = Obj->getGeometry(GeoId);
|
|
if (geom && geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
|
|
const Part::GeomArcOfCircle *arc = static_cast<const Part::GeomArcOfCircle *>(geom);
|
|
diameter = 2*arc->getRadius();
|
|
}
|
|
else if (geom && geom->getTypeId() == Part::GeomCircle::getClassTypeId()) {
|
|
const Part::GeomCircle *circle = static_cast<const Part::GeomCircle *>(geom);
|
|
diameter = 2*circle->getRadius();
|
|
}
|
|
else {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Constraint only applies to arcs or circles."));
|
|
return;
|
|
}
|
|
|
|
// Create the diameter constraint now
|
|
openCommand("Add diameter constraint");
|
|
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Diameter',%d,%f)) ",
|
|
GeoId, diameter);
|
|
|
|
const std::vector<Sketcher::Constraint *> &ConStr = Obj->Constraints.getValues();
|
|
|
|
int indexConstr = ConStr.size() - 1;
|
|
bool fixed = isPointOrSegmentFixed(Obj,GeoId);
|
|
if(fixed || constraintCreationMode==Reference) {
|
|
Gui::cmdAppObjectArgs(Obj, "setDriving(%i,%s)", ConStr.size()-1, "False");
|
|
updateNeeded=true; // We do need to update the solver DoF after setting the constraint driving.
|
|
}
|
|
|
|
// Guess some reasonable distance for placing the datum text
|
|
Gui::Document *doc = getActiveGuiDocument();
|
|
float sf = 1.f;
|
|
if (doc && doc->getInEdit() && doc->getInEdit()->isDerivedFrom(SketcherGui::ViewProviderSketch::getClassTypeId())) {
|
|
SketcherGui::ViewProviderSketch *vp = static_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit());
|
|
sf = vp->getScaleFactor();
|
|
|
|
Sketcher::Constraint *constr = ConStr[ConStr.size()-1];
|
|
constr->LabelDistance = 2. * sf;
|
|
vp->draw(); // Redraw
|
|
}
|
|
|
|
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
|
bool show = hGrp->GetBool("ShowDialogOnDistanceConstraint", true);
|
|
// Ask for the value of the diameter immediately
|
|
if (show && constraintCreationMode==Driving && !fixed) {
|
|
QDialog dlg(Gui::getMainWindow());
|
|
Ui::InsertDatum ui_Datum;
|
|
ui_Datum.setupUi(&dlg);
|
|
dlg.setWindowTitle(EditDatumDialog::tr("Change diameter"));
|
|
ui_Datum.label->setText(EditDatumDialog::tr("Diameter:"));
|
|
Base::Quantity init_val;
|
|
init_val.setUnit(Base::Unit::Length);
|
|
init_val.setValue(diameter);
|
|
|
|
ui_Datum.labelEdit->setValue(init_val);
|
|
ui_Datum.labelEdit->selectNumber();
|
|
ui_Datum.labelEdit->bind(Obj->Constraints.createPath(indexConstr));
|
|
|
|
if (dlg.exec() == QDialog::Accepted) {
|
|
Base::Quantity newQuant = ui_Datum.labelEdit->value();
|
|
double newDiameter = newQuant.getValue();
|
|
|
|
try {
|
|
Gui::cmdAppObjectArgs(Obj, "setDatum(%i,App.Units.Quantity('%f %s'))",
|
|
indexConstr, newDiameter, (const char*)newQuant.getUnit().getString().toUtf8());
|
|
|
|
QString constraintName = ui_Datum.name->text().trimmed();
|
|
if (Base::Tools::toStdString(constraintName) != Obj->Constraints[indexConstr]->Name) {
|
|
std::string escapedstr = Base::Tools::escapedUnicodeFromUtf8(constraintName.toUtf8().constData());
|
|
Gui::cmdAppObjectArgs(Obj, "renameConstraint(%d, u'%s')", indexConstr, escapedstr.c_str());
|
|
}
|
|
|
|
commitCommand();
|
|
|
|
if (Obj->noRecomputes && Obj->ExpressionEngine.depsAreTouched()) {
|
|
Obj->ExpressionEngine.execute();
|
|
Obj->solve();
|
|
}
|
|
|
|
tryAutoRecompute(Obj);
|
|
|
|
commitNeeded=false;
|
|
updateNeeded=false;
|
|
}
|
|
catch (const Base::Exception& e) {
|
|
QMessageBox::critical(qApp->activeWindow(), QObject::tr("Dimensional constraint"), QString::fromUtf8(e.what()));
|
|
abortCommand();
|
|
|
|
tryAutoRecomputeIfNotSolve(Obj); // we have to update the solver after this aborted addition.
|
|
}
|
|
}
|
|
else {
|
|
// command canceled
|
|
abortCommand();
|
|
|
|
updateNeeded=true;
|
|
}
|
|
}
|
|
else {
|
|
// now dialog was shown so commit the command
|
|
commitCommand();
|
|
commitNeeded=false;
|
|
}
|
|
//updateActive();
|
|
getSelection().clearSelection();
|
|
|
|
if(commitNeeded)
|
|
commitCommand();
|
|
|
|
if(updateNeeded) {
|
|
tryAutoRecomputeIfNotSolve(Obj); // we have to update the solver after this aborted addition.
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CmdSketcherConstrainDiameter::updateAction(int mode)
|
|
{
|
|
switch (mode) {
|
|
case Reference:
|
|
if (getAction())
|
|
getAction()->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Diameter_Driven"));
|
|
break;
|
|
case Driving:
|
|
if (getAction())
|
|
getAction()->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Diameter"));
|
|
break;
|
|
}
|
|
}
|
|
|
|
// ======================================================================================
|
|
|
|
DEF_STD_CMD_ACLU(CmdSketcherCompConstrainRadDia)
|
|
|
|
CmdSketcherCompConstrainRadDia::CmdSketcherCompConstrainRadDia()
|
|
: Command("Sketcher_CompConstrainRadDia")
|
|
{
|
|
sAppModule = "Sketcher";
|
|
sGroup = QT_TR_NOOP("Sketcher");
|
|
sMenuText = QT_TR_NOOP("Constrain arc or circle");
|
|
sToolTipText = QT_TR_NOOP("Constrain an arc or a circle");
|
|
sWhatsThis = "Sketcher_CompCreateCircle";
|
|
sStatusTip = sToolTipText;
|
|
sAccel = "SHIFT+R";
|
|
eType = ForEdit;
|
|
}
|
|
|
|
void CmdSketcherCompConstrainRadDia::activated(int iMsg)
|
|
{
|
|
Gui::CommandManager &rcCmdMgr = Gui::Application::Instance->commandManager();
|
|
if (iMsg==0) {
|
|
rcCmdMgr.runCommandByName("Sketcher_ConstrainRadius");
|
|
}
|
|
else if (iMsg==1) {
|
|
rcCmdMgr.runCommandByName("Sketcher_ConstrainDiameter");
|
|
}
|
|
else
|
|
return;
|
|
|
|
// Since the default icon is reset when enabling/disabling the command we have
|
|
// to explicitly set the icon of the used command.
|
|
Gui::ActionGroup* pcAction = qobject_cast<Gui::ActionGroup*>(_pcAction);
|
|
QList<QAction*> a = pcAction->actions();
|
|
|
|
assert(iMsg < a.size());
|
|
pcAction->setIcon(a[iMsg]->icon());
|
|
}
|
|
|
|
Gui::Action * CmdSketcherCompConstrainRadDia::createAction(void)
|
|
{
|
|
Gui::ActionGroup* pcAction = new Gui::ActionGroup(this, Gui::getMainWindow());
|
|
pcAction->setDropDownMenu(true);
|
|
applyCommandData(this->className(), pcAction);
|
|
|
|
QAction* arc1 = pcAction->addAction(QString());
|
|
arc1->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Radius"));
|
|
QAction* arc2 = pcAction->addAction(QString());
|
|
arc2->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Diameter"));
|
|
|
|
_pcAction = pcAction;
|
|
languageChange();
|
|
|
|
pcAction->setIcon(arc1->icon());
|
|
int defaultId = 0;
|
|
pcAction->setProperty("defaultAction", QVariant(defaultId));
|
|
|
|
pcAction->setShortcut(QString::fromLatin1(sAccel));
|
|
|
|
return pcAction;
|
|
}
|
|
|
|
void CmdSketcherCompConstrainRadDia::updateAction(int mode)
|
|
{
|
|
Gui::ActionGroup* pcAction = qobject_cast<Gui::ActionGroup*>(getAction());
|
|
if (!pcAction)
|
|
return;
|
|
|
|
QList<QAction*> a = pcAction->actions();
|
|
int index = pcAction->property("defaultAction").toInt();
|
|
switch (mode) {
|
|
case Reference:
|
|
a[0]->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Radius_Driven"));
|
|
a[1]->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Diameter_Driven"));
|
|
getAction()->setIcon(a[index]->icon());
|
|
break;
|
|
case Driving:
|
|
a[0]->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Radius"));
|
|
a[1]->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Diameter"));
|
|
getAction()->setIcon(a[index]->icon());
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CmdSketcherCompConstrainRadDia::languageChange()
|
|
{
|
|
Command::languageChange();
|
|
|
|
if (!_pcAction)
|
|
return;
|
|
Gui::ActionGroup* pcAction = qobject_cast<Gui::ActionGroup*>(_pcAction);
|
|
QList<QAction*> a = pcAction->actions();
|
|
|
|
QAction* arc1 = a[0];
|
|
arc1->setText(QApplication::translate("CmdSketcherCompConstrainRadDia", "Constrain radius"));
|
|
arc1->setToolTip(QApplication::translate("Sketcher_ConstrainRadius", "Fix the radius of a circle or an arc"));
|
|
arc1->setStatusTip(QApplication::translate("Sketcher_ConstrainRadius", "Fix the radius of a circle or an arc"));
|
|
QAction* arc2 = a[1];
|
|
arc2->setText(QApplication::translate("CmdSketcherCompConstrainRadDia", "Constrain diameter"));
|
|
arc2->setToolTip(QApplication::translate("Sketcher_ConstrainDiameter", "Fix the diameter of a circle or an arc"));
|
|
arc2->setStatusTip(QApplication::translate("Sketcher_ConstrainDiameter", "Fix the diameter of a circle or an arc"));
|
|
}
|
|
|
|
bool CmdSketcherCompConstrainRadDia::isActive(void)
|
|
{
|
|
return isCreateGeoActive(getActiveGuiDocument());
|
|
}
|
|
|
|
// ======================================================================================
|
|
|
|
class CmdSketcherConstrainAngle : public CmdSketcherConstraint
|
|
{
|
|
public:
|
|
CmdSketcherConstrainAngle();
|
|
virtual ~CmdSketcherConstrainAngle(){}
|
|
virtual void updateAction(int mode);
|
|
virtual const char* className() const
|
|
{ return "CmdSketcherConstrainAngle"; }
|
|
protected:
|
|
virtual void activated(int iMsg);
|
|
virtual void applyConstraint(std::vector<SelIdPair> &selSeq, int seqIndex);
|
|
};
|
|
|
|
CmdSketcherConstrainAngle::CmdSketcherConstrainAngle()
|
|
:CmdSketcherConstraint("Sketcher_ConstrainAngle")
|
|
{
|
|
sAppModule = "Sketcher";
|
|
sGroup = QT_TR_NOOP("Sketcher");
|
|
sMenuText = QT_TR_NOOP("Constrain angle");
|
|
sToolTipText = QT_TR_NOOP("Fix the angle of a line or the angle between two lines");
|
|
sWhatsThis = "Sketcher_ConstrainAngle";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "Constraint_InternalAngle";
|
|
sAccel = "A";
|
|
eType = ForEdit;
|
|
|
|
allowedSelSequences = {{SelEdge, SelEdgeOrAxis}, {SelEdgeOrAxis, SelEdge},
|
|
{SelEdge, SelExternalEdge}, {SelExternalEdge, SelEdge},
|
|
{SelExternalEdge, SelExternalEdge},
|
|
{SelEdge, SelVertexOrRoot, SelEdgeOrAxis},
|
|
{SelEdgeOrAxis, SelVertexOrRoot, SelEdge},
|
|
{SelEdge, SelVertexOrRoot, SelExternalEdge},
|
|
{SelExternalEdge, SelVertexOrRoot, SelEdge},
|
|
{SelExternalEdge, SelVertexOrRoot, SelExternalEdge},
|
|
{SelVertexOrRoot, SelEdge, SelEdgeOrAxis},
|
|
{SelVertexOrRoot, SelEdgeOrAxis, SelEdge},
|
|
{SelVertexOrRoot, SelEdge, SelExternalEdge},
|
|
{SelVertexOrRoot, SelExternalEdge, SelEdge},
|
|
{SelVertexOrRoot, SelExternalEdge, SelExternalEdge}};
|
|
constraintCursor = cursor_genericconstraint;
|
|
}
|
|
|
|
void CmdSketcherConstrainAngle::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
//TODO: comprehensive messages, like in CmdSketcherConstrainTangent
|
|
// get the selection
|
|
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
|
|
|
|
// only one sketch with its subelements are allowed to be selected
|
|
if (selection.size() != 1 || !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
|
|
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
|
bool constraintMode = hGrp->GetBool("ContinuousConstraintMode", true);
|
|
|
|
if (constraintMode) {
|
|
ActivateHandler(getActiveGuiDocument(),
|
|
new DrawSketchHandlerGenConstraint(constraintCursor, this));
|
|
getSelection().clearSelection();
|
|
} else {
|
|
// TODO: Get the exact message from git history and put it here
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select the right things from the sketch."));
|
|
}
|
|
return;
|
|
}
|
|
|
|
// get the needed lists and objects
|
|
const std::vector<std::string> &SubNames = selection[0].getSubNames();
|
|
Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
|
|
|
|
if (SubNames.size() < 1 || SubNames.size() > 3) {
|
|
//goto ExitWithMessage;
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select one or two lines from the sketch. Or select two edges and a point."));
|
|
|
|
}
|
|
|
|
|
|
int GeoId1, GeoId2=Constraint::GeoUndef, GeoId3 = Constraint::GeoUndef;
|
|
Sketcher::PointPos PosId1, PosId2=Sketcher::none, PosId3 = Sketcher::none;
|
|
getIdsFromName(SubNames[0], Obj, GeoId1, PosId1);
|
|
if (SubNames.size() > 1)
|
|
getIdsFromName(SubNames[1], Obj, GeoId2, PosId2);
|
|
if (SubNames.size() > 2)
|
|
getIdsFromName(SubNames[2], Obj, GeoId3, PosId3);
|
|
|
|
if (SubNames.size() == 3){//standalone implementation of angle-via-point
|
|
|
|
//let's sink the point to be GeoId3. We want to keep the order the two curves have been selected in.
|
|
if ( isVertex(GeoId1, PosId1) ){
|
|
std::swap(GeoId1,GeoId2);
|
|
std::swap(PosId1,PosId2);
|
|
};
|
|
if ( isVertex(GeoId2, PosId2) ){
|
|
std::swap(GeoId2,GeoId3);
|
|
std::swap(PosId2,PosId3);
|
|
};
|
|
|
|
bool bothexternal=areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2);
|
|
|
|
if (isEdge(GeoId1, PosId1) && isEdge(GeoId2, PosId2) && isVertex(GeoId3, PosId3)) {
|
|
double ActAngle = 0.0;
|
|
|
|
openCommand("Add angle constraint");
|
|
|
|
//add missing point-on-object constraints
|
|
if (!IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)) {
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
|
GeoId3,PosId3,GeoId1);
|
|
}
|
|
if (!IsPointAlreadyOnCurve(GeoId2, GeoId3, PosId3, Obj)) {
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
|
GeoId3,PosId3,GeoId2);
|
|
}
|
|
if (!IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)) {//FIXME: it's a good idea to add a check if the sketch is solved
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
|
GeoId3,PosId3,GeoId1);
|
|
}
|
|
|
|
//assuming point-on-curves have been solved, calculate the angle.
|
|
//DeepSOIC: this may be slow, but I wanted to reuse the conversion from Geometry to GCS shapes that is done in Sketch
|
|
Base::Vector3d p = Obj->getPoint(GeoId3, PosId3 );
|
|
ActAngle = Obj->calculateAngleViaPoint(GeoId1,GeoId2,p.x,p.y);
|
|
|
|
//negative constraint value avoidance
|
|
if (ActAngle < -Precision::Angular()){
|
|
std::swap(GeoId1, GeoId2);
|
|
std::swap(PosId1, PosId2);
|
|
ActAngle = -ActAngle;
|
|
}
|
|
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),"addConstraint(Sketcher.Constraint('AngleViaPoint',%d,%d,%d,%d,%f)) ",
|
|
GeoId1,GeoId2,GeoId3,PosId3,ActAngle);
|
|
|
|
if (bothexternal || constraintCreationMode==Reference) { // it is a constraint on a external line, make it non-driving
|
|
const std::vector<Sketcher::Constraint *> &ConStr = Obj->Constraints.getValues();
|
|
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),"setDriving(%i,%s)",
|
|
ConStr.size()-1,"False");
|
|
finishDistanceConstraint(this, Obj,false);
|
|
}
|
|
else
|
|
finishDistanceConstraint(this, Obj,true);
|
|
|
|
return;
|
|
};
|
|
|
|
} else if (SubNames.size() < 3) {
|
|
|
|
bool bothexternal = areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2);
|
|
|
|
if (isVertex(GeoId1,PosId1) && isEdge(GeoId2,PosId2)) {
|
|
std::swap(GeoId1,GeoId2);
|
|
std::swap(PosId1,PosId2);
|
|
}
|
|
|
|
if (isEdge(GeoId2,PosId2)) { // line to line angle
|
|
|
|
const Part::Geometry *geom1 = Obj->getGeometry(GeoId1);
|
|
const Part::Geometry *geom2 = Obj->getGeometry(GeoId2);
|
|
if (geom1->getTypeId() == Part::GeomLineSegment::getClassTypeId() &&
|
|
geom2->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
|
|
const Part::GeomLineSegment *lineSeg1 = static_cast<const Part::GeomLineSegment*>(geom1);
|
|
const Part::GeomLineSegment *lineSeg2 = static_cast<const Part::GeomLineSegment*>(geom2);
|
|
|
|
// find the two closest line ends
|
|
Sketcher::PointPos PosId1 = Sketcher::none;
|
|
Sketcher::PointPos PosId2 = Sketcher::none;
|
|
Base::Vector3d p1[2], p2[2];
|
|
p1[0] = lineSeg1->getStartPoint();
|
|
p1[1] = lineSeg1->getEndPoint();
|
|
p2[0] = lineSeg2->getStartPoint();
|
|
p2[1] = lineSeg2->getEndPoint();
|
|
|
|
// Get the intersection point in 2d of the two lines if possible
|
|
Base::Line2d line1(Base::Vector2d(p1[0].x, p1[0].y), Base::Vector2d(p1[1].x, p1[1].y));
|
|
Base::Line2d line2(Base::Vector2d(p2[0].x, p2[0].y), Base::Vector2d(p2[1].x, p2[1].y));
|
|
Base::Vector2d s;
|
|
if (line1.Intersect(line2, s)) {
|
|
// get the end points of the line segments that are closest to the intersection point
|
|
Base::Vector3d s3d(s.x, s.y, p1[0].z);
|
|
if (Base::DistanceP2(s3d, p1[0]) < Base::DistanceP2(s3d, p1[1]))
|
|
PosId1 = Sketcher::start;
|
|
else
|
|
PosId1 = Sketcher::end;
|
|
if (Base::DistanceP2(s3d, p2[0]) < Base::DistanceP2(s3d, p2[1]))
|
|
PosId2 = Sketcher::start;
|
|
else
|
|
PosId2 = Sketcher::end;
|
|
}
|
|
else {
|
|
// if all points are collinear
|
|
double length = DBL_MAX;
|
|
for (int i=0; i <= 1; i++) {
|
|
for (int j=0; j <= 1; j++) {
|
|
double tmp = Base::DistanceP2(p2[j], p1[i]);
|
|
if (tmp < length) {
|
|
length = tmp;
|
|
PosId1 = i ? Sketcher::end : Sketcher::start;
|
|
PosId2 = j ? Sketcher::end : Sketcher::start;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Base::Vector3d dir1 = ((PosId1 == Sketcher::start) ? 1. : -1.) *
|
|
(lineSeg1->getEndPoint()-lineSeg1->getStartPoint());
|
|
Base::Vector3d dir2 = ((PosId2 == Sketcher::start) ? 1. : -1.) *
|
|
(lineSeg2->getEndPoint()-lineSeg2->getStartPoint());
|
|
|
|
// check if the two lines are parallel, in this case an angle is not possible
|
|
Base::Vector3d dir3 = dir1 % dir2;
|
|
if (dir3.Length() < Precision::Intersection()) {
|
|
Base::Vector3d dist = (p1[0] - p2[0]) % dir1;
|
|
if (dist.Sqr() > Precision::Intersection()) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Parallel lines"),
|
|
QObject::tr("An angle constraint cannot be set for two parallel lines."));
|
|
return;
|
|
}
|
|
}
|
|
|
|
double ActAngle = atan2(dir1.x*dir2.y-dir1.y*dir2.x,
|
|
dir1.y*dir2.y+dir1.x*dir2.x);
|
|
if (ActAngle < 0) {
|
|
ActAngle *= -1;
|
|
std::swap(GeoId1,GeoId2);
|
|
std::swap(PosId1,PosId2);
|
|
}
|
|
|
|
openCommand("Add angle constraint");
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),"addConstraint(Sketcher.Constraint('Angle',%d,%d,%d,%d,%f)) ",
|
|
GeoId1,PosId1,GeoId2,PosId2,ActAngle);
|
|
|
|
if (bothexternal || constraintCreationMode==Reference) { // it is a constraint on a external line, make it non-driving
|
|
const std::vector<Sketcher::Constraint *> &ConStr = Obj->Constraints.getValues();
|
|
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),"setDriving(%i,%s)",
|
|
ConStr.size()-1,"False");
|
|
finishDistanceConstraint(this, Obj,false);
|
|
}
|
|
else
|
|
finishDistanceConstraint(this, Obj,true);
|
|
|
|
return;
|
|
}
|
|
} else if (isEdge(GeoId1,PosId1)) { // line angle
|
|
if (GeoId1 < 0 && GeoId1 >= Sketcher::GeoEnum::VAxis) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Cannot add an angle constraint on an axis!"));
|
|
return;
|
|
}
|
|
|
|
const Part::Geometry *geom = Obj->getGeometry(GeoId1);
|
|
if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
|
|
const Part::GeomLineSegment *lineSeg;
|
|
lineSeg = static_cast<const Part::GeomLineSegment*>(geom);
|
|
Base::Vector3d dir = lineSeg->getEndPoint()-lineSeg->getStartPoint();
|
|
double ActAngle = atan2(dir.y,dir.x);
|
|
|
|
openCommand("Add angle constraint");
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),"addConstraint(Sketcher.Constraint('Angle',%d,%f)) ",
|
|
GeoId1,ActAngle);
|
|
|
|
if (GeoId1 <= Sketcher::GeoEnum::RefExt || constraintCreationMode==Reference) { // it is a constraint on a external line, make it non-driving
|
|
const std::vector<Sketcher::Constraint *> &ConStr = Obj->Constraints.getValues();
|
|
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "setDriving(%i,%s)",
|
|
ConStr.size()-1,"False");
|
|
finishDistanceConstraint(this, Obj,false);
|
|
}
|
|
else
|
|
finishDistanceConstraint(this, Obj,true);
|
|
|
|
return;
|
|
}
|
|
else if (geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
|
|
const Part::GeomArcOfCircle *arc;
|
|
arc = static_cast<const Part::GeomArcOfCircle*>(geom);
|
|
double startangle, endangle;
|
|
arc->getRange(startangle, endangle, /*EmulateCCWXY=*/true);
|
|
double angle = endangle - startangle;
|
|
|
|
openCommand("Add angle constraint");
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('Angle',%d,%f)) ",
|
|
GeoId1,angle);
|
|
|
|
if (GeoId1 <= Sketcher::GeoEnum::RefExt || constraintCreationMode==Reference) { // it is a constraint on a external line, make it non-driving
|
|
const std::vector<Sketcher::Constraint *> &ConStr = Obj->Constraints.getValues();
|
|
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "setDriving(%i,%s)",
|
|
ConStr.size()-1,"False");
|
|
finishDistanceConstraint(this, Obj,false);
|
|
}
|
|
else
|
|
finishDistanceConstraint(this, Obj,true);
|
|
|
|
return;
|
|
}
|
|
}
|
|
};
|
|
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select one or two lines from the sketch. Or select two edges and a point."));
|
|
return;
|
|
}
|
|
|
|
void CmdSketcherConstrainAngle::applyConstraint(std::vector<SelIdPair> &selSeq, int seqIndex)
|
|
{
|
|
SketcherGui::ViewProviderSketch* sketchgui = static_cast<SketcherGui::ViewProviderSketch*>(getActiveGuiDocument()->getInEdit());
|
|
Sketcher::SketchObject* Obj = sketchgui->getSketchObject();
|
|
|
|
int GeoId1 = Constraint::GeoUndef, GeoId2 = Constraint::GeoUndef, GeoId3 = Constraint::GeoUndef;
|
|
Sketcher::PointPos PosId1 = Sketcher::none, PosId2 = Sketcher::none, PosId3 = Sketcher::none;
|
|
|
|
switch (seqIndex) {
|
|
case 0: // {SelEdge, SelEdgeOrAxis}
|
|
case 1: // {SelEdgeOrAxis, SelEdge}
|
|
case 2: // {SelEdge, SelExternalEdge}
|
|
case 3: // {SelExternalEdge, SelEdge}
|
|
case 4: // {SelExternalEdge, SelExternalEdge}
|
|
{
|
|
GeoId1 = selSeq.at(0).GeoId; GeoId2 = selSeq.at(1).GeoId;
|
|
|
|
const Part::Geometry *geom1 = Obj->getGeometry(GeoId1);
|
|
const Part::Geometry *geom2 = Obj->getGeometry(GeoId2);
|
|
if (geom1->getTypeId() == Part::GeomLineSegment::getClassTypeId() &&
|
|
geom2->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
|
|
const Part::GeomLineSegment *lineSeg1 = static_cast<const Part::GeomLineSegment*>(geom1);
|
|
const Part::GeomLineSegment *lineSeg2 = static_cast<const Part::GeomLineSegment*>(geom2);
|
|
|
|
// find the two closest line ends
|
|
Sketcher::PointPos PosId1 = Sketcher::none;
|
|
Sketcher::PointPos PosId2 = Sketcher::none;
|
|
Base::Vector3d p1[2], p2[2];
|
|
p1[0] = lineSeg1->getStartPoint();
|
|
p1[1] = lineSeg1->getEndPoint();
|
|
p2[0] = lineSeg2->getStartPoint();
|
|
p2[1] = lineSeg2->getEndPoint();
|
|
|
|
// Get the intersection point in 2d of the two lines if possible
|
|
Base::Line2d line1(Base::Vector2d(p1[0].x, p1[0].y), Base::Vector2d(p1[1].x, p1[1].y));
|
|
Base::Line2d line2(Base::Vector2d(p2[0].x, p2[0].y), Base::Vector2d(p2[1].x, p2[1].y));
|
|
Base::Vector2d s;
|
|
if (line1.Intersect(line2, s)) {
|
|
// get the end points of the line segments that are closest to the intersection point
|
|
Base::Vector3d s3d(s.x, s.y, p1[0].z);
|
|
if (Base::DistanceP2(s3d, p1[0]) < Base::DistanceP2(s3d, p1[1]))
|
|
PosId1 = Sketcher::start;
|
|
else
|
|
PosId1 = Sketcher::end;
|
|
if (Base::DistanceP2(s3d, p2[0]) < Base::DistanceP2(s3d, p2[1]))
|
|
PosId2 = Sketcher::start;
|
|
else
|
|
PosId2 = Sketcher::end;
|
|
}
|
|
else {
|
|
// if all points are collinear
|
|
double length = DBL_MAX;
|
|
for (int i=0; i <= 1; i++) {
|
|
for (int j=0; j <= 1; j++) {
|
|
double tmp = Base::DistanceP2(p2[j], p1[i]);
|
|
if (tmp < length) {
|
|
length = tmp;
|
|
PosId1 = i ? Sketcher::end : Sketcher::start;
|
|
PosId2 = j ? Sketcher::end : Sketcher::start;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Base::Vector3d dir1 = ((PosId1 == Sketcher::start) ? 1. : -1.) *
|
|
(lineSeg1->getEndPoint()-lineSeg1->getStartPoint());
|
|
Base::Vector3d dir2 = ((PosId2 == Sketcher::start) ? 1. : -1.) *
|
|
(lineSeg2->getEndPoint()-lineSeg2->getStartPoint());
|
|
|
|
// check if the two lines are parallel, in this case an angle is not possible
|
|
Base::Vector3d dir3 = dir1 % dir2;
|
|
if (dir3.Length() < Precision::Intersection()) {
|
|
Base::Vector3d dist = (p1[0] - p2[0]) % dir1;
|
|
if (dist.Sqr() > Precision::Intersection()) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Parallel lines"),
|
|
QObject::tr("An angle constraint cannot be set for two parallel lines."));
|
|
return;
|
|
}
|
|
}
|
|
|
|
double ActAngle = atan2(dir1.x*dir2.y-dir1.y*dir2.x,
|
|
dir1.y*dir2.y+dir1.x*dir2.x);
|
|
if (ActAngle < 0) {
|
|
ActAngle *= -1;
|
|
std::swap(GeoId1,GeoId2);
|
|
std::swap(PosId1,PosId2);
|
|
}
|
|
|
|
openCommand("Add angle constraint");
|
|
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Angle',%d,%d,%d,%d,%f)) ",
|
|
GeoId1,PosId1,GeoId2,PosId2,ActAngle);
|
|
|
|
if (areBothPointsOrSegmentsFixed(Obj,GeoId1, GeoId2) || constraintCreationMode==Reference) { // it is a constraint on a external line, make it non-driving
|
|
const std::vector<Sketcher::Constraint *> &ConStr = Obj->Constraints.getValues();
|
|
|
|
Gui::cmdAppObjectArgs(Obj,"setDriving(%i,%s)",
|
|
ConStr.size()-1,"False");
|
|
finishDistanceConstraint(this, Obj,false);
|
|
}
|
|
else
|
|
finishDistanceConstraint(this, Obj,true);
|
|
|
|
return;
|
|
}
|
|
return;
|
|
}
|
|
case 5: // {SelEdge, SelVertexOrRoot, SelEdgeOrAxis}
|
|
case 6: // {SelEdgeOrAxis, SelVertexOrRoot, SelEdge}
|
|
case 7: // {SelEdge, SelVertexOrRoot, SelExternalEdge}
|
|
case 8: // {SelExternalEdge, SelVertexOrRoot, SelEdge}
|
|
case 9: // {SelExternalEdge, SelVertexOrRoot, SelExternalEdge}
|
|
{
|
|
GeoId1 = selSeq.at(0).GeoId; GeoId2 = selSeq.at(2).GeoId; GeoId3 = selSeq.at(1).GeoId;
|
|
PosId3 = selSeq.at(1).PosId;
|
|
break;
|
|
}
|
|
case 10: // {SelVertexOrRoot, SelEdge, SelEdgeOrAxis}
|
|
case 11: // {SelVertexOrRoot, SelEdgeOrAxis, SelEdge}
|
|
case 12: // {SelVertexOrRoot, SelEdge, SelExternalEdge}
|
|
case 13: // {SelVertexOrRoot, SelExternalEdge, SelEdge}
|
|
case 14: // {SelVertexOrRoot, SelExternalEdge, SelExternalEdge}
|
|
{
|
|
GeoId1 = selSeq.at(1).GeoId; GeoId2 = selSeq.at(2).GeoId; GeoId3 = selSeq.at(0).GeoId;
|
|
PosId3 = selSeq.at(0).PosId;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
bool bothexternal=areBothPointsOrSegmentsFixed(Obj,GeoId1, GeoId2);
|
|
|
|
if (isEdge(GeoId1, PosId1) && isEdge(GeoId2, PosId2) && isVertex(GeoId3, PosId3)) {
|
|
double ActAngle = 0.0;
|
|
|
|
openCommand("Add angle constraint");
|
|
|
|
//add missing point-on-object constraints
|
|
if(! IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)){
|
|
Gui::cmdAppObjectArgs(Obj,"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
|
GeoId3,PosId3,GeoId1);
|
|
}
|
|
if(! IsPointAlreadyOnCurve(GeoId2, GeoId3, PosId3, Obj)){
|
|
Gui::cmdAppObjectArgs(Obj,"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
|
GeoId3,PosId3,GeoId2);
|
|
}
|
|
if(! IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)){//FIXME: it's a good idea to add a check if the sketch is solved
|
|
Gui::cmdAppObjectArgs(Obj,"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
|
GeoId3,PosId3,GeoId1);
|
|
}
|
|
|
|
//assuming point-on-curves have been solved, calculate the angle.
|
|
//DeepSOIC: this may be slow, but I wanted to reuse the conversion from Geometry to GCS shapes that is done in Sketch
|
|
Base::Vector3d p = Obj->getPoint(GeoId3, PosId3 );
|
|
ActAngle = Obj->calculateAngleViaPoint(GeoId1,GeoId2,p.x,p.y);
|
|
|
|
//negative constraint value avoidance
|
|
if (ActAngle < -Precision::Angular()){
|
|
std::swap(GeoId1, GeoId2);
|
|
std::swap(PosId1, PosId2);
|
|
ActAngle = -ActAngle;
|
|
}
|
|
|
|
Gui::cmdAppObjectArgs(Obj,"addConstraint(Sketcher.Constraint('AngleViaPoint',%d,%d,%d,%d,%f)) ",
|
|
GeoId1,GeoId2,GeoId3,PosId3,ActAngle);
|
|
|
|
if (bothexternal || constraintCreationMode==Reference) { // it is a constraint on a external line, make it non-driving
|
|
const std::vector<Sketcher::Constraint *> &ConStr = Obj->Constraints.getValues();
|
|
|
|
Gui::cmdAppObjectArgs(Obj,"setDriving(%i,%s)",
|
|
ConStr.size()-1,"False");
|
|
finishDistanceConstraint(this, Obj,false);
|
|
}
|
|
else
|
|
finishDistanceConstraint(this, Obj,true);
|
|
|
|
return;
|
|
};
|
|
|
|
}
|
|
|
|
void CmdSketcherConstrainAngle::updateAction(int mode)
|
|
{
|
|
switch (mode) {
|
|
case Reference:
|
|
if (getAction())
|
|
getAction()->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_InternalAngle_Driven"));
|
|
break;
|
|
case Driving:
|
|
if (getAction())
|
|
getAction()->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_InternalAngle"));
|
|
break;
|
|
}
|
|
}
|
|
|
|
// ======================================================================================
|
|
|
|
class CmdSketcherConstrainEqual : public CmdSketcherConstraint
|
|
{
|
|
public:
|
|
CmdSketcherConstrainEqual();
|
|
virtual ~CmdSketcherConstrainEqual(){}
|
|
virtual const char* className() const
|
|
{ return "CmdSketcherConstrainEqual"; }
|
|
protected:
|
|
virtual void activated(int iMsg);
|
|
virtual void applyConstraint(std::vector<SelIdPair> &selSeq, int seqIndex);
|
|
};
|
|
|
|
CmdSketcherConstrainEqual::CmdSketcherConstrainEqual()
|
|
:CmdSketcherConstraint("Sketcher_ConstrainEqual")
|
|
{
|
|
sAppModule = "Sketcher";
|
|
sGroup = QT_TR_NOOP("Sketcher");
|
|
sMenuText = QT_TR_NOOP("Constrain equal");
|
|
sToolTipText = QT_TR_NOOP("Create an equality constraint between two lines or between circles and arcs");
|
|
sWhatsThis = "Sketcher_ConstrainEqual";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "Constraint_EqualLength";
|
|
sAccel = "E";
|
|
eType = ForEdit;
|
|
|
|
allowedSelSequences = {{SelEdge, SelEdge}, {SelEdge, SelExternalEdge},
|
|
{SelExternalEdge, SelEdge}}; // Only option for equal constraint
|
|
constraintCursor = cursor_genericconstraint;
|
|
}
|
|
|
|
void CmdSketcherConstrainEqual::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
// get the selection
|
|
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
|
|
|
|
// only one sketch with its subelements are allowed to be selected
|
|
if (selection.size() != 1 || !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
|
|
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
|
bool constraintMode = hGrp->GetBool("ContinuousConstraintMode", true);
|
|
|
|
if (constraintMode) {
|
|
ActivateHandler(getActiveGuiDocument(),
|
|
new DrawSketchHandlerGenConstraint(constraintCursor, this));
|
|
getSelection().clearSelection();
|
|
} else {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select two edges from the sketch."));
|
|
}
|
|
return;
|
|
}
|
|
|
|
// get the needed lists and objects
|
|
const std::vector<std::string> &SubNames = selection[0].getSubNames();
|
|
Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
|
|
|
|
// go through the selected subelements
|
|
|
|
if (SubNames.size() < 2) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select at least two lines from the sketch."));
|
|
return;
|
|
}
|
|
|
|
std::vector<int> ids;
|
|
bool lineSel = false, arcSel = false, circSel = false, ellipsSel = false, arcEllipsSel=false, hasAlreadyExternal = false;
|
|
bool hyperbSel = false, parabSel=false;
|
|
|
|
for (std::vector<std::string>::const_iterator it=SubNames.begin(); it != SubNames.end(); ++it) {
|
|
|
|
int GeoId;
|
|
Sketcher::PointPos PosId;
|
|
getIdsFromName(*it, Obj, GeoId, PosId);
|
|
|
|
if (!isEdge(GeoId,PosId)) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select two or more compatible edges"));
|
|
return;
|
|
}
|
|
else if (GeoId == Sketcher::GeoEnum::HAxis || GeoId == Sketcher::GeoEnum::VAxis) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Sketch axes cannot be used in equality constraints"));
|
|
return;
|
|
}
|
|
else if (isPointOrSegmentFixed(Obj,GeoId)) {
|
|
|
|
if (hasAlreadyExternal) {
|
|
showNoConstraintBetweenFixedGeometry();
|
|
return;
|
|
}
|
|
else {
|
|
hasAlreadyExternal = true;
|
|
}
|
|
}
|
|
|
|
const Part::Geometry *geo = Obj->getGeometry(GeoId);
|
|
|
|
if(geo->getTypeId() == Part::GeomBSplineCurve::getClassTypeId()) {
|
|
// unsupported as they are generally hereogeneus shapes
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Equality for B-spline edge currently unsupported."));
|
|
return;
|
|
}
|
|
|
|
if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId())
|
|
lineSel = true;
|
|
else if (geo->getTypeId() == Part::GeomArcOfCircle::getClassTypeId())
|
|
arcSel = true;
|
|
else if (geo->getTypeId() == Part::GeomCircle::getClassTypeId())
|
|
circSel = true;
|
|
else if (geo->getTypeId() == Part::GeomEllipse::getClassTypeId())
|
|
ellipsSel = true;
|
|
else if (geo->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId())
|
|
arcEllipsSel = true;
|
|
else if (geo->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId())
|
|
hyperbSel = true;
|
|
else if (geo->getTypeId() == Part::GeomArcOfParabola::getClassTypeId())
|
|
parabSel = true;
|
|
else {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select two or more edges of similar type"));
|
|
return;
|
|
}
|
|
|
|
ids.push_back(GeoId);
|
|
}
|
|
|
|
// Check for heterogeneous groups in selection
|
|
if ( (lineSel && ((arcSel || circSel) || (ellipsSel || arcEllipsSel) || hyperbSel || parabSel) ) ||
|
|
((arcSel || circSel) && ((ellipsSel || arcEllipsSel) || hyperbSel || parabSel)) ||
|
|
((ellipsSel || arcEllipsSel) && (hyperbSel || parabSel)) ||
|
|
(hyperbSel && parabSel) ) {
|
|
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select two or more edges of similar type"));
|
|
return;
|
|
}
|
|
|
|
// undo command open
|
|
openCommand("add equality constraint");
|
|
for (int i=0; i < int(ids.size()-1); i++) {
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('Equal',%d,%d)) ",
|
|
ids[i],ids[i+1]);
|
|
}
|
|
// finish the transaction and update
|
|
commitCommand();
|
|
tryAutoRecompute(Obj);
|
|
|
|
// clear the selection (convenience)
|
|
getSelection().clearSelection();
|
|
}
|
|
|
|
void CmdSketcherConstrainEqual::applyConstraint(std::vector<SelIdPair> &selSeq, int seqIndex)
|
|
{
|
|
SketcherGui::ViewProviderSketch* sketchgui = static_cast<SketcherGui::ViewProviderSketch*>(getActiveGuiDocument()->getInEdit());
|
|
Sketcher::SketchObject* Obj = sketchgui->getSketchObject();
|
|
QString strError;
|
|
|
|
int GeoId1 = Constraint::GeoUndef, GeoId2 = Constraint::GeoUndef;
|
|
|
|
switch (seqIndex) {
|
|
case 0: // {SelEdge, SelEdge}
|
|
case 1: // {SelEdge, SelExternalEdge}
|
|
case 2: // {SelExternalEdge, SelEdge}
|
|
{
|
|
GeoId1 = selSeq.at(0).GeoId; GeoId2 = selSeq.at(1).GeoId;
|
|
|
|
// check if the edge already has a Block constraint
|
|
if ( areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2) ) {
|
|
showNoConstraintBetweenFixedGeometry();
|
|
return;
|
|
}
|
|
|
|
// undo command open
|
|
openCommand("add equality constraint");
|
|
Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Equal',%d,%d)) ",
|
|
GeoId1, GeoId2);
|
|
// finish the transaction and update
|
|
commitCommand();
|
|
tryAutoRecompute(Obj);
|
|
|
|
return;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// ======================================================================================
|
|
|
|
class CmdSketcherConstrainSymmetric : public CmdSketcherConstraint
|
|
{
|
|
public:
|
|
CmdSketcherConstrainSymmetric();
|
|
virtual ~CmdSketcherConstrainSymmetric(){}
|
|
virtual const char* className() const
|
|
{ return "CmdSketcherConstrainSymmetric"; }
|
|
protected:
|
|
virtual void activated(int iMsg);
|
|
virtual void applyConstraint(std::vector<SelIdPair> &selSeq, int seqIndex);
|
|
};
|
|
|
|
CmdSketcherConstrainSymmetric::CmdSketcherConstrainSymmetric()
|
|
:CmdSketcherConstraint("Sketcher_ConstrainSymmetric")
|
|
{
|
|
sAppModule = "Sketcher";
|
|
sGroup = QT_TR_NOOP("Sketcher");
|
|
sMenuText = QT_TR_NOOP("Constrain symmetrical");
|
|
sToolTipText = QT_TR_NOOP("Create a symmetry constraint between two points with respect to a line or a third point");
|
|
sWhatsThis = "Sketcher_ConstrainSymmetric";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "Constraint_Symmetric";
|
|
sAccel = "S";
|
|
eType = ForEdit;
|
|
|
|
allowedSelSequences = {{SelEdge, SelVertexOrRoot},
|
|
{SelExternalEdge, SelVertex},
|
|
{SelVertex, SelEdge, SelVertexOrRoot},
|
|
{SelRoot, SelEdge, SelVertex},
|
|
{SelVertex, SelExternalEdge, SelVertexOrRoot},
|
|
{SelRoot, SelExternalEdge, SelVertex},
|
|
{SelVertex, SelEdgeOrAxis, SelVertex},
|
|
{SelVertex, SelVertexOrRoot, SelVertex},
|
|
{SelVertex, SelVertex, SelVertexOrRoot},
|
|
{SelVertexOrRoot, SelVertex, SelVertex}};
|
|
constraintCursor = cursor_genericconstraint;
|
|
}
|
|
|
|
void CmdSketcherConstrainSymmetric::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
// get the selection
|
|
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
|
|
|
|
// only one sketch with its subelements are allowed to be selected
|
|
if (selection.size() != 1 || !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
|
|
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
|
|
bool constraintMode = hGrp->GetBool("ContinuousConstraintMode", true);
|
|
|
|
if (constraintMode) {
|
|
ActivateHandler(getActiveGuiDocument(),
|
|
new DrawSketchHandlerGenConstraint(constraintCursor, this));
|
|
getSelection().clearSelection();
|
|
} else {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select two points and a symmetry line, two points and a symmetry point "
|
|
"or a line and a symmetry point from the sketch."));
|
|
}
|
|
return;
|
|
}
|
|
|
|
// get the needed lists and objects
|
|
const std::vector<std::string> &SubNames = selection[0].getSubNames();
|
|
Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
|
|
|
|
if (SubNames.size() != 3 && SubNames.size() != 2) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select two points and a symmetry line, two points and a symmetry point "
|
|
"or a line and a symmetry point from the sketch."));
|
|
return;
|
|
}
|
|
|
|
int GeoId1, GeoId2, GeoId3;
|
|
Sketcher::PointPos PosId1, PosId2, PosId3;
|
|
getIdsFromName(SubNames[0], Obj, GeoId1, PosId1);
|
|
getIdsFromName(SubNames[1], Obj, GeoId2, PosId2);
|
|
|
|
if (SubNames.size() == 2) {
|
|
if ( areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2) ) {
|
|
showNoConstraintBetweenFixedGeometry();
|
|
return;
|
|
}
|
|
if (isVertex(GeoId1,PosId1) && isEdge(GeoId2,PosId2)) {
|
|
std::swap(GeoId1,GeoId2);
|
|
std::swap(PosId1,PosId2);
|
|
}
|
|
if (isEdge(GeoId1,PosId1) && isVertex(GeoId2,PosId2)) {
|
|
const Part::Geometry *geom = Obj->getGeometry(GeoId1);
|
|
if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
|
|
if (GeoId1 == GeoId2) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Cannot add a symmetry constraint between a line and its end points!"));
|
|
return;
|
|
}
|
|
|
|
// undo command open
|
|
openCommand("add symmetric constraint");
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),
|
|
"addConstraint(Sketcher.Constraint('Symmetric',%d,%d,%d,%d,%d,%d)) ",
|
|
GeoId1,Sketcher::start,GeoId1,Sketcher::end,GeoId2,PosId2);
|
|
|
|
// finish the transaction and update
|
|
commitCommand();
|
|
tryAutoRecompute(Obj);
|
|
|
|
// clear the selection (convenience)
|
|
getSelection().clearSelection();
|
|
return;
|
|
}
|
|
}
|
|
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select two points and a symmetry line, two points and a symmetry point "
|
|
"or a line and a symmetry point from the sketch."));
|
|
return;
|
|
}
|
|
|
|
getIdsFromName(SubNames[2], Obj, GeoId3, PosId3);
|
|
|
|
if (isEdge(GeoId1,PosId1) && isVertex(GeoId3,PosId3)) {
|
|
std::swap(GeoId1,GeoId3);
|
|
std::swap(PosId1,PosId3);
|
|
}
|
|
else if (isEdge(GeoId2,PosId2) && isVertex(GeoId3,PosId3)) {
|
|
std::swap(GeoId2,GeoId3);
|
|
std::swap(PosId2,PosId3);
|
|
}
|
|
|
|
if ( areAllPointsOrSegmentsFixed(Obj, GeoId1, GeoId2, GeoId3) ) {
|
|
showNoConstraintBetweenFixedGeometry();
|
|
return;
|
|
}
|
|
|
|
if (isVertex(GeoId1,PosId1) &&
|
|
isVertex(GeoId2,PosId2)) {
|
|
|
|
if (isEdge(GeoId3,PosId3)) {
|
|
const Part::Geometry *geom = Obj->getGeometry(GeoId3);
|
|
if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
|
|
if (GeoId1 == GeoId2 && GeoId2 == GeoId3) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Cannot add a symmetry constraint between a line and its end points!"));
|
|
return;
|
|
}
|
|
|
|
// undo command open
|
|
openCommand("add symmetric constraint");
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),
|
|
"addConstraint(Sketcher.Constraint('Symmetric',%d,%d,%d,%d,%d)) ",
|
|
GeoId1,PosId1,GeoId2,PosId2,GeoId3);
|
|
|
|
// finish the transaction and update
|
|
commitCommand();
|
|
tryAutoRecompute(Obj);
|
|
|
|
// clear the selection (convenience)
|
|
getSelection().clearSelection();
|
|
return;
|
|
}
|
|
}
|
|
else if (isVertex(GeoId3,PosId3)) {
|
|
// undo command open
|
|
openCommand("add symmetric constraint");
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),
|
|
"addConstraint(Sketcher.Constraint('Symmetric',%d,%d,%d,%d,%d,%d)) ",
|
|
GeoId1,PosId1,GeoId2,PosId2,GeoId3,PosId3);
|
|
|
|
// finish the transaction and update
|
|
commitCommand();
|
|
tryAutoRecompute(Obj);
|
|
|
|
// clear the selection (convenience)
|
|
getSelection().clearSelection();
|
|
return;
|
|
}
|
|
}
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select two points and a symmetry line, two points and a symmetry point "
|
|
"or a line and a symmetry point from the sketch."));
|
|
}
|
|
|
|
void CmdSketcherConstrainSymmetric::applyConstraint(std::vector<SelIdPair> &selSeq, int seqIndex)
|
|
{
|
|
SketcherGui::ViewProviderSketch* sketchgui = static_cast<SketcherGui::ViewProviderSketch*>(getActiveGuiDocument()->getInEdit());
|
|
Sketcher::SketchObject* Obj = sketchgui->getSketchObject();
|
|
QString strError;
|
|
|
|
int GeoId1 = Constraint::GeoUndef, GeoId2 = Constraint::GeoUndef, GeoId3 = Constraint::GeoUndef;
|
|
Sketcher::PointPos PosId1 = Sketcher::none, PosId2 = Sketcher::none, PosId3 = Sketcher::none;
|
|
|
|
switch (seqIndex) {
|
|
case 0: // {SelEdge, SelVertexOrRoot}
|
|
case 1: // {SelExternalEdge, SelVertex}
|
|
{
|
|
GeoId1 = GeoId2 = selSeq.at(0).GeoId; GeoId3 = selSeq.at(1).GeoId;
|
|
PosId1 = Sketcher::start; PosId2 = Sketcher::end; PosId3 = selSeq.at(1).PosId;
|
|
|
|
if (GeoId1 == GeoId3) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Cannot add a symmetry constraint between a line and its end points!"));
|
|
return;
|
|
}
|
|
|
|
if ( areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2) ) {
|
|
showNoConstraintBetweenFixedGeometry();
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
case 2: // {SelVertex, SelEdge, SelVertexOrRoot}
|
|
case 3: // {SelRoot, SelEdge, SelVertex}
|
|
case 4: // {SelVertex, SelExternalEdge, SelVertexOrRoot}
|
|
case 5: // {SelRoot, SelExternalEdge, SelVertex}
|
|
case 6: // {SelVertex, SelEdgeOrAxis, SelVertex}
|
|
{
|
|
GeoId1 = selSeq.at(0).GeoId; GeoId2 = selSeq.at(2).GeoId; GeoId3 = selSeq.at(1).GeoId;
|
|
PosId1 = selSeq.at(0).PosId; PosId2 = selSeq.at(2).PosId;
|
|
|
|
if ( areAllPointsOrSegmentsFixed(Obj, GeoId1, GeoId2, GeoId3) ) {
|
|
showNoConstraintBetweenFixedGeometry();
|
|
return;
|
|
}
|
|
const Part::Geometry *geom = Obj->getGeometry(GeoId3);
|
|
if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
|
|
if (GeoId1 == GeoId2 && GeoId2 == GeoId3) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Cannot add a symmetry constraint between a line and its end points!"));
|
|
return;
|
|
}
|
|
|
|
// undo command open
|
|
openCommand("add symmetric constraint");
|
|
Gui::cmdAppObjectArgs(Obj,"addConstraint(Sketcher.Constraint('Symmetric',%d,%d,%d,%d,%d)) ",
|
|
GeoId1,PosId1,GeoId2,PosId2,GeoId3);
|
|
|
|
// finish the transaction and update
|
|
commitCommand();
|
|
tryAutoRecompute(Obj);
|
|
}
|
|
else {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select two points and a symmetry line, two points and a symmetry point "
|
|
"or a line and a symmetry point from the sketch."));
|
|
}
|
|
|
|
return;
|
|
}
|
|
case 7: // {SelVertex, SelVertexOrRoot, SelVertex}
|
|
case 8: // {SelVertex, SelVertex, SelVertexOrRoot}
|
|
case 9: // {SelVertexOrRoot, SelVertex, SelVertex}
|
|
{
|
|
GeoId1 = selSeq.at(0).GeoId; GeoId2 = selSeq.at(1).GeoId; GeoId3 = selSeq.at(2).GeoId;
|
|
PosId1 = selSeq.at(0).PosId; PosId2 = selSeq.at(1).PosId; PosId3 = selSeq.at(2).PosId;
|
|
|
|
if ( areAllPointsOrSegmentsFixed(Obj, GeoId1, GeoId2, GeoId3) ) {
|
|
showNoConstraintBetweenFixedGeometry();
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// undo command open
|
|
openCommand("add symmetric constraint");
|
|
Gui::cmdAppObjectArgs(Obj,"addConstraint(Sketcher.Constraint('Symmetric',%d,%d,%d,%d,%d,%d)) ",
|
|
GeoId1,PosId1,GeoId2,PosId2,GeoId3,PosId3);
|
|
|
|
// finish the transaction and update
|
|
commitCommand();
|
|
|
|
tryAutoRecompute(Obj);
|
|
|
|
// clear the selection (convenience)
|
|
getSelection().clearSelection();
|
|
return;
|
|
}
|
|
|
|
// ======================================================================================
|
|
|
|
DEF_STD_CMD_A(CmdSketcherConstrainSnellsLaw)
|
|
|
|
CmdSketcherConstrainSnellsLaw::CmdSketcherConstrainSnellsLaw()
|
|
:Command("Sketcher_ConstrainSnellsLaw")
|
|
{
|
|
sAppModule = "Sketcher";
|
|
sGroup = QT_TR_NOOP("Sketcher");
|
|
sMenuText = QT_TR_NOOP("Constrain refraction (Snell's law')");
|
|
sToolTipText = QT_TR_NOOP("Create a refraction law (Snell's law) constraint between two endpoints of rays and an edge as an interface.");
|
|
sWhatsThis = "Sketcher_ConstrainSnellsLaw";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "Constraint_SnellsLaw";
|
|
sAccel = "";
|
|
eType = ForEdit;
|
|
}
|
|
|
|
void CmdSketcherConstrainSnellsLaw::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
QString strHelp = QObject::tr("Select two endpoints of lines to act as rays, and"
|
|
" an edge representing a boundary. The first"
|
|
" selected point corresponds to index n1, second"
|
|
" - to n2, and datum value sets the ratio n2/n1.",
|
|
"Constraint_SnellsLaw");
|
|
QString strError;
|
|
|
|
const char dmbg[] = "Constraint_SnellsLaw";
|
|
|
|
try{
|
|
// get the selection
|
|
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
|
|
|
|
// only one sketch with its subelements are allowed to be selected
|
|
if (selection.size() != 1 || !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
|
|
strError = QObject::tr("Selected objects are not just geometry from one sketch.", dmbg);
|
|
throw Base::ValueError("");
|
|
}
|
|
|
|
// get the needed lists and objects
|
|
Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
|
|
const std::vector<std::string> &SubNames = selection[0].getSubNames();
|
|
|
|
if (SubNames.size() != 3) {
|
|
strError = QObject::tr("Number of selected objects is not 3 (is %1).", dmbg).arg(SubNames.size());
|
|
throw Base::ValueError("");
|
|
}
|
|
|
|
int GeoId1, GeoId2, GeoId3;
|
|
Sketcher::PointPos PosId1, PosId2, PosId3;
|
|
getIdsFromName(SubNames[0], Obj, GeoId1, PosId1);
|
|
getIdsFromName(SubNames[1], Obj, GeoId2, PosId2);
|
|
getIdsFromName(SubNames[2], Obj, GeoId3, PosId3);
|
|
|
|
//sink the edge to be the last item
|
|
if (isEdge(GeoId1,PosId1) ) {
|
|
std::swap(GeoId1,GeoId2);
|
|
std::swap(PosId1,PosId2);
|
|
}
|
|
if (isEdge(GeoId2,PosId2) ) {
|
|
std::swap(GeoId2,GeoId3);
|
|
std::swap(PosId2,PosId3);
|
|
}
|
|
|
|
//a bunch of validity checks
|
|
if (areAllPointsOrSegmentsFixed(Obj, GeoId1, GeoId2, GeoId3) ) {
|
|
strError = QObject::tr("Cannot create constraint with external geometry only!!", dmbg);
|
|
throw Base::ValueError("");
|
|
}
|
|
|
|
if (!(isVertex(GeoId1,PosId1) && !isSimpleVertex(Obj, GeoId1, PosId1) &&
|
|
isVertex(GeoId2,PosId2) && !isSimpleVertex(Obj, GeoId2, PosId2) &&
|
|
isEdge(GeoId3,PosId3) )) {
|
|
strError = QObject::tr("Incompatible geometry is selected!", dmbg);
|
|
throw Base::ValueError("");
|
|
};
|
|
|
|
const Part::Geometry *geo = Obj->getGeometry(GeoId3);
|
|
|
|
if( geo && geo->getTypeId() == Part::GeomBSplineCurve::getClassTypeId() ){
|
|
// unsupported until normal to B-spline at any point implemented.
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("SnellsLaw on B-spline edge currently unsupported."));
|
|
return;
|
|
}
|
|
|
|
double n2divn1=0;
|
|
|
|
//the essence.
|
|
//Unlike other constraints, we'll ask for a value immediately.
|
|
QDialog dlg(Gui::getMainWindow());
|
|
Ui::InsertDatum ui_Datum;
|
|
ui_Datum.setupUi(&dlg);
|
|
dlg.setWindowTitle(EditDatumDialog::tr("Refractive index ratio", dmbg));
|
|
ui_Datum.label->setText(EditDatumDialog::tr("Ratio n2/n1:", dmbg));
|
|
Base::Quantity init_val;
|
|
init_val.setUnit(Base::Unit());
|
|
init_val.setValue(0.0);
|
|
|
|
ui_Datum.labelEdit->setValue(init_val);
|
|
ui_Datum.labelEdit->setParamGrpPath(QByteArray("User parameter:BaseApp/History/SketcherRefrIndexRatio"));
|
|
ui_Datum.labelEdit->setToLastUsedValue();
|
|
ui_Datum.labelEdit->selectNumber();
|
|
// Unable to bind, because the constraint does not yet exist
|
|
|
|
if (dlg.exec() != QDialog::Accepted) return;
|
|
ui_Datum.labelEdit->pushToHistory();
|
|
|
|
Base::Quantity newQuant = ui_Datum.labelEdit->value();
|
|
n2divn1 = newQuant.getValue();
|
|
|
|
//add constraint
|
|
openCommand("add Snell's law constraint");
|
|
|
|
if (! IsPointAlreadyOnCurve(GeoId2,GeoId1,PosId1,Obj))
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),
|
|
"addConstraint(Sketcher.Constraint('Coincident',%d,%d,%d,%d)) ",
|
|
GeoId1,PosId1,GeoId2,PosId2);
|
|
|
|
if (! IsPointAlreadyOnCurve(GeoId3,GeoId1,PosId1,Obj))
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),
|
|
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
|
|
GeoId1,PosId1,GeoId3);
|
|
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('SnellsLaw',%d,%d,%d,%d,%d,%.12f)) ",
|
|
GeoId1,PosId1,GeoId2,PosId2,GeoId3,n2divn1);
|
|
|
|
/*if (allexternal || constraintCreationMode==Reference) { // it is a constraint on a external line, make it non-driving
|
|
const std::vector<Sketcher::Constraint *> &ConStr = Obj->Constraints.getValues();
|
|
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),"setDriving(%i,%s)",
|
|
ConStr.size()-1,"False");
|
|
}*/
|
|
|
|
commitCommand();
|
|
tryAutoRecompute(Obj);
|
|
|
|
// clear the selection (convenience)
|
|
getSelection().clearSelection();
|
|
} catch (Base::Exception &e) {
|
|
if (strError.isEmpty()) strError = QString::fromLatin1(e.what());
|
|
if (!strError.isEmpty()) strError.append(QString::fromLatin1("\n\n"));
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Error"), strError + strHelp);
|
|
}
|
|
}
|
|
|
|
bool CmdSketcherConstrainSnellsLaw::isActive(void)
|
|
{
|
|
return isCreateConstraintActive( getActiveGuiDocument() );
|
|
}
|
|
|
|
// ======================================================================================
|
|
|
|
DEF_STD_CMD_A(CmdSketcherConstrainInternalAlignment)
|
|
|
|
CmdSketcherConstrainInternalAlignment::CmdSketcherConstrainInternalAlignment()
|
|
:Command("Sketcher_ConstrainInternalAlignment")
|
|
{
|
|
sAppModule = "Sketcher";
|
|
sGroup = QT_TR_NOOP("Sketcher");
|
|
sMenuText = QT_TR_NOOP("Constrain InternalAlignment");
|
|
sToolTipText = QT_TR_NOOP("Constrains an element to be aligned with the internal geometry of another element");
|
|
sWhatsThis = "Sketcher_ConstrainInternalAlignment";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "Constraint_InternalAlignment";
|
|
sAccel = "Ctrl+A";
|
|
eType = ForEdit;
|
|
}
|
|
|
|
void CmdSketcherConstrainInternalAlignment::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
// get the selection
|
|
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx();
|
|
|
|
// only one sketch with its subelements are allowed to be selected
|
|
if (selection.size() != 1 || !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select at least one ellipse and one edge from the sketch."));
|
|
return;
|
|
}
|
|
|
|
// get the needed lists and objects
|
|
const std::vector<std::string> &SubNames = selection[0].getSubNames();
|
|
Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
|
|
|
|
// go through the selected subelements
|
|
if (SubNames.size() < 2) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select at least one ellipse and one edge from the sketch."));
|
|
return;
|
|
}
|
|
|
|
std::vector<int> pointids;
|
|
std::vector<int> lineids;
|
|
std::vector<int> ellipseids;
|
|
std::vector<int> arcsofellipseids;
|
|
|
|
bool hasAlreadyExternal = false;
|
|
|
|
for (std::vector<std::string>::const_iterator it=SubNames.begin(); it != SubNames.end(); ++it) {
|
|
|
|
int GeoId;
|
|
Sketcher::PointPos PosId;
|
|
getIdsFromName(*it, Obj, GeoId, PosId);
|
|
|
|
if (isPointOrSegmentFixed(Obj,GeoId)) {
|
|
if (GeoId == Sketcher::GeoEnum::HAxis || GeoId == Sketcher::GeoEnum::VAxis) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Sketch axes cannot be used in internal alignment constraint"));
|
|
return;
|
|
}
|
|
else if (hasAlreadyExternal) {
|
|
showNoConstraintBetweenExternal();
|
|
return;
|
|
}
|
|
else
|
|
hasAlreadyExternal = true;
|
|
}
|
|
|
|
const Part::Geometry *geo = Obj->getGeometry(GeoId);
|
|
|
|
if (geo->getTypeId() == Part::GeomPoint::getClassTypeId())
|
|
pointids.push_back(GeoId);
|
|
else if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId())
|
|
lineids.push_back(GeoId);
|
|
else if (geo->getTypeId() == Part::GeomEllipse::getClassTypeId())
|
|
ellipseids.push_back(GeoId);
|
|
else if (geo->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId())
|
|
arcsofellipseids.push_back(GeoId);
|
|
else {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select two or more compatible edges"));
|
|
return;
|
|
}
|
|
}
|
|
|
|
int GeoId;
|
|
Sketcher::PointPos PosId;
|
|
getIdsFromName(SubNames[SubNames.size()-1], Obj, GeoId, PosId); // last selected element
|
|
|
|
const Part::Geometry *geo = Obj->getGeometry(GeoId);
|
|
|
|
// Currently it is only supported for ellipses
|
|
if(geo->getTypeId() == Part::GeomEllipse::getClassTypeId()) {
|
|
|
|
// Priority list
|
|
// EllipseMajorDiameter = 1,
|
|
// EllipseMinorDiameter = 2,
|
|
// EllipseFocus1 = 3,
|
|
// EllipseFocus2 = 4
|
|
|
|
if(ellipseids.size()>1){
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("You cannot internally constrain an ellipse on other ellipse. Select only one ellipse."));
|
|
return;
|
|
}
|
|
|
|
if (pointids.size()>2) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Maximum 2 points are supported."));
|
|
return;
|
|
}
|
|
|
|
if (lineids.size()>2) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Maximum 2 lines are supported."));
|
|
return;
|
|
}
|
|
|
|
// look for which internal constraints are already applied
|
|
bool major=false;
|
|
bool minor=false;
|
|
bool focus1=false;
|
|
bool focus2=false;
|
|
bool extra_elements=false;
|
|
|
|
const std::vector< Sketcher::Constraint * > &vals = Obj->Constraints.getValues();
|
|
|
|
for (std::vector< Sketcher::Constraint * >::const_iterator it= vals.begin();
|
|
it != vals.end(); ++it) {
|
|
if((*it)->Type == Sketcher::InternalAlignment && (*it)->Second == GeoId)
|
|
{
|
|
switch((*it)->AlignmentType){
|
|
case Sketcher::EllipseMajorDiameter:
|
|
major=true;
|
|
break;
|
|
case Sketcher::EllipseMinorDiameter:
|
|
minor=true;
|
|
break;
|
|
case Sketcher::EllipseFocus1:
|
|
focus1=true;
|
|
break;
|
|
case Sketcher::EllipseFocus2:
|
|
focus2=true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(major && minor && focus1 && focus2) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Nothing to constrain"),
|
|
QObject::tr("Currently all internal geometry of the ellipse is already exposed."));
|
|
return;
|
|
}
|
|
|
|
if((!(focus1 && focus2) && pointids.size()>=1) || // if some element is missing and we are adding an element of that type
|
|
(!(major && minor) && lineids.size()>=1) ){
|
|
|
|
openCommand("add internal alignment constraint");
|
|
|
|
if(pointids.size()>=1)
|
|
{
|
|
if(!focus1) {
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),
|
|
"addConstraint(Sketcher.Constraint('InternalAlignment:EllipseFocus1',%d,%d,%d)) ",
|
|
pointids[0],Sketcher::start,ellipseids[0]);
|
|
}
|
|
else if(!focus2) {
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),
|
|
"addConstraint(Sketcher.Constraint('InternalAlignment:EllipseFocus2',%d,%d,%d)) ",
|
|
pointids[0],Sketcher::start,ellipseids[0]);
|
|
focus2=true;
|
|
}
|
|
else
|
|
extra_elements=true;
|
|
}
|
|
|
|
if(pointids.size()==2)
|
|
{
|
|
if(!focus2) {
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),
|
|
"addConstraint(Sketcher.Constraint('InternalAlignment:EllipseFocus2',%d,%d,%d)) ",
|
|
pointids[1],Sketcher::start,ellipseids[0]);
|
|
}
|
|
else
|
|
extra_elements=true;
|
|
}
|
|
|
|
if(lineids.size()>=1)
|
|
{
|
|
if(!major) {
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),
|
|
"addConstraint(Sketcher.Constraint('InternalAlignment:EllipseMajorDiameter',%d,%d)) ",
|
|
lineids[0],ellipseids[0]);
|
|
|
|
const Part::GeomLineSegment *geo = static_cast<const Part::GeomLineSegment *>(Obj->getGeometry(lineids[0]));
|
|
|
|
if(!geo->Construction)
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),"toggleConstruction(%d) ",lineids[0]);
|
|
|
|
}
|
|
else if(!minor) {
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),
|
|
"addConstraint(Sketcher.Constraint('InternalAlignment:EllipseMinorDiameter',%d,%d)) ",
|
|
lineids[0],ellipseids[0]);
|
|
|
|
const Part::GeomLineSegment *geo = static_cast<const Part::GeomLineSegment *>(Obj->getGeometry(lineids[0]));
|
|
|
|
if(!geo->Construction)
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),"toggleConstruction(%d) ",lineids[0]);
|
|
|
|
minor=true;
|
|
}
|
|
else
|
|
extra_elements=true;
|
|
}
|
|
if(lineids.size()==2)
|
|
{
|
|
if(!minor){
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),
|
|
"addConstraint(Sketcher.Constraint('InternalAlignment:EllipseMinorDiameter',%d,%d)) ",
|
|
lineids[1],ellipseids[0]);
|
|
|
|
const Part::GeomLineSegment *geo = static_cast<const Part::GeomLineSegment *>(Obj->getGeometry(lineids[1]));
|
|
|
|
if(!geo->Construction)
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),"toggleConstruction(%d) ",lineids[1]);
|
|
}
|
|
else
|
|
extra_elements=true;
|
|
}
|
|
|
|
// finish the transaction and update
|
|
commitCommand();
|
|
|
|
tryAutoRecompute(Obj);
|
|
|
|
if(extra_elements){
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Extra elements"),
|
|
QObject::tr("More elements than possible for the given ellipse were provided. These were ignored."));
|
|
}
|
|
|
|
// clear the selection (convenience)
|
|
getSelection().clearSelection();
|
|
}
|
|
else {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Extra elements"),
|
|
QObject::tr("More elements than possible for the given ellipse were provided. These were ignored."));
|
|
}
|
|
}
|
|
else if(geo->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()) {
|
|
|
|
// Priority list
|
|
// EllipseMajorDiameter = 1,
|
|
// EllipseMinorDiameter = 2,
|
|
// EllipseFocus1 = 3,
|
|
// EllipseFocus2 = 4
|
|
|
|
if(arcsofellipseids.size()>1){
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("You cannot internally constrain an arc of ellipse on another arc of ellipse. Select only one arc of ellipse."));
|
|
return;
|
|
}
|
|
|
|
if(ellipseids.size()>0){
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("You cannot internally constrain an ellipse on an arc of ellipse. Select only one ellipse or arc of ellipse."));
|
|
return;
|
|
}
|
|
|
|
if (pointids.size()>2) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Maximum 2 points are supported."));
|
|
return;
|
|
}
|
|
|
|
if (lineids.size()>2) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Maximum 2 lines are supported."));
|
|
return;
|
|
}
|
|
|
|
// look for which internal constraints are already applied
|
|
bool major=false;
|
|
bool minor=false;
|
|
bool focus1=false;
|
|
bool focus2=false;
|
|
bool extra_elements=false;
|
|
|
|
const std::vector< Sketcher::Constraint * > &vals = Obj->Constraints.getValues();
|
|
|
|
for (std::vector< Sketcher::Constraint * >::const_iterator it= vals.begin();
|
|
it != vals.end(); ++it) {
|
|
if((*it)->Type == Sketcher::InternalAlignment && (*it)->First == GeoId)
|
|
{
|
|
switch((*it)->AlignmentType){
|
|
case Sketcher::EllipseMajorDiameter:
|
|
major=true;
|
|
break;
|
|
case Sketcher::EllipseMinorDiameter:
|
|
minor=true;
|
|
break;
|
|
case Sketcher::EllipseFocus1:
|
|
focus1=true;
|
|
break;
|
|
case Sketcher::EllipseFocus2:
|
|
focus2=true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(major && minor && focus1 && focus2) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Nothing to constrain"),
|
|
QObject::tr("Currently all internal geometry of the arc of ellipse is already exposed."));
|
|
return;
|
|
}
|
|
|
|
if((!(focus1 && focus2) && pointids.size()>=1) || // if some element is missing and we are adding an element of that type
|
|
(!(major && minor) && lineids.size()>=1) ){
|
|
|
|
openCommand("add internal alignment constraint");
|
|
|
|
if(pointids.size()>=1)
|
|
{
|
|
if(!focus1) {
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),
|
|
"addConstraint(Sketcher.Constraint('InternalAlignment:EllipseFocus1',%d,%d,%d)) ",
|
|
pointids[0],Sketcher::start,arcsofellipseids[0]);
|
|
}
|
|
else if(!focus2) {
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),
|
|
"addConstraint(Sketcher.Constraint('InternalAlignment:EllipseFocus2',%d,%d,%d)) ",
|
|
pointids[0],Sketcher::start,arcsofellipseids[0]);
|
|
focus2=true;
|
|
}
|
|
else
|
|
extra_elements=true;
|
|
}
|
|
|
|
if(pointids.size()==2)
|
|
{
|
|
if(!focus2) {
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),
|
|
"addConstraint(Sketcher.Constraint('InternalAlignment:EllipseFocus2',%d,%d,%d)) ",
|
|
pointids[1],Sketcher::start,arcsofellipseids[0]);
|
|
}
|
|
else
|
|
extra_elements=true;
|
|
}
|
|
|
|
if(lineids.size()>=1)
|
|
{
|
|
if(!major) {
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),
|
|
"addConstraint(Sketcher.Constraint('InternalAlignment:EllipseMajorDiameter',%d,%d)) ",
|
|
lineids[0],arcsofellipseids[0]);
|
|
|
|
const Part::GeomLineSegment *geo = static_cast<const Part::GeomLineSegment *>(Obj->getGeometry(lineids[0]));
|
|
|
|
if(!geo->Construction)
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),"toggleConstruction(%d) ",lineids[0]);
|
|
|
|
}
|
|
else if(!minor) {
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),
|
|
"addConstraint(Sketcher.Constraint('InternalAlignment:EllipseMinorDiameter',%d,%d)) ",
|
|
lineids[0],arcsofellipseids[0]);
|
|
|
|
const Part::GeomLineSegment *geo = static_cast<const Part::GeomLineSegment *>(Obj->getGeometry(lineids[0]));
|
|
|
|
if(!geo->Construction)
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),"toggleConstruction(%d) ",lineids[0]);
|
|
|
|
minor=true;
|
|
}
|
|
else
|
|
extra_elements=true;
|
|
}
|
|
|
|
if(lineids.size()==2) {
|
|
if (!minor) {
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),
|
|
"addConstraint(Sketcher.Constraint('InternalAlignment:EllipseMinorDiameter',%d,%d)) ",
|
|
lineids[1],arcsofellipseids[0]);
|
|
|
|
const Part::GeomLineSegment *geo = static_cast<const Part::GeomLineSegment *>(Obj->getGeometry(lineids[1]));
|
|
|
|
if (!geo->Construction)
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(),"toggleConstruction(%d) ",lineids[1]);
|
|
}
|
|
else
|
|
extra_elements=true;
|
|
}
|
|
|
|
// finish the transaction and update
|
|
commitCommand();
|
|
|
|
tryAutoRecompute(Obj);
|
|
|
|
if(extra_elements){
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Extra elements"),
|
|
QObject::tr("More elements than possible for the given ellipse were provided. These were ignored."));
|
|
}
|
|
|
|
// clear the selection (convenience)
|
|
getSelection().clearSelection();
|
|
}
|
|
else {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Extra elements"),
|
|
QObject::tr("More elements than possible for the given arc of ellipse were provided. These were ignored."));
|
|
}
|
|
}
|
|
else {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Currently internal geometry is only supported for ellipse or arc of ellipse. The last selected element must be an ellipse or an arc of ellipse."));
|
|
}
|
|
}
|
|
|
|
bool CmdSketcherConstrainInternalAlignment::isActive(void)
|
|
{
|
|
return isCreateConstraintActive( getActiveGuiDocument() );
|
|
}
|
|
|
|
// ======================================================================================
|
|
/*** Creation Mode / Toggle to or from Reference ***/
|
|
DEF_STD_CMD_A(CmdSketcherToggleDrivingConstraint)
|
|
|
|
CmdSketcherToggleDrivingConstraint::CmdSketcherToggleDrivingConstraint()
|
|
:Command("Sketcher_ToggleDrivingConstraint")
|
|
{
|
|
sAppModule = "Sketcher";
|
|
sGroup = QT_TR_NOOP("Sketcher");
|
|
sMenuText = QT_TR_NOOP("Toggle reference/driving constraint");
|
|
sToolTipText = QT_TR_NOOP("Toggles the toolbar or selected constraints to/from reference mode");
|
|
sWhatsThis = "Sketcher_ToggleDrivingConstraint";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "Sketcher_ToggleConstraint";
|
|
sAccel = "";
|
|
eType = ForEdit;
|
|
|
|
// list of toggle driving constraint commands
|
|
Gui::CommandManager &rcCmdMgr = Gui::Application::Instance->commandManager();
|
|
rcCmdMgr.addCommandMode("ToggleDrivingConstraint", "Sketcher_ConstrainLock");
|
|
rcCmdMgr.addCommandMode("ToggleDrivingConstraint", "Sketcher_ConstrainDistance");
|
|
rcCmdMgr.addCommandMode("ToggleDrivingConstraint", "Sketcher_ConstrainDistanceX");
|
|
rcCmdMgr.addCommandMode("ToggleDrivingConstraint", "Sketcher_ConstrainDistanceY");
|
|
rcCmdMgr.addCommandMode("ToggleDrivingConstraint", "Sketcher_ConstrainRadius");
|
|
rcCmdMgr.addCommandMode("ToggleDrivingConstraint", "Sketcher_ConstrainDiameter");
|
|
rcCmdMgr.addCommandMode("ToggleDrivingConstraint", "Sketcher_ConstrainAngle");
|
|
rcCmdMgr.addCommandMode("ToggleDrivingConstraint", "Sketcher_CompConstrainRadDia");
|
|
//rcCmdMgr.addCommandMode("ToggleDrivingConstraint", "Sketcher_ConstrainSnellsLaw");
|
|
}
|
|
|
|
void CmdSketcherToggleDrivingConstraint::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
bool modeChange=true;
|
|
|
|
std::vector<Gui::SelectionObject> selection;
|
|
|
|
if (Gui::Selection().countObjectsOfType(Sketcher::SketchObject::getClassTypeId()) > 0){
|
|
// Now we check whether we have a constraint selected or not.
|
|
|
|
// get the selection
|
|
selection = getSelection().getSelectionEx();
|
|
|
|
// only one sketch with its subelements are allowed to be selected
|
|
if (selection.size() != 1 || !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select constraint(s) from the sketch."));
|
|
return;
|
|
}
|
|
|
|
// get the needed lists and objects
|
|
const std::vector<std::string> &SubNames = selection[0].getSubNames();
|
|
if (SubNames.empty()) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select constraint(s) from the sketch."));
|
|
return;
|
|
}
|
|
|
|
for (std::vector<std::string>::const_iterator it=SubNames.begin();it!=SubNames.end();++it){
|
|
// see if we have constraints, if we do it is not a mode change, but a toggle.
|
|
if (it->size() > 10 && it->substr(0,10) == "Constraint")
|
|
modeChange=false;
|
|
}
|
|
}
|
|
|
|
if (modeChange) {
|
|
// Here starts the code for mode change
|
|
Gui::CommandManager &rcCmdMgr = Gui::Application::Instance->commandManager();
|
|
|
|
if (constraintCreationMode == Driving) {
|
|
constraintCreationMode = Reference;
|
|
}
|
|
else {
|
|
constraintCreationMode = Driving;
|
|
}
|
|
|
|
rcCmdMgr.updateCommands("ToggleDrivingConstraint", static_cast<int>(constraintCreationMode));
|
|
}
|
|
else // toggle the selected constraint(s)
|
|
{
|
|
Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
|
|
|
|
// get the needed lists and objects
|
|
const std::vector<std::string> &SubNames = selection[0].getSubNames();
|
|
if (SubNames.empty()) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select constraint(s) from the sketch."));
|
|
return;
|
|
}
|
|
|
|
// undo command open
|
|
openCommand("Toggle driving from/to non-driving");
|
|
|
|
int successful=SubNames.size();
|
|
// go through the selected subelements
|
|
for (std::vector<std::string>::const_iterator it=SubNames.begin();it!=SubNames.end();++it){
|
|
// only handle constraints
|
|
if (it->size() > 10 && it->substr(0,10) == "Constraint") {
|
|
int ConstrId = Sketcher::PropertyConstraintList::getIndexFromConstraintName(*it);
|
|
try {
|
|
// issue the actual commands to toggle
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "toggleDriving(%d) ", ConstrId);
|
|
}
|
|
catch(const Base::Exception&) {
|
|
successful--;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (successful > 0)
|
|
commitCommand();
|
|
else
|
|
abortCommand();
|
|
|
|
tryAutoRecompute(Obj);
|
|
|
|
// clear the selection (convenience)
|
|
getSelection().clearSelection();
|
|
}
|
|
}
|
|
|
|
bool CmdSketcherToggleDrivingConstraint::isActive(void)
|
|
{
|
|
return isCreateGeoActive( getActiveGuiDocument() );
|
|
}
|
|
|
|
DEF_STD_CMD_A(CmdSketcherToggleActiveConstraint)
|
|
|
|
CmdSketcherToggleActiveConstraint::CmdSketcherToggleActiveConstraint()
|
|
:Command("Sketcher_ToggleActiveConstraint")
|
|
{
|
|
sAppModule = "Sketcher";
|
|
sGroup = QT_TR_NOOP("Sketcher");
|
|
sMenuText = QT_TR_NOOP("Toggle activate/deactivate constraint");
|
|
sToolTipText = QT_TR_NOOP("Toggles activate/deactivate state for selected constraints");
|
|
sWhatsThis = "Sketcher_ToggleActiveConstraint";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "Sketcher_ToggleActiveConstraint";
|
|
sAccel = "";
|
|
eType = ForEdit;
|
|
}
|
|
|
|
void CmdSketcherToggleActiveConstraint::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
|
|
std::vector<Gui::SelectionObject> selection;
|
|
|
|
if (Gui::Selection().countObjectsOfType(Sketcher::SketchObject::getClassTypeId()) > 0){
|
|
// Now we check whether we have a constraint selected or not.
|
|
|
|
// get the selection
|
|
selection = getSelection().getSelectionEx();
|
|
|
|
// only one sketch with its subelements are allowed to be selected
|
|
if (selection.size() != 1 || !selection[0].isObjectTypeOf(Sketcher::SketchObject::getClassTypeId())) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select constraint(s) from the sketch."));
|
|
return;
|
|
}
|
|
|
|
Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
|
|
|
|
// get the needed lists and objects
|
|
const std::vector<std::string> &SubNames = selection[0].getSubNames();
|
|
if (SubNames.empty()) {
|
|
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
|
|
QObject::tr("Select constraint(s) from the sketch."));
|
|
return;
|
|
}
|
|
|
|
// undo command open
|
|
openCommand("Active/Deactivate constraints");
|
|
|
|
int successful=SubNames.size();
|
|
|
|
for (std::vector<std::string>::const_iterator it=SubNames.begin();it!=SubNames.end();++it){
|
|
|
|
if (it->size() > 10 && it->substr(0,10) == "Constraint") {
|
|
int ConstrId = Sketcher::PropertyConstraintList::getIndexFromConstraintName(*it);
|
|
try {
|
|
// issue the actual commands to toggle
|
|
Gui::cmdAppObjectArgs(selection[0].getObject(), "toggleActive(%d) ",ConstrId);
|
|
}
|
|
catch(const Base::Exception&) {
|
|
successful--;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (successful > 0)
|
|
commitCommand();
|
|
else
|
|
abortCommand();
|
|
|
|
tryAutoRecompute(Obj);
|
|
|
|
// clear the selection (convenience)
|
|
getSelection().clearSelection();
|
|
}
|
|
}
|
|
|
|
bool CmdSketcherToggleActiveConstraint::isActive(void)
|
|
{
|
|
return isCreateConstraintActive( getActiveGuiDocument() );
|
|
}
|
|
|
|
|
|
void CreateSketcherCommandsConstraints(void)
|
|
{
|
|
Gui::CommandManager &rcCmdMgr = Gui::Application::Instance->commandManager();
|
|
|
|
rcCmdMgr.addCommand(new CmdSketcherConstrainHorizontal());
|
|
rcCmdMgr.addCommand(new CmdSketcherConstrainVertical());
|
|
rcCmdMgr.addCommand(new CmdSketcherConstrainLock());
|
|
rcCmdMgr.addCommand(new CmdSketcherConstrainBlock());
|
|
rcCmdMgr.addCommand(new CmdSketcherConstrainCoincident());
|
|
rcCmdMgr.addCommand(new CmdSketcherConstrainParallel());
|
|
rcCmdMgr.addCommand(new CmdSketcherConstrainPerpendicular());
|
|
rcCmdMgr.addCommand(new CmdSketcherConstrainTangent());
|
|
rcCmdMgr.addCommand(new CmdSketcherConstrainDistance());
|
|
rcCmdMgr.addCommand(new CmdSketcherConstrainDistanceX());
|
|
rcCmdMgr.addCommand(new CmdSketcherConstrainDistanceY());
|
|
rcCmdMgr.addCommand(new CmdSketcherConstrainRadius());
|
|
rcCmdMgr.addCommand(new CmdSketcherConstrainDiameter());
|
|
rcCmdMgr.addCommand(new CmdSketcherCompConstrainRadDia());
|
|
rcCmdMgr.addCommand(new CmdSketcherConstrainAngle());
|
|
rcCmdMgr.addCommand(new CmdSketcherConstrainEqual());
|
|
rcCmdMgr.addCommand(new CmdSketcherConstrainPointOnObject());
|
|
rcCmdMgr.addCommand(new CmdSketcherConstrainSymmetric());
|
|
rcCmdMgr.addCommand(new CmdSketcherConstrainSnellsLaw());
|
|
rcCmdMgr.addCommand(new CmdSketcherConstrainInternalAlignment());
|
|
rcCmdMgr.addCommand(new CmdSketcherToggleDrivingConstraint());
|
|
rcCmdMgr.addCommand(new CmdSketcherToggleActiveConstraint());
|
|
}
|