FreeCAD/src/Mod/Sketcher/Gui/DrawSketchHandler.cpp
Abdullah Tahiri f5df65c4c4 Sketcher: Ellipse enhancement: All internal geometry is shown on creation
Elements that have internal geometry are created with the internal geometry on creation.

It has been under discussion for a long time. Ulrich was in favour (asked for it several times).
DeepSOIC implemented the focus because he hated the poor dragging of the ellipse when empty.
2014-12-20 12:33:33 +01:00

446 lines
17 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 <Standard_math.hxx>
# include <Inventor/nodes/SoTranslation.h>
# include <Inventor/nodes/SoText2.h>
# include <Inventor/nodes/SoFont.h>
# include <QPainter>
#endif
/// Here the FreeCAD includes sorted by Base,App,Gui......
#include <Base/Console.h>
#include <Base/Exception.h>
#include <Base/Interpreter.h>
#include <Gui/Application.h>
#include <Gui/BitmapFactory.h>
#include <Gui/Command.h>
#include <Gui/Document.h>
#include <Gui/Macro.h>
#include <Gui/MainWindow.h>
#include <Gui/View3DInventorViewer.h>
#include <Gui/View3DInventor.h>
#include <Mod/Part/App/Geometry.h>
#include <Mod/Sketcher/App/SketchObject.h>
#include "DrawSketchHandler.h"
#include "ViewProviderSketch.h"
using namespace SketcherGui;
using namespace Sketcher;
//**************************************************************************
// Construction/Destruction
DrawSketchHandler::DrawSketchHandler()
: sketchgui(0)
{
}
DrawSketchHandler::~DrawSketchHandler()
{
}
void DrawSketchHandler::quit(void)
{
assert(sketchgui);
sketchgui->drawEdit(std::vector<Base::Vector2D>());
resetPositionText();
unsetCursor();
sketchgui->purgeHandler();
}
//**************************************************************************
// Helpers
int DrawSketchHandler::getHighestVertexIndex(void)
{
return sketchgui->getSketchObject()->getHighestVertexIndex();
}
int DrawSketchHandler::getHighestCurveIndex(void)
{
return sketchgui->getSketchObject()->getHighestCurveIndex();
}
void DrawSketchHandler::setCursor(const QPixmap &p,int x,int y)
{
Gui::MDIView* view = Gui::getMainWindow()->activeWindow();
if (view && view->isDerivedFrom(Gui::View3DInventor::getClassTypeId())) {
Gui::View3DInventorViewer* viewer = static_cast<Gui::View3DInventor*>(view)->getViewer();
oldCursor = viewer->getWidget()->cursor();
QCursor cursor(p, x, y);
actCursor = cursor;
viewer->getWidget()->setCursor(cursor);
}
}
void DrawSketchHandler::applyCursor(void)
{
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(void)
{
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);
}
}
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
// Get Preselection
int preSelPnt = sketchgui->getPreselectPoint();
int preSelCrv = sketchgui->getPreselectCurve();
int preSelCrs = sketchgui->getPreselectCross();
int GeoId = Constraint::GeoUndef;
Sketcher::PointPos PosId = Sketcher::none;
if (preSelPnt != -1)
sketchgui->getSketchObject()->getGeoVertexIndex(preSelPnt, GeoId, PosId);
else if (preSelCrv != -1)
GeoId = preSelCrv;
else if (preSelCrs == 0) { // root point
GeoId = -1;
PosId = Sketcher::start;
}
else if (preSelCrs == 1) // x axis
GeoId = -1;
else if (preSelCrs == 2) // y axis
GeoId = -2;
if (GeoId != Constraint::GeoUndef) {
// Currently only considers objects in current Sketcher
AutoConstraint constr;
constr.Type = Sketcher::None;
constr.GeoId = GeoId;
constr.PosId = PosId;
if (type == AutoConstraint::VERTEX && PosId != Sketcher::none)
constr.Type = Sketcher::Coincident;
else if (type == AutoConstraint::CURVE && PosId != Sketcher::none)
constr.Type = Sketcher::PointOnObject;
else if (type == AutoConstraint::VERTEX && PosId == Sketcher::none)
constr.Type = Sketcher::PointOnObject;
else if (type == AutoConstraint::CURVE && PosId == Sketcher::none)
constr.Type = Sketcher::Tangent;
if (constr.Type != Sketcher::None)
suggestedConstraints.push_back(constr);
}
if (Dir.Length() < 1e-8)
// 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 = Constraint::GeoUndef;
constr.PosId = Sketcher::none;
double angle = std::abs(atan2(Dir.fY, Dir.fX));
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);
// Find if there are tangent constraints (currently arcs and circles)
int tangId = Constraint::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.fX, Pos.fY, 0.f); // Current cursor point
Base::Vector3d tmpDir(Dir.fX, Dir.fY, 0.f); // Direction of line
Base::Vector3d tmpStart(Pos.fX-Dir.fX, Pos.fY-Dir.fY, 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 = dynamic_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.ProjToLine(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 = dynamic_cast<const Part::GeomEllipse *>((*it));
Base::Vector3d center = ellipse->getCenter();
Base::Vector3d tmpPos(Pos.fX, Pos.fY, 0.f);
double radius = ellipse->getMajorRadius();
Base::Vector3d projPnt(0.f, 0.f, 0.f);
projPnt = projPnt.ProjToLine(center - tmpPos, Base::Vector3d(Dir.fX, Dir.fY));
double projDist = projPnt.Length();
if ( (projDist < radius + tangDeviation ) && (projDist > radius - tangDeviation)) {
//Find if nearest
if (projDist < tangDeviation) {
tangId = i;
tangDeviation = projDist;
}
}
} else if ((*it)->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
const Part::GeomArcOfCircle *arc = dynamic_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.ProjToLine(center - tmpPos, tmpDir);
double projDist = std::abs(projPnt.Length() - radius);
if (projDist < tangDeviation) {
double startAngle, endAngle;
arc->getRange(startAngle, endAngle);
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;
}
}
}
}
if (tangId != Constraint::GeoUndef) {
if (tangId > getHighestCurveIndex()) // external Geometry
tangId = getHighestCurveIndex() - tangId;
// Suggest vertical constraint
constr.Type = Tangent;
constr.GeoId = tangId;
constr.PosId = Sketcher::none;
suggestedConstraints.push_back(constr);
}
return suggestedConstraints.size();
}
void DrawSketchHandler::createAutoConstraints(const std::vector<AutoConstraint> &autoConstrs,
int geoId1, Sketcher::PointPos posId1)
{
if (!sketchgui->Autoconstraints.getValue())
return; // If Autoconstraints property is not set quit
if (autoConstrs.size() > 0) {
// Open the Command
Gui::Command::openCommand("Add auto constraints");
// Iterate through constraints
std::vector<AutoConstraint>::const_iterator it = autoConstrs.begin();
for (; it != autoConstrs.end(); ++it) {
switch (it->Type)
{
case Sketcher::Coincident: {
if (posId1 == Sketcher::none)
continue;
// If the auto constraint has a point create a coincident otherwise it is an edge on a point
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Coincident',%i,%i,%i,%i)) "
,sketchgui->getObject()->getNameInDocument()
,geoId1, posId1, it->GeoId, it->PosId
);
} break;
case Sketcher::PointOnObject: {
int geoId2 = it->GeoId;
Sketcher::PointPos posId2 = it->PosId;
if (posId1 == Sketcher::none) {
// Auto constraining an edge so swap parameters
std::swap(geoId1,geoId2);
std::swap(posId1,posId2);
}
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('PointOnObject',%i,%i,%i)) "
,sketchgui->getObject()->getNameInDocument()
,geoId1, posId1, geoId2
);
} break;
case Sketcher::Horizontal: {
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Horizontal',%i)) "
,sketchgui->getObject()->getNameInDocument()
,geoId1
);
} break;
case Sketcher::Vertical: {
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Vertical',%i)) "
,sketchgui->getObject()->getNameInDocument()
,geoId1
);
} break;
case Sketcher::Tangent: {
Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.addConstraint(Sketcher.Constraint('Tangent',%i, %i)) "
,sketchgui->getObject()->getNameInDocument()
,geoId1, it->GeoId
);
} break;
default:
break;
}
Gui::Command::commitCommand();
Gui::Command::updateActive();
}
}
}
void DrawSketchHandler::renderSuggestConstraintsCursor(std::vector<AutoConstraint> &suggestedConstraints)
{
// Auto Constrait icon size in px
int iconSize = 16;
// Create a pixmap that will contain icon and each autoconstraint icon
QPixmap baseIcon = actCursor.pixmap();
QPixmap newIcon(baseIcon.width() + suggestedConstraints.size() * iconSize,
baseIcon.height());
newIcon.fill(Qt::transparent);
QPainter qp;
qp.begin(&newIcon);
qp.drawPixmap(0,0, baseIcon);
// Iterate through AutoConstraints type and add icons to the cursor pixmap
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::fromAscii("Constraint_Horizontal");
break;
case Vertical:
iconType = QString::fromAscii("Constraint_Vertical");
break;
case Coincident:
iconType = QString::fromAscii("Constraint_PointOnPoint");
break;
case PointOnObject:
iconType = QString::fromAscii("Constraint_PointOnObject");
break;
case Tangent:
iconType = QString::fromAscii("Constraint_Tangent");
break;
default:
break;
}
if (!iconType.isEmpty()) {
QPixmap icon = Gui::BitmapFactory().pixmap(iconType.toAscii()).scaledToWidth(iconSize);
qp.drawPixmap(QPoint(baseIcon.width() + i * iconSize, baseIcon.height() - iconSize), icon);
}
}
qp.end(); // Finish painting
// Create the new cursor with the icon.
QPoint p=actCursor.hotSpot();
QCursor newCursor(newIcon, p.x(), p.y());
applyCursor(newCursor);
}
void DrawSketchHandler::setPositionText(const Base::Vector2D &Pos, const SbString &text)
{
sketchgui->setPositionText(Pos, text);
}
void DrawSketchHandler::setPositionText(const Base::Vector2D &Pos)
{
sketchgui->setPositionText(Pos);
}
void DrawSketchHandler::resetPositionText(void)
{
sketchgui->resetPositionText();
}