Sketcher-Utils: Refactor common code up

This commit is contained in:
Abdullah Tahiri
2022-05-17 12:00:52 +02:00
parent f99abec58e
commit bae8050fac
7 changed files with 221 additions and 156 deletions

View File

@@ -0,0 +1,47 @@
/***************************************************************************
* Copyright (c) 2022 Abdullah Tahiri <abdullah.tahiri.yo@gmail.com> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#ifndef SKETCHERGUI_AutoConstraint_H
#define SKETCHERGUI_AutoConstraint_H
namespace SketcherGui {
// A Simple data type to hold basic information for suggested constraints
struct AutoConstraint
{
enum TargetType
{
VERTEX,
CURVE,
VERTEX_NO_TANGENCY
};
Sketcher::ConstraintType Type;
int GeoId;
Sketcher::PointPos PosId;
};
} // namespace SketcherGui
#endif // SKETCHERGUI_AutoConstraint_H

View File

@@ -61,6 +61,7 @@ SET(SketcherGui_SRCS
AppSketcherGui.cpp
GeometryCreationMode.h
Command.cpp
AutoConstraint.h
Utils.h
Utils.cpp
CommandCreateGeo.cpp

View File

@@ -72,10 +72,6 @@ namespace SketcherGui
ConstraintCreationMode constraintCreationMode = Driving;
void ActivateHandler(Gui::Document *doc, DrawSketchHandler *handler);
bool isCreateGeoActive(Gui::Document *doc);
bool isCreateConstraintActive(Gui::Document *doc)
{
if (doc) {

View File

@@ -77,144 +77,6 @@ namespace SketcherGui {
GeometryCreationMode geometryCreationMode=Normal;
}
/* helper functions ======================================================*/
// Return counter-clockwise angle from horizontal out of p1 to p2 in radians.
double GetPointAngle (const Base::Vector2d &p1, const Base::Vector2d &p2)
{
double dX = p2.x - p1.x;
double dY = p2.y - p1.y;
return dY >= 0 ? atan2(dY, dX) : atan2(dY, dX) + 2*M_PI;
}
void ActivateHandler(Gui::Document *doc, DrawSketchHandler *handler)
{
std::unique_ptr<DrawSketchHandler> ptr(handler);
if (doc) {
if (doc->getInEdit() && doc->getInEdit()->isDerivedFrom(SketcherGui::ViewProviderSketch::getClassTypeId())) {
SketcherGui::ViewProviderSketch* vp = static_cast<SketcherGui::ViewProviderSketch*> (doc->getInEdit());
vp->purgeHandler();
vp->activateHandler(ptr.release());
}
}
}
bool isCreateGeoActive(Gui::Document *doc)
{
if (doc) {
// checks if a Sketch Viewprovider is in Edit and is in no special mode
if (doc->getInEdit() && doc->getInEdit()->isDerivedFrom
(SketcherGui::ViewProviderSketch::getClassTypeId())) {
/*if (dynamic_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit())->
getSketchMode() == ViewProviderSketch::STATUS_NONE)*/
return true;
}
}
return false;
}
SketcherGui::ViewProviderSketch* getSketchViewprovider(Gui::Document *doc)
{
if (doc) {
if (doc->getInEdit() && doc->getInEdit()->isDerivedFrom
(SketcherGui::ViewProviderSketch::getClassTypeId()) )
return dynamic_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit());
}
return nullptr;
}
void removeRedundantHorizontalVertical(Sketcher::SketchObject* psketch,
std::vector<AutoConstraint> &sug1,
std::vector<AutoConstraint> &sug2)
{
if(!sug1.empty() && !sug2.empty()) {
bool rmvhorvert = false;
// we look for:
// 1. Coincident to external on both endpoints
// 2. Coincident in one endpoint to origin and pointonobject/tangent to an axis on the other
auto detectredundant = [psketch](std::vector<AutoConstraint> &sug, bool &ext, bool &orig, bool &axis) {
ext = false;
orig = false;
axis = false;
for(std::vector<AutoConstraint>::const_iterator it = sug.begin(); it!=sug.end(); ++it) {
if( (*it).Type == Sketcher::Coincident && !ext) {
const std::map<int, Sketcher::PointPos> coincidents = psketch->getAllCoincidentPoints((*it).GeoId, (*it).PosId);
if(!coincidents.empty()) {
// the keys are ordered, so if the first is negative, it is coincident with external
ext = coincidents.begin()->first < 0;
std::map<int, Sketcher::PointPos>::const_iterator geoId1iterator;
geoId1iterator = coincidents.find(-1);
if( geoId1iterator != coincidents.end()) {
if( (*geoId1iterator).second == Sketcher::PointPos::start )
orig = true;
}
}
else { // it may be that there is no constraint at all, but there is external geometry
ext = (*it).GeoId < 0;
orig = ((*it).GeoId == -1 && (*it).PosId == Sketcher::PointPos::start);
}
}
else if( (*it).Type == Sketcher::PointOnObject && !axis) {
axis = (((*it).GeoId == -1 && (*it).PosId == Sketcher::PointPos::none) || ((*it).GeoId == -2 && (*it).PosId == Sketcher::PointPos::none));
}
}
};
bool firstext = false, secondext = false, firstorig = false, secondorig = false, firstaxis = false, secondaxis = false;
detectredundant(sug1, firstext, firstorig, firstaxis);
detectredundant(sug2, secondext, secondorig, secondaxis);
rmvhorvert = ((firstext && secondext) || // coincident with external on both endpoints
(firstorig && secondaxis) || // coincident origin and point on object on other
(secondorig && firstaxis));
if(rmvhorvert) {
for(std::vector<AutoConstraint>::reverse_iterator it = sug2.rbegin(); it!=sug2.rend(); ++it) {
if( (*it).Type == Sketcher::Horizontal || (*it).Type == Sketcher::Vertical) {
sug2.erase(std::next(it).base());
it = sug2.rbegin(); // erase invalidates the iterator
}
}
}
}
}
void ConstraintToAttachment(Sketcher::GeoElementId element, Sketcher::GeoElementId attachment, double distance, App::DocumentObject* obj) {
if (distance == 0.) {
if(attachment.isCurve()) {
Gui::cmdAppObjectArgs(obj, "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
element.GeoId, element.posIdAsInt(), attachment.GeoId);
}
else {
Gui::cmdAppObjectArgs(obj, "addConstraint(Sketcher.Constraint('Coincident',%d,%d,%d,%d)) ",
element.GeoId, element.posIdAsInt(), attachment.GeoId, attachment.posIdAsInt());
}
}
else {
if(attachment == Sketcher::GeoElementId::VAxis) {
Gui::cmdAppObjectArgs(obj, "addConstraint(Sketcher.Constraint('DistanceX',%d,%d,%f)) ",
element.GeoId, element.posIdAsInt(), distance);
}
else if(attachment == Sketcher::GeoElementId::HAxis) {
Gui::cmdAppObjectArgs(obj, "addConstraint(Sketcher.Constraint('DistanceY',%d,%d,%f)) ",
element.GeoId, element.posIdAsInt(), distance);
}
}
}
/* Sketch commands =======================================================*/
class DrawSketchHandlerLine: public DrawSketchHandler

