Files
create/src/Mod/Sketcher/Gui/DrawSketchHandler.cpp
2023-08-05 16:50:31 +02:00

1089 lines
41 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 <cmath>
#include <QGuiApplication>
#include <QPainter>
#endif// #ifndef _PreComp_
#include <Base/Console.h>
#include <Base/Exception.h>
#include <Gui/Application.h>
#include <Gui/BitmapFactory.h>
#include <Gui/CommandT.h>
#include <Gui/MainWindow.h>
#include <Gui/View3DInventor.h>
#include <Gui/View3DInventorViewer.h>
#include <Mod/Sketcher/App/SketchObject.h>
#include "CommandConstraints.h"
#include "DrawSketchHandler.h"
#include "Utils.h"
#include "ViewProviderSketch.h"
using namespace SketcherGui;
using namespace Sketcher;
/************************************ Attorney *******************************************/
inline void
ViewProviderSketchDrawSketchHandlerAttorney::setConstraintSelectability(ViewProviderSketch& vp,
bool enabled /*= true*/)
{
vp.setConstraintSelectability(enabled);
}
inline void ViewProviderSketchDrawSketchHandlerAttorney::setPositionText(ViewProviderSketch& vp,
const Base::Vector2d& Pos,
const SbString& txt)
{
vp.setPositionText(Pos, txt);
}
inline void ViewProviderSketchDrawSketchHandlerAttorney::setPositionText(ViewProviderSketch& vp,
const Base::Vector2d& Pos)
{
vp.setPositionText(Pos);
}
inline void ViewProviderSketchDrawSketchHandlerAttorney::resetPositionText(ViewProviderSketch& vp)
{
vp.resetPositionText();
}
inline void
ViewProviderSketchDrawSketchHandlerAttorney::drawEdit(ViewProviderSketch& vp,
const std::vector<Base::Vector2d>& EditCurve)
{
vp.drawEdit(EditCurve);
}
inline void ViewProviderSketchDrawSketchHandlerAttorney::drawEdit(
ViewProviderSketch& vp, const std::list<std::vector<Base::Vector2d>>& list)
{
vp.drawEdit(list);
}
inline void ViewProviderSketchDrawSketchHandlerAttorney::drawEditMarkers(
ViewProviderSketch& vp, const std::vector<Base::Vector2d>& EditMarkers,
unsigned int augmentationlevel)
{
vp.drawEditMarkers(EditMarkers, augmentationlevel);
}
inline void ViewProviderSketchDrawSketchHandlerAttorney::setAxisPickStyle(ViewProviderSketch& vp,
bool on)
{
vp.setAxisPickStyle(on);
}
inline void
ViewProviderSketchDrawSketchHandlerAttorney::moveCursorToSketchPoint(ViewProviderSketch& vp,
Base::Vector2d point)
{
vp.moveCursorToSketchPoint(point);
}
inline void ViewProviderSketchDrawSketchHandlerAttorney::preselectAtPoint(ViewProviderSketch& vp,
Base::Vector2d point)
{
vp.preselectAtPoint(point);
}
inline int
ViewProviderSketchDrawSketchHandlerAttorney::getPreselectPoint(const ViewProviderSketch& vp)
{
return vp.getPreselectPoint();
}
inline int
ViewProviderSketchDrawSketchHandlerAttorney::getPreselectCurve(const ViewProviderSketch& vp)
{
return vp.getPreselectCurve();
}
inline int
ViewProviderSketchDrawSketchHandlerAttorney::getPreselectCross(const ViewProviderSketch& vp)
{
return vp.getPreselectCross();
}
inline void
ViewProviderSketchDrawSketchHandlerAttorney::setAngleSnapping(ViewProviderSketch& vp, bool enable,
Base::Vector2d referencePoint)
{
vp.setAngleSnapping(enable, referencePoint);
}
/**************************** CurveConverter **********************************************/
CurveConverter::CurveConverter()
{
try {
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/View");
hGrp->Attach(this);
}
catch (const Base::ValueError& e) {// ensure that if parameter strings are not well-formed, the
// exception is not propagated
Base::Console().DeveloperError(
"CurveConverter", "Malformed parameter string: %s\n", e.what());
}
updateCurvedEdgeCountSegmentsParameter();
}
CurveConverter::~CurveConverter()
{
try {
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/View");
hGrp->Detach(this);
}
catch (const Base::ValueError&
e) {// ensure that if parameter strings are not well-formed, the program is not
// terminated when calling the noexcept destructor.
Base::Console().DeveloperError(
"CurveConverter", "Malformed parameter string: %s\n", e.what());
}
}
std::vector<Base::Vector2d> CurveConverter::toVector2D(const Part::Geometry* geometry)
{
std::vector<Base::Vector2d> vector2d;
const auto type = geometry->getTypeId();
auto emplaceasvector2d = [&vector2d](const Base::Vector3d& point) {
vector2d.emplace_back(point.x, point.y);
};
auto isconic = type.isDerivedFrom(Part::GeomConic::getClassTypeId());
auto isbounded = type.isDerivedFrom(Part::GeomBoundedCurve::getClassTypeId());
if (type == Part::GeomLineSegment::getClassTypeId()) {// add a line
auto geo = static_cast<const Part::GeomLineSegment*>(geometry);
emplaceasvector2d(geo->getStartPoint());
emplaceasvector2d(geo->getEndPoint());
}
else if (isconic || isbounded) {
auto geo = static_cast<const Part::GeomConic*>(geometry);
double segment =
(geo->getLastParameter() - geo->getFirstParameter()) / curvedEdgeCountSegments;
for (int i = 0; i < curvedEdgeCountSegments; i++)
emplaceasvector2d(geo->value(geo->getFirstParameter() + i * segment));
// either close the curve for untrimmed conic or set the last point for bounded curves
emplaceasvector2d(isconic ? geo->value(0) : geo->value(geo->getLastParameter()));
}
return vector2d;
}
std::list<std::vector<Base::Vector2d>>
CurveConverter::toVector2DList(const std::vector<Part::Geometry*>& geometries)
{
std::list<std::vector<Base::Vector2d>> list;
for (const auto& geo : geometries)
list.push_back(toVector2D(geo));
return list;
}
void CurveConverter::updateCurvedEdgeCountSegmentsParameter()
{
ParameterGrp::handle hGrp =
App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View");
int stdcountsegments = hGrp->GetInt("SegmentsPerGeometry", 50);
// value cannot be smaller than 6
if (stdcountsegments < 6)
stdcountsegments = 6;
curvedEdgeCountSegments = stdcountsegments;
};
/** Observer for parameter group. */
void CurveConverter::OnChange(Base::Subject<const char*>& rCaller, const char* sReason)
{
(void)rCaller;
if (strcmp(sReason, "SegmentsPerGeometry") == 0) {
updateCurvedEdgeCountSegmentsParameter();
}
}
/**************************** DrawSketchHandler *******************************************/
//**************************************************************************
// Construction/Destruction
DrawSketchHandler::DrawSketchHandler()
: sketchgui(nullptr)
{}
DrawSketchHandler::~DrawSketchHandler()
{}
QString DrawSketchHandler::getCrosshairCursorSVGName() const
{
return QString::fromLatin1("None");
}
void DrawSketchHandler::activate(ViewProviderSketch* vp)
{
sketchgui = vp;
// save the cursor at the time the DSH is activated
Gui::MDIView* view = Gui::getMainWindow()->activeWindow();
Gui::View3DInventorViewer* viewer = static_cast<Gui::View3DInventor*>(view)->getViewer();
oldCursor = viewer->getWidget()->cursor();
updateCursor();
this->preActivated();
this->activated();
}
void DrawSketchHandler::deactivate()
{
this->deactivated();
this->postDeactivated();
ViewProviderSketchDrawSketchHandlerAttorney::setConstraintSelectability(*sketchgui, true);
// clear temporary Curve and Markers from the scenograph
drawEdit(std::vector<Base::Vector2d>());
drawEditMarkers(std::vector<Base::Vector2d>());
resetPositionText();
unsetCursor();
setAngleSnapping(false);
}
void DrawSketchHandler::preActivated()
{
ViewProviderSketchDrawSketchHandlerAttorney::setConstraintSelectability(*sketchgui, false);
}
void DrawSketchHandler::quit()
{
assert(sketchgui);
Gui::Selection().rmvSelectionGate();
Gui::Selection().rmvPreselect();
sketchgui->purgeHandler();
}
//**************************************************************************
// Helpers
int DrawSketchHandler::getHighestVertexIndex()
{
return sketchgui->getSketchObject()->getHighestVertexIndex();
}
int DrawSketchHandler::getHighestCurveIndex()
{
return sketchgui->getSketchObject()->getHighestCurveIndex();
}
unsigned long DrawSketchHandler::getCrosshairColor()
{
unsigned long color = 0xFFFFFFFF;// white
ParameterGrp::handle hGrp =
App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View");
color = hGrp->GetUnsigned("CursorCrosshairColor", color);
// from rgba to rgb
color = (color >> 8) & 0xFFFFFF;
return color;
}
void DrawSketchHandler::setCrosshairCursor(const QString& svgName)
{
const unsigned long defaultCrosshairColor = 0xFFFFFF;
unsigned long color = getCrosshairColor();
auto colorMapping = std::map<unsigned long, unsigned long>();
colorMapping[defaultCrosshairColor] = color;
// hot spot of all SVG icons should be 8,8 for 32x32 size (16x16 for 64x64)
int hotX = 8;
int hotY = 8;
setSvgCursor(svgName, hotX, hotY, colorMapping);
}
void DrawSketchHandler::setCrosshairCursor(const char* svgName)
{
QString cursorName = QString::fromLatin1(svgName);
setCrosshairCursor(cursorName);
}
void DrawSketchHandler::setSvgCursor(const QString& cursorName, int x, int y,
const std::map<unsigned long, unsigned long>& colorMapping)
{
// The Sketcher_Pointer_*.svg icons have a default size of 64x64. When directly creating
// them with a size of 32x32 they look very bad.
// As a workaround the icons are created with 64x64 and afterwards the pixmap is scaled to
// 32x32. This workaround is only needed if pRatio is equal to 1.0
//
qreal pRatio = devicePixelRatio();
bool isRatioOne = (pRatio == 1.0);
qreal defaultCursorSize = isRatioOne ? 64 : 32;
qreal hotX = x;
qreal hotY = y;
#if !defined(Q_OS_WIN32) && !defined(Q_OS_MAC)
if (qGuiApp->platformName() == QLatin1String("xcb")) {
hotX *= pRatio;
hotY *= pRatio;
}
#endif
qreal cursorSize = defaultCursorSize * pRatio;
QPixmap pointer = Gui::BitmapFactory().pixmapFromSvg(
cursorName.toStdString().c_str(), QSizeF(cursorSize, cursorSize), colorMapping);
if (isRatioOne)
pointer = pointer.scaled(32, 32);
pointer.setDevicePixelRatio(pRatio);
setCursor(pointer, hotX, hotY, false);
}
void DrawSketchHandler::setCursor(const QPixmap& p, int x, int y, bool autoScale)
{
Gui::MDIView* view = Gui::getMainWindow()->activeWindow();
if (view && view->isDerivedFrom(Gui::View3DInventor::getClassTypeId())) {
Gui::View3DInventorViewer* viewer = static_cast<Gui::View3DInventor*>(view)->getViewer();
QCursor cursor;
QPixmap p1(p);
// TODO remove autoScale after all cursors are SVG-based
if (autoScale) {
qreal pRatio = viewer->devicePixelRatio();
int newWidth = p.width() * pRatio;
int newHeight = p.height() * pRatio;
p1 = p1.scaled(newWidth, newHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation);
p1.setDevicePixelRatio(pRatio);
qreal hotX = x;
qreal hotY = y;
#if !defined(Q_OS_WIN32) && !defined(Q_OS_MAC)
if (qGuiApp->platformName() == QLatin1String("xcb")) {
hotX *= pRatio;
hotY *= pRatio;
}
#endif
cursor = QCursor(p1, hotX, hotY);
}
else {
// already scaled
cursor = QCursor(p1, x, y);
}
actCursor = cursor;
actCursorPixmap = p1;
viewer->getWidget()->setCursor(cursor);
}
}
void DrawSketchHandler::addCursorTail(std::vector<QPixmap>& pixmaps)
{
// Create a pixmap that will contain icon and each autoconstraint icon
Gui::MDIView* view = Gui::getMainWindow()->activeWindow();
if (view && view->isDerivedFrom(Gui::View3DInventor::getClassTypeId())) {
QPixmap baseIcon = QPixmap(actCursorPixmap);
baseIcon.setDevicePixelRatio(actCursorPixmap.devicePixelRatio());
qreal pixelRatio = baseIcon.devicePixelRatio();
// cursor size in device independent pixels
qreal baseCursorWidth = baseIcon.width();
qreal baseCursorHeight = baseIcon.height();
int tailWidth = 0;
for (auto const& p : pixmaps) {
tailWidth += p.width();
}
int newIconWidth = baseCursorWidth + tailWidth;
int newIconHeight = baseCursorHeight;
QPixmap newIcon(newIconWidth, newIconHeight);
newIcon.fill(Qt::transparent);
QPainter qp;
qp.begin(&newIcon);
qp.drawPixmap(QPointF(0, 0),
baseIcon.scaled(baseCursorWidth * pixelRatio,
baseCursorHeight * pixelRatio,
Qt::KeepAspectRatio,
Qt::SmoothTransformation));
// Iterate through pixmaps and them to the cursor pixmap
std::vector<QPixmap>::iterator pit = pixmaps.begin();
int i = 0;
qreal currentIconX = baseCursorWidth;
qreal currentIconY;
for (; pit != pixmaps.end(); ++pit, i++) {
QPixmap icon = *pit;
currentIconY = baseCursorHeight - icon.height();
qp.drawPixmap(QPointF(currentIconX, currentIconY), icon);
currentIconX += icon.width();
}
qp.end();// Finish painting
// Create the new cursor with the icon.
QPoint p = actCursor.hotSpot();
newIcon.setDevicePixelRatio(pixelRatio);
QCursor newCursor(newIcon, p.x(), p.y());
applyCursor(newCursor);
}
}
void DrawSketchHandler::updateCursor()
{
auto cursorstring = getCrosshairCursorSVGName();
if (cursorstring != QString::fromLatin1("None"))
setCrosshairCursor(cursorstring);
}
void DrawSketchHandler::applyCursor()
{
applyCursor(actCursor);
}
void DrawSketchHandler::applyCursor(QCursor& newCursor)
{
Gui::MDIView* view = Gui::getMainWindow()->activeWindow();
if (view && view->isDerivedFrom(Gui::View3DInventor::getClassTypeId())) {
Gui::View3DInventorViewer* viewer = static_cast<Gui::View3DInventor*>(view)->getViewer();
viewer->getWidget()->setCursor(newCursor);
}
}
void DrawSketchHandler::unsetCursor()
{
Gui::MDIView* view = Gui::getMainWindow()->activeWindow();
if (view && view->isDerivedFrom(Gui::View3DInventor::getClassTypeId())) {
Gui::View3DInventorViewer* viewer = static_cast<Gui::View3DInventor*>(view)->getViewer();
viewer->getWidget()->setCursor(oldCursor);
}
}
qreal DrawSketchHandler::devicePixelRatio()
{
qreal pixelRatio = 1;
Gui::MDIView* view = Gui::getMainWindow()->activeWindow();
if (view && view->isDerivedFrom(Gui::View3DInventor::getClassTypeId())) {
Gui::View3DInventorViewer* viewer = static_cast<Gui::View3DInventor*>(view)->getViewer();
pixelRatio = viewer->devicePixelRatio();
}
return pixelRatio;
}
std::vector<QPixmap>
DrawSketchHandler::suggestedConstraintsPixmaps(std::vector<AutoConstraint>& suggestedConstraints)
{
std::vector<QPixmap> pixmaps;
// Iterate through AutoConstraints types and get their pixmaps
std::vector<AutoConstraint>::iterator it = suggestedConstraints.begin();
int i = 0;
for (; it != suggestedConstraints.end(); ++it, i++) {
QString iconType;
switch (it->Type) {
case Horizontal:
iconType = QString::fromLatin1("Constraint_Horizontal");
break;
case Vertical:
iconType = QString::fromLatin1("Constraint_Vertical");
break;
case Coincident:
iconType = QString::fromLatin1("Constraint_PointOnPoint");
break;
case PointOnObject:
iconType = QString::fromLatin1("Constraint_PointOnObject");
break;
case Tangent:
iconType = QString::fromLatin1("Constraint_Tangent");
break;
default:
break;
}
if (!iconType.isEmpty()) {
qreal pixelRatio = 1;
Gui::MDIView* view = Gui::getMainWindow()->activeWindow();
if (view && view->isDerivedFrom(Gui::View3DInventor::getClassTypeId())) {
Gui::View3DInventorViewer* viewer =
static_cast<Gui::View3DInventor*>(view)->getViewer();
pixelRatio = viewer->devicePixelRatio();
}
int iconWidth = 16 * pixelRatio;
QPixmap icon = Gui::BitmapFactory().pixmapFromSvg(iconType.toStdString().c_str(),
QSize(iconWidth, iconWidth));
pixmaps.push_back(icon);
}
}
return pixmaps;
}
int DrawSketchHandler::seekAutoConstraint(std::vector<AutoConstraint>& suggestedConstraints,
const Base::Vector2d& Pos, const Base::Vector2d& Dir,
AutoConstraint::TargetType type)
{
suggestedConstraints.clear();
if (!sketchgui->Autoconstraints.getValue())
return 0;// If Autoconstraints property is not set quit
Base::Vector3d hitShapeDir = Base::Vector3d(
0, 0, 0);// direction of hit shape (if it is a line, the direction of the line)
// Get Preselection
int preSelPnt = getPreselectPoint();
int preSelCrv = getPreselectCurve();
int preSelCrs = getPreselectCross();
int GeoId = GeoEnum::GeoUndef;
Sketcher::PointPos PosId = Sketcher::PointPos::none;
if (preSelPnt != -1)
sketchgui->getSketchObject()->getGeoVertexIndex(preSelPnt, GeoId, PosId);
else if (preSelCrv != -1) {
const Part::Geometry* geom = sketchgui->getSketchObject()->getGeometry(preSelCrv);
// ensure geom exists in case object was called before preselection is updated
if (geom) {
GeoId = preSelCrv;
if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
const Part::GeomLineSegment* line = static_cast<const Part::GeomLineSegment*>(geom);
hitShapeDir = line->getEndPoint() - line->getStartPoint();
}
}
}
else if (preSelCrs == 0) {// root point
GeoId = Sketcher::GeoEnum::RtPnt;
PosId = Sketcher::PointPos::start;
}
else if (preSelCrs == 1) {// x axis
GeoId = Sketcher::GeoEnum::HAxis;
hitShapeDir = Base::Vector3d(1, 0, 0);
}
else if (preSelCrs == 2) {// y axis
GeoId = Sketcher::GeoEnum::VAxis;
hitShapeDir = Base::Vector3d(0, 1, 0);
}
if (GeoId != GeoEnum::GeoUndef) {
// Currently only considers objects in current Sketcher
AutoConstraint constr;
constr.Type = Sketcher::None;
constr.GeoId = GeoId;
constr.PosId = PosId;
if ((type == AutoConstraint::VERTEX || type == AutoConstraint::VERTEX_NO_TANGENCY)
&& PosId != Sketcher::PointPos::none)
constr.Type = Sketcher::Coincident;
else if (type == AutoConstraint::CURVE && PosId != Sketcher::PointPos::none)
constr.Type = Sketcher::PointOnObject;
else if ((type == AutoConstraint::VERTEX || type == AutoConstraint::VERTEX_NO_TANGENCY)
&& PosId == Sketcher::PointPos::none)
constr.Type = Sketcher::PointOnObject;
else if (type == AutoConstraint::CURVE && PosId == Sketcher::PointPos::none)
constr.Type = Sketcher::Tangent;
if (constr.Type == Sketcher::Tangent && Dir.Length() > 1e-8
&& hitShapeDir.Length()
> 1e-8) {// We are hitting a line and have hitting vector information
Base::Vector3d dir3d = Base::Vector3d(Dir.x, Dir.y, 0);
double cosangle = dir3d.Normalize() * hitShapeDir.Normalize();
// the angle between the line and the hitting direction are over around 6 degrees (it is
// substantially parallel) or if it is an sketch axis (that can not move to accommodate
// to the shape), then only if it is around 6 degrees with the normal (around 84
// degrees)
if (fabs(cosangle) < 0.995f
|| ((GeoId == Sketcher::GeoEnum::HAxis || GeoId == Sketcher::GeoEnum::VAxis)
&& fabs(cosangle) < 0.1))
suggestedConstraints.push_back(constr);
return suggestedConstraints.size();
}
if (constr.Type != Sketcher::None)
suggestedConstraints.push_back(constr);
}
if (Dir.Length() < 1e-8 || type == AutoConstraint::CURVE)
// Direction not set so return;
return suggestedConstraints.size();
// Suggest vertical and horizontal constraints
// Number of Degree of deviation from horizontal or vertical lines
const double angleDev = 2;
const double angleDevRad = angleDev * M_PI / 180.;
AutoConstraint constr;
constr.Type = Sketcher::None;
constr.GeoId = GeoEnum::GeoUndef;
constr.PosId = Sketcher::PointPos::none;
double angle = std::abs(atan2(Dir.y, Dir.x));
if (angle < angleDevRad || (M_PI - angle) < angleDevRad)
// Suggest horizontal constraint
constr.Type = Sketcher::Horizontal;
else if (std::abs(angle - M_PI_2) < angleDevRad)
// Suggest vertical constraint
constr.Type = Sketcher::Vertical;
if (constr.Type != Sketcher::None)
suggestedConstraints.push_back(constr);
// Do not seek for tangent if we are actually building a primitive
if (type == AutoConstraint::VERTEX_NO_TANGENCY)
return suggestedConstraints.size();
// Find if there are tangent constraints (currently arcs and circles)
int tangId = GeoEnum::GeoUndef;
// Do not consider if distance is more than that.
// Decrease this value when a candidate is found.
double tangDeviation = 0.1 * sketchgui->getScaleFactor();
// Get geometry list
const std::vector<Part::Geometry*> geomlist =
sketchgui->getSketchObject()->getCompleteGeometry();
Base::Vector3d tmpPos(Pos.x, Pos.y, 0.f); // Current cursor point
Base::Vector3d tmpDir(Dir.x, Dir.y, 0.f); // Direction of line
Base::Vector3d tmpStart(Pos.x - Dir.x, Pos.y - Dir.y, 0.f);// Start point
// Iterate through geometry
int i = 0;
for (std::vector<Part::Geometry*>::const_iterator it = geomlist.begin(); it != geomlist.end();
++it, i++) {
if ((*it)->getTypeId() == Part::GeomCircle::getClassTypeId()) {
const Part::GeomCircle* circle = static_cast<const Part::GeomCircle*>((*it));
Base::Vector3d center = circle->getCenter();
double radius = circle->getRadius();
// ignore if no touch (use dot product)
if (tmpDir * (center - tmpPos) > 0 || tmpDir * (center - tmpStart) < 0)
continue;
Base::Vector3d projPnt(0.f, 0.f, 0.f);
projPnt = projPnt.ProjectToLine(center - tmpPos, tmpDir);
double projDist = std::abs(projPnt.Length() - radius);
// Find if nearest
if (projDist < tangDeviation) {
tangId = i;
tangDeviation = projDist;
}
}
else if ((*it)->getTypeId() == Part::GeomEllipse::getClassTypeId()) {
const Part::GeomEllipse* ellipse = static_cast<const Part::GeomEllipse*>((*it));
Base::Vector3d center = ellipse->getCenter();
double a = ellipse->getMajorRadius();
double b = ellipse->getMinorRadius();
Base::Vector3d majdir = ellipse->getMajorAxisDir();
double cf = sqrt(a * a - b * b);
Base::Vector3d focus1P = center + cf * majdir;
Base::Vector3d focus2P = center - cf * majdir;
Base::Vector3d norm = Base::Vector3d(Dir.y, -Dir.x).Normalize();
double distancetoline = norm * (tmpPos - focus1P);// distance focus1 to line
Base::Vector3d focus1PMirrored =
focus1P + 2 * distancetoline * norm;// mirror of focus1 with respect to the line
double error = fabs((focus1PMirrored - focus2P).Length() - 2 * a);
if (error < tangDeviation) {
tangId = i;
tangDeviation = error;
}
}
else if ((*it)->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
const Part::GeomArcOfCircle* arc = static_cast<const Part::GeomArcOfCircle*>((*it));
Base::Vector3d center = arc->getCenter();
double radius = arc->getRadius();
// ignore if no touch (use dot product)
if (tmpDir * (center - tmpPos) > 0 || tmpDir * (center - tmpStart) < 0)
continue;
Base::Vector3d projPnt(0.f, 0.f, 0.f);
projPnt = projPnt.ProjectToLine(center - tmpPos, tmpDir);
double projDist = std::abs(projPnt.Length() - radius);
if (projDist < tangDeviation) {
double startAngle, endAngle;
arc->getRange(startAngle, endAngle, /*emulateCCW=*/true);
double angle = atan2(projPnt.y, projPnt.x);
while (angle < startAngle)
angle += 2 * D_PI;// Bring it to range of arc
// if the point is on correct side of arc
if (angle <= endAngle) {// Now need to check only one side
tangId = i;
tangDeviation = projDist;
}
}
}
else if ((*it)->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()) {
const Part::GeomArcOfEllipse* aoe = static_cast<const Part::GeomArcOfEllipse*>((*it));
Base::Vector3d center = aoe->getCenter();
double a = aoe->getMajorRadius();
double b = aoe->getMinorRadius();
Base::Vector3d majdir = aoe->getMajorAxisDir();
double cf = sqrt(a * a - b * b);
Base::Vector3d focus1P = center + cf * majdir;
Base::Vector3d focus2P = center - cf * majdir;
Base::Vector3d norm = Base::Vector3d(Dir.y, -Dir.x).Normalize();
double distancetoline = norm * (tmpPos - focus1P);// distance focus1 to line
Base::Vector3d focus1PMirrored =
focus1P + 2 * distancetoline * norm;// mirror of focus1 with respect to the line
double error = fabs((focus1PMirrored - focus2P).Length() - 2 * a);
if (error < tangDeviation) {
tangId = i;
tangDeviation = error;
}
if (error < tangDeviation) {
double startAngle, endAngle;
aoe->getRange(startAngle, endAngle, /*emulateCCW=*/true);
double angle = Base::fmod(
atan2(
-aoe->getMajorRadius()
* ((tmpPos.x - center.x) * majdir.y - (tmpPos.y - center.y) * majdir.x),
aoe->getMinorRadius()
* ((tmpPos.x - center.x) * majdir.x + (tmpPos.y - center.y) * majdir.y))
- startAngle,
2.f * M_PI);
while (angle < startAngle)
angle += 2 * D_PI;// Bring it to range of arc
// if the point is on correct side of arc
if (angle <= endAngle) {// Now need to check only one side
tangId = i;
tangDeviation = error;
}
}
}
}
if (tangId != GeoEnum::GeoUndef) {
if (tangId > getHighestCurveIndex())// external Geometry
tangId = getHighestCurveIndex() - tangId;
// Suggest vertical constraint
constr.Type = Tangent;
constr.GeoId = tangId;
constr.PosId = Sketcher::PointPos::none;
suggestedConstraints.push_back(constr);
}
return suggestedConstraints.size();
}
void DrawSketchHandler::createAutoConstraints(const std::vector<AutoConstraint>& autoConstrs,
int geoId1, Sketcher::PointPos posId1,
bool createowncommand /*= true*/)
{
if (!sketchgui->Autoconstraints.getValue())
return;// If Autoconstraints property is not set quit
if (!autoConstrs.empty()) {
if (createowncommand) {
// Open the Command
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add auto constraints"));
}
// Iterate through constraints
std::vector<AutoConstraint>::const_iterator it = autoConstrs.begin();
for (; it != autoConstrs.end(); ++it) {
int geoId2 = it->GeoId;
switch (it->Type) {
case Sketcher::Coincident: {
if (posId1 == Sketcher::PointPos::none)
continue;
// If the auto constraint has a point create a coincident otherwise it is an
// edge on a point
Gui::cmdAppObjectArgs(
sketchgui->getObject(),
"addConstraint(Sketcher.Constraint('Coincident',%d,%d,%d,%d)) ",
geoId1,
static_cast<int>(posId1),
it->GeoId,
static_cast<int>(it->PosId));
} break;
case Sketcher::PointOnObject: {
Sketcher::PointPos posId2 = it->PosId;
if (posId1 == Sketcher::PointPos::none) {
// Auto constraining an edge so swap parameters
std::swap(geoId1, geoId2);
std::swap(posId1, posId2);
}
Gui::cmdAppObjectArgs(
sketchgui->getObject(),
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ",
geoId1,
static_cast<int>(posId1),
geoId2);
} break;
// In special case of Horizontal/Vertical constraint, geoId2 is normally unused
// and should be 'Constraint::GeoUndef' However it can be used as a way to
// require the function to apply these constraints on another geometry In this
// case the caller as to set geoId2, then it will be used as target instead of
// geoId2
case Sketcher::Horizontal: {
Gui::cmdAppObjectArgs(sketchgui->getObject(),
"addConstraint(Sketcher.Constraint('Horizontal',%d)) ",
geoId2 != GeoEnum::GeoUndef ? geoId2 : geoId1);
} break;
case Sketcher::Vertical: {
Gui::cmdAppObjectArgs(sketchgui->getObject(),
"addConstraint(Sketcher.Constraint('Vertical',%d)) ",
geoId2 != GeoEnum::GeoUndef ? geoId2 : geoId1);
} break;
case Sketcher::Tangent: {
Sketcher::SketchObject* Obj =
static_cast<Sketcher::SketchObject*>(sketchgui->getObject());
const Part::Geometry* geom1 = Obj->getGeometry(geoId1);
const Part::Geometry* geom2 = Obj->getGeometry(it->GeoId);
// ellipse tangency support using construction elements (lines)
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()) {
// in all these cases an intermediate element is needed
makeTangentToEllipseviaNewPoint(
Obj,
static_cast<const Part::GeomEllipse*>(geom1),
geom2,
geoId1,
geoId2);
return;
}
}
// arc of ellipse tangency support using external elements
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::GeomArcOfEllipse::getClassTypeId()
|| geom2->getTypeId() == Part::GeomCircle::getClassTypeId()
|| geom2->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
// in all these cases an intermediate element is needed
makeTangentToArcOfEllipseviaNewPoint(
Obj,
static_cast<const Part::GeomArcOfEllipse*>(geom1),
geom2,
geoId1,
geoId2);
return;
}
}
Gui::cmdAppObjectArgs(sketchgui->getObject(),
"addConstraint(Sketcher.Constraint('Tangent',%d, %d)) ",
geoId1,
it->GeoId);
} break;
default:
break;
}
if (createowncommand) {
Gui::Command::commitCommand();
}
// Gui::Command::updateActive(); // There is already an recompute in each command
// creation, this is redundant.
}
}
}
void DrawSketchHandler::renderSuggestConstraintsCursor(
std::vector<AutoConstraint>& suggestedConstraints)
{
std::vector<QPixmap> pixmaps = suggestedConstraintsPixmaps(suggestedConstraints);
addCursorTail(pixmaps);
}
void DrawSketchHandler::setPositionText(const Base::Vector2d& Pos, const SbString& text)
{
ViewProviderSketchDrawSketchHandlerAttorney::setPositionText(*sketchgui, Pos, text);
}
void DrawSketchHandler::setPositionText(const Base::Vector2d& Pos)
{
ViewProviderSketchDrawSketchHandlerAttorney::setPositionText(*sketchgui, Pos);
}
void DrawSketchHandler::resetPositionText()
{
ViewProviderSketchDrawSketchHandlerAttorney::resetPositionText(*sketchgui);
}
void DrawSketchHandler::drawEdit(const std::vector<Base::Vector2d>& EditCurve)
{
ViewProviderSketchDrawSketchHandlerAttorney::drawEdit(*sketchgui, EditCurve);
}
void DrawSketchHandler::drawEdit(const std::list<std::vector<Base::Vector2d>>& list)
{
ViewProviderSketchDrawSketchHandlerAttorney::drawEdit(*sketchgui, list);
}
void DrawSketchHandler::drawEdit(const std::vector<Part::Geometry*>& geometries)
{
static CurveConverter c;
auto list = c.toVector2DList(geometries);
drawEdit(list);
}
void DrawSketchHandler::drawPositionAtCursor(const Base::Vector2d& position)
{
setPositionText(position);
}
void DrawSketchHandler::drawDirectionAtCursor(const Base::Vector2d& position,
const Base::Vector2d& origin)
{
float length = (position - origin).Length();
float angle = (position - origin).GetAngle(Base::Vector2d(1.f, 0.f));
if (showCursorCoords()) {
SbString text;
std::string lengthString = lengthToDisplayFormat(length, 1);
std::string angleString = angleToDisplayFormat(angle * 180.0 / M_PI, 1);
text.sprintf(" (%s, %s)", lengthString.c_str(), angleString.c_str());
setPositionText(position, text);
}
}
void DrawSketchHandler::drawEditMarkers(const std::vector<Base::Vector2d>& EditMarkers,
unsigned int augmentationlevel)
{
ViewProviderSketchDrawSketchHandlerAttorney::drawEditMarkers(
*sketchgui, EditMarkers, augmentationlevel);
}
void DrawSketchHandler::setAxisPickStyle(bool on)
{
ViewProviderSketchDrawSketchHandlerAttorney::setAxisPickStyle(*sketchgui, on);
}
void DrawSketchHandler::moveCursorToSketchPoint(Base::Vector2d point)
{
ViewProviderSketchDrawSketchHandlerAttorney::moveCursorToSketchPoint(*sketchgui, point);
}
void DrawSketchHandler::preselectAtPoint(Base::Vector2d point)
{
ViewProviderSketchDrawSketchHandlerAttorney::preselectAtPoint(*sketchgui, point);
}
int DrawSketchHandler::getPreselectPoint() const
{
return ViewProviderSketchDrawSketchHandlerAttorney::getPreselectPoint(*sketchgui);
}
int DrawSketchHandler::getPreselectCurve() const
{
return ViewProviderSketchDrawSketchHandlerAttorney::getPreselectCurve(*sketchgui);
}
int DrawSketchHandler::getPreselectCross() const
{
return ViewProviderSketchDrawSketchHandlerAttorney::getPreselectCross(*sketchgui);
}
Sketcher::SketchObject* DrawSketchHandler::getSketchObject()
{
return sketchgui->getSketchObject();
}
void DrawSketchHandler::setAngleSnapping(bool enable, Base::Vector2d referencePoint)
{
ViewProviderSketchDrawSketchHandlerAttorney::setAngleSnapping(
*sketchgui, enable, referencePoint);
}