View File

@@ -28,6 +28,8 @@
#include <Mod/Sketcher/App/Constraint.h>
#include <Gui/Selection.h>
#include "AutoConstraint.h"
class QPixmap;
namespace Sketcher {
@@ -89,20 +91,6 @@ private:
friend class DrawSketchHandler;
};
// A Simple data type to hold basic information for suggested constraints
struct AutoConstraint
{
enum TargetType
{
VERTEX,
CURVE,
VERTEX_NO_TANGENCY
};
Sketcher::ConstraintType Type;
int GeoId;
Sketcher::PointPos PosId;
};
/** Handler to create new sketch geometry
* This class has to be reimplemented to create geometry in the
* sketcher while its in editing.

View File

@@ -279,3 +279,141 @@ bool SketcherGui::checkConstraint(const std::vector< Sketcher::Constraint * > &v
return false;
}
/* helper functions ======================================================*/
// Return counter-clockwise angle from horizontal out of p1 to p2 in radians.
double SketcherGui::GetPointAngle (const Base::Vector2d &p1, const Base::Vector2d &p2)
{
double dX = p2.x - p1.x;
double dY = p2.y - p1.y;
return dY >= 0 ? atan2(dY, dX) : atan2(dY, dX) + 2*M_PI;
}
void SketcherGui::ActivateHandler(Gui::Document *doc, DrawSketchHandler *handler)
{
std::unique_ptr<DrawSketchHandler> ptr(handler);
if (doc) {
if (doc->getInEdit() && doc->getInEdit()->isDerivedFrom(SketcherGui::ViewProviderSketch::getClassTypeId())) {
SketcherGui::ViewProviderSketch* vp = static_cast<SketcherGui::ViewProviderSketch*> (doc->getInEdit());
vp->purgeHandler();
vp->activateHandler(ptr.release());
}
}
}
bool SketcherGui::isCreateGeoActive(Gui::Document *doc)
{
if (doc) {
// checks if a Sketch Viewprovider is in Edit and is in no special mode
if (doc->getInEdit() && doc->getInEdit()->isDerivedFrom
(SketcherGui::ViewProviderSketch::getClassTypeId())) {
/*if (dynamic_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit())->
getSketchMode() == ViewProviderSketch::STATUS_NONE)*/
return true;
}
}
return false;
}
SketcherGui::ViewProviderSketch* SketcherGui::getSketchViewprovider(Gui::Document *doc)
{
if (doc) {
if (doc->getInEdit() && doc->getInEdit()->isDerivedFrom
(SketcherGui::ViewProviderSketch::getClassTypeId()) )
return dynamic_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit());
}
return nullptr;
}
void SketcherGui::removeRedundantHorizontalVertical(Sketcher::SketchObject* psketch,
std::vector<AutoConstraint> &sug1,
std::vector<AutoConstraint> &sug2)
{
if(!sug1.empty() && !sug2.empty()) {
bool rmvhorvert = false;
// we look for:
// 1. Coincident to external on both endpoints
// 2. Coincident in one endpoint to origin and pointonobject/tangent to an axis on the other
auto detectredundant = [psketch](std::vector<AutoConstraint> &sug, bool &ext, bool &orig, bool &axis) {
ext = false;
orig = false;
axis = false;
for(std::vector<AutoConstraint>::const_iterator it = sug.begin(); it!=sug.end(); ++it) {
if( (*it).Type == Sketcher::Coincident && ext == false) {
const std::map<int, Sketcher::PointPos> coincidents = psketch->getAllCoincidentPoints((*it).GeoId, (*it).PosId);
if(!coincidents.empty()) {
// the keys are ordered, so if the first is negative, it is coincident with external
ext = coincidents.begin()->first < 0;
std::map<int, Sketcher::PointPos>::const_iterator geoId1iterator;
geoId1iterator = coincidents.find(-1);
if( geoId1iterator != coincidents.end()) {
if( (*geoId1iterator).second == Sketcher::PointPos::start )
orig = true;
}
}
else { // it may be that there is no constraint at all, but there is external geometry
ext = (*it).GeoId < 0;
orig = ((*it).GeoId == -1 && (*it).PosId == Sketcher::PointPos::start);
}
}
else if( (*it).Type == Sketcher::PointOnObject && axis == false) {
axis = (((*it).GeoId == -1 && (*it).PosId == Sketcher::PointPos::none) || ((*it).GeoId == -2 && (*it).PosId == Sketcher::PointPos::none));
}
}
};
bool firstext = false, secondext = false, firstorig = false, secondorig = false, firstaxis = false, secondaxis = false;
detectredundant(sug1, firstext, firstorig, firstaxis);
detectredundant(sug2, secondext, secondorig, secondaxis);
rmvhorvert = ((firstext && secondext) || // coincident with external on both endpoints
(firstorig && secondaxis) || // coincident origin and point on object on other
(secondorig && firstaxis));
if(rmvhorvert) {
for(std::vector<AutoConstraint>::reverse_iterator it = sug2.rbegin(); it!=sug2.rend(); ++it) {
if( (*it).Type == Sketcher::Horizontal || (*it).Type == Sketcher::Vertical) {
sug2.erase(std::next(it).base());
it = sug2.rbegin(); // erase invalidates the iterator
}
}
}
}
}
void SketcherGui::ConstraintToAttachment(Sketcher::GeoElementId element, Sketcher::GeoElementId attachment, double distance, App::DocumentObject* obj) {
if (distance == 0.) {
if(attachment.isCurve()) {
Gui::cmdAppObjectArgs(obj, "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
element.GeoId, element.posIdAsInt(), attachment.GeoId);
}
else {
Gui::cmdAppObjectArgs(obj, "addConstraint(Sketcher.Constraint('Coincident',%d,%d,%d,%d)) ",
element.GeoId, element.posIdAsInt(), attachment.GeoId, attachment.posIdAsInt());
}
}
else {
if(attachment == Sketcher::GeoElementId::VAxis) {
Gui::cmdAppObjectArgs(obj, "addConstraint(Sketcher.Constraint('DistanceX',%d,%d,%f)) ",
element.GeoId, element.posIdAsInt(), distance);
}
else if(attachment == Sketcher::GeoElementId::HAxis) {
Gui::cmdAppObjectArgs(obj, "addConstraint(Sketcher.Constraint('DistanceY',%d,%d,%f)) ",
element.GeoId, element.posIdAsInt(), distance);
}
}
}

View File

@@ -24,12 +24,26 @@
#ifndef SKETCHERGUI_Recompute_H
#define SKETCHERGUI_Recompute_H
#include <Base/Tools.h>
#include <Mod/Sketcher/App/GeoEnum.h>
#include "AutoConstraint.h"
namespace App {
class DocumentObject;
}
namespace Gui {
class DocumentObject;
}
namespace Sketcher {
enum class PointPos : int;
class SketchObject;
}
namespace SketcherGui {
class DrawSketchHandler;
class ViewProviderSketch;
/// This function tries to auto-recompute the active document if the option
/// is set in the user parameter. If the option is not set nothing will be done
@@ -90,6 +104,25 @@ inline bool isEdge(int GeoId, Sketcher::PointPos PosId)
return (GeoId != Sketcher::GeoEnum::GeoUndef && PosId == Sketcher::PointPos::none);
}
/* helper functions ======================================================*/
// Return counter-clockwise angle from horizontal out of p1 to p2 in radians.
double GetPointAngle (const Base::Vector2d &p1, const Base::Vector2d &p2);
void ActivateHandler(Gui::Document *doc, DrawSketchHandler *handler);
bool isCreateGeoActive(Gui::Document *doc);
SketcherGui::ViewProviderSketch* getSketchViewprovider(Gui::Document *doc);
void removeRedundantHorizontalVertical(Sketcher::SketchObject* psketch,
std::vector<AutoConstraint> &sug1,
std::vector<AutoConstraint> &sug2);
void ConstraintToAttachment(Sketcher::GeoElementId element, Sketcher::GeoElementId attachment, double distance, App::DocumentObject* obj);
}
#endif // SKETCHERGUI_Recompute_H