Files
create/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp
Abdullah Tahiri 71c9dc3e0e Sketcher: EditModeCoinManager/DrawSkechHandler refactoring
======================================================

Creation of EditModeCoinManager class and helpers.

In a nutshell:
- EditModeCoinManager gets most of the content of struct EditData
- Drawing is partly outsourced to EditModeCoinManager
- EditModeCoinManager gets a nested Observer class to deal with parameters
- A struct DrawingParameters is created to store all parameters used for drawing
- EditModeCoinManager assume responsibility for determining the drawing size of the Axes
- Preselection detection responsibility is moved to EditModeCoinManager.
- Generation of constraint nodes and constraint drawing is moved to EditModeCoinManager.
- Constraint generation parameters are refactored into ConstraintParameters.
- Text rendering functions are moved to EditModeCoinManager.
- Move HDPI resolution responsibility from VPSketch to EditModeCoinManager
- Move responsibility to create the scenograph for edit mode to EditModeCoinManager
- Move full updateColor responsibility to EditModeCoinManager
- Allows for mapping N logical layers (LayerId of GeometryFacade) to M coin Layers (M<N). This
is convenient as, unless the representation must be different, there is no point in creating coin
layers (overhead).

Refactoring of geometry drawing:
- Determination of the curve values to draw are outsourced to OCC (SRP and remove code duplications).
- Refactor specific drawing of each geometry type into a single template method, based on classes of geometry.
- Drawing of geometry and constraints made agnostic of scale factors of BSpline weights so that a uniform treatment can be provided.

Refactoring of Overlay Layer:
- A new class EditModeInformationOverlayConverter is a full rewrite of the previous overlay routines.

ViewProviderSketch:
- Major cleanup due to migration of functionalities to EditModeCoinManager
- Reduce public api of ViewProviderSketch due to refactor of DrawSketchHandler
- Major addition of documentation
- ShortcutListener implementation using new ViewProvider Attorney
- Gets a parameter handling nested class to handle all parameters (observer)
- Move rubberband to smart pointer
- Refactor selection and preselection into nested classes
- Removal of SEL_PARAMS macro. This macro was making the code unreadable as it "captured" a local stringstream that appeared unused. Substituted by local private member functions.
- Remove EditData
- Improve documentation
- Refactor Preselection struct to remove magical numbers
- Refactor Selection mechanism to remove hacks

ViewProviderSketchDrawSketchHandlerAttorney:
- new Attorney to limit access to ViewProviderSketch and reduce its public interface
- In order to enforce a certain degree of encapsulation and promote a not too tight coupling, while still allowing well
defined collaboration, DrawSketchHandler accesses ViewProviderSketch via this Attorney class.
-DrawSketchHandler has the responsibility of drawing edit temporal curves and markers necessary to enable visual feedback
to the user, as well as the UI interaction during such edits. This is its exclusive responsibility under the Single
Responsibility Principle.
- A plethora of speciliased handlers derive from DrawSketchHandler for each specialised editing (see for example all the
handlers for creation of new geometry). These derived classes do * not * have direct access to the
ViewProviderSketchDrawSketchHandlerAttorney. This is intentional to keep coupling under control. However, generic
functionality requiring access to the Attorney can be implemented in DrawSketchHandler and used from its derived classes
by virtue of the inheritance. This promotes a concentrating the coupling in a single point (and code reuse).

EditModeCoinManager:
- Refactor of updateConstraintColor
- Multifield - new struct to identify a single element in a multifield field per layer
- Move geometry management to delegate class EditModeCoinGeometryManager
- Remove refactored code that was never used in the original ViewProviderSketch.

CommandSketcherBSpline:
- EditModeCoinManager automatically tracks parameter change and triggers the necessary redraw, rendering an explicit redraw obsolete and unnecessary.

Rebase on top of master:
- Commits added to master to ViewProviderSketch applied to EditModeCoinManager.
- Memory leaks - wmayer
- Constraint Diameter Symbol - OpenBrain
- Minor bugfix to display angle constraints - syres

Architecture Description
=======================

* Encapsulation and collaboration - restricting friendship - reducing public interface

Summary:
- DrawSketchHandler to ViewProviderSketch friendship regulated via attorney.
- ShortcutListener to ViewProviderSketch friendship regulated via attorney.
- EditModeCoinManager (new class) to ViewProviderSketch friendship regulated via attorney.
- ViewProviderSketch public interface is heavily reduced.

In further detail:
While access from ViewProviderSketch to other classes is regulated via their public interface, DrawSketchHandler, ShortcutListener and EditCoinManager (new class) access
to ViewProviderSketch non-public interface via attorneys. Previously, it was an unrestricted access (friend classes). Now this interface is restricted and regulated via attorneys.
This increases the encapsulation of ViewProviderSketch, reduces the coupling between classes and promotes an ordered growth. This I call the "collaboration interface".

At the same time, ViewProviderSketch substantially reduces its public interface. Access from Command draw handlers (deriving from DrawSketchHandler) is intended to be restricted to
the functionality exposed by DrawSketchHandler to its derived classes. However, this is still only partly enforced to keep the refactoring within limits. A further refactoring of
DrawSketchHandler and derivatives is for future discussion.

* Complexity and delegation

Summary:
- Complexity of coin node management is dealt with by delegation to helper classes and specialised objects.

In further detail:

ViewProviderSketch is halved in terms of code size. Higher level ViewProviderSketch functions remain

* Automatic update of parameters - Parameter observer nested classes

Summary:
- ViewProviderSketch and CoinManager get their own observer nested classes to monitor the parameters relevant to them and automatically update on change.

The split enables that each class deals only with parameters within their own responsibilities, effectively isolating the specifics and decoupling the implementations. It is
more convenient as there is no need to leave edit mode to update parameters. It is more compact as it leverages core code.

More information:
https://forum.freecadweb.org/viewtopic.php?p=553257#p553257
2021-12-27 21:03:51 +01:00

2388 lines
87 KiB
C++

/***************************************************************************
* Copyright (c) 2014 Abdullah Tahiri <abdullah.tahiri.yo@gmail.com> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
# include <cfloat>
# include <memory>
# include <QMessageBox>
# include <Precision.hxx>
# include <QApplication>
# include <QMessageBox>
# include <Inventor/SbString.h>
#endif
#include <Base/Console.h>
#include <App/Application.h>
#include <Gui/Application.h>
#include <Gui/Document.h>
#include <Gui/Selection.h>
#include <Gui/CommandT.h>
#include <Gui/MainWindow.h>
#include <Gui/DlgEditFileIncludePropertyExternal.h>
#include <Gui/Action.h>
#include <Gui/BitmapFactory.h>
#include "ViewProviderSketch.h"
#include "DrawSketchHandler.h"
#include <Mod/Part/App/Geometry.h>
#include <Mod/Sketcher/App/SketchObject.h>
#include <Mod/Sketcher/App/SolverGeometryExtension.h>
#include "ViewProviderSketch.h"
#include "SketchRectangularArrayDialog.h"
#include "Utils.h"
using namespace std;
using namespace SketcherGui;
using namespace Sketcher;
bool isSketcherAcceleratorActive(Gui::Document *doc, bool actsOnSelection)
{
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())) {
auto mode = static_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit())
->getSketchMode();
if (mode == ViewProviderSketch::STATUS_NONE ||
mode == ViewProviderSketch::STATUS_SKETCH_UseHandler) {
if (!actsOnSelection)
return true;
else if (Gui::Selection().countObjectsOfType(Sketcher::SketchObject::getClassTypeId()) > 0)
return true;
}
}
}
return false;
}
void ActivateAcceleratorHandler(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());
}
}
}
// ================================================================================
// Close Shape Command
DEF_STD_CMD_A(CmdSketcherCloseShape)
CmdSketcherCloseShape::CmdSketcherCloseShape()
:Command("Sketcher_CloseShape")
{
sAppModule = "Sketcher";
sGroup = "Sketcher";
sMenuText = QT_TR_NOOP("Close shape");
sToolTipText = QT_TR_NOOP("Produce a closed shape by tying the end point "
"of one element with the next element's starting point");
sWhatsThis = "Sketcher_CloseShape";
sStatusTip = sToolTipText;
sPixmap = "Sketcher_CloseShape";
sAccel = "Z, W";
eType = ForEdit;
}
void CmdSketcherCloseShape::activated(int iMsg)
{
Q_UNUSED(iMsg);
// Cancel any in-progress operation
Gui::Document* doc = Gui::Application::Instance->activeDocument();
SketcherGui::ReleaseHandler(doc);
// get the selection
std::vector<Gui::SelectionObject> selection;
selection = getSelection().getSelectionEx(0, Sketcher::SketchObject::getClassTypeId());
// only one sketch with its subelements are allowed to be selected
if (selection.size() != 1) {
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
QObject::tr("Select at least two edges from the sketch."));
return;
}
// get the needed lists and objects
const std::vector<std::string> &SubNames = selection[0].getSubNames();
if (SubNames.size() < 2) {
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
QObject::tr("Select at least two edges from the sketch."));
return;
}
Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
int GeoIdFirst = -1;
int GeoIdLast = -1;
// undo command open
openCommand(QT_TRANSLATE_NOOP("Command", "Add coincident constraint"));
// go through the selected subelements
for (size_t i=0; i < (SubNames.size() - 1); i++) {
// only handle edges
if (SubNames[i].size() > 4 && SubNames[i].substr(0,4) == "Edge" &&
SubNames[i+1].size() > 4 && SubNames[i+1].substr(0,4) == "Edge") {
int GeoId1 = std::atoi(SubNames[i].substr(4,4000).c_str()) - 1;
int GeoId2 = std::atoi(SubNames[i+1].substr(4,4000).c_str()) - 1;
if (GeoIdFirst == -1)
GeoIdFirst = GeoId1;
GeoIdLast = GeoId2;
const Part::Geometry *geo1 = Obj->getGeometry(GeoId1);
const Part::Geometry *geo2 = Obj->getGeometry(GeoId2);
if ((geo1->getTypeId() != Part::GeomLineSegment::getClassTypeId() &&
geo1->getTypeId() != Part::GeomArcOfCircle::getClassTypeId()) ||
(geo2->getTypeId() != Part::GeomLineSegment::getClassTypeId() &&
geo2->getTypeId() != Part::GeomArcOfCircle::getClassTypeId())) {
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Impossible constraint"),
QObject::tr("One selected edge is not connectable"));
abortCommand();
return;
}
// Check for the special case of closing a shape with two lines to avoid overlap
if (SubNames.size() == 2 &&
geo1->getTypeId() == Part::GeomLineSegment::getClassTypeId() &&
geo2->getTypeId() == Part::GeomLineSegment::getClassTypeId() ) {
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
QObject::tr("Closing a shape formed by exactly two lines makes no sense."));
abortCommand();
return;
}
Gui::cmdAppObjectArgs(selection[0].getObject(),
"addConstraint(Sketcher.Constraint('Coincident', %d, %d, %d, %d)) ",
GeoId1, static_cast<int>(Sketcher::PointPos::end), GeoId2, static_cast<int>(Sketcher::PointPos::start));
}
}
// Close Last Edge with First Edge
Gui::cmdAppObjectArgs(selection[0].getObject(),
"addConstraint(Sketcher.Constraint('Coincident', %d, %d, %d, %d)) ",
GeoIdLast, static_cast<int>(Sketcher::PointPos::end), GeoIdFirst, static_cast<int>(Sketcher::PointPos::start));
// finish the transaction and update, and clear the selection (convenience)
commitCommand();
tryAutoRecompute(Obj);
getSelection().clearSelection();
}
bool CmdSketcherCloseShape::isActive(void)
{
return isSketcherAcceleratorActive(getActiveGuiDocument(), true);
}
// ================================================================================
// Connect Edges Command
DEF_STD_CMD_A(CmdSketcherConnect)
CmdSketcherConnect::CmdSketcherConnect()
:Command("Sketcher_ConnectLines")
{
sAppModule = "Sketcher";
sGroup = "Sketcher";
sMenuText = QT_TR_NOOP("Connect edges");
sToolTipText = QT_TR_NOOP("Tie the end point of the element with next element's starting point");
sWhatsThis = "Sketcher_ConnectLines";
sStatusTip = sToolTipText;
sPixmap = "Sketcher_ConnectLines";
sAccel = "Z, J";
eType = ForEdit;
}
void CmdSketcherConnect::activated(int iMsg)
{
Q_UNUSED(iMsg);
// Cancel any in-progress operation
Gui::Document* doc = Gui::Application::Instance->activeDocument();
SketcherGui::ReleaseHandler(doc);
// get the selection
std::vector<Gui::SelectionObject> selection;
selection = getSelection().getSelectionEx(0, Sketcher::SketchObject::getClassTypeId());
// only one sketch with its subelements are allowed to be selected
if (selection.size() != 1) {
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
QObject::tr("Select at least two edges from the sketch."));
return;
}
// get the needed lists and objects
const std::vector<std::string> &SubNames = selection[0].getSubNames();
if (SubNames.size() < 2) {
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
QObject::tr("Select at least two edges from the sketch."));
return;
}
Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
// undo command open
openCommand(QT_TRANSLATE_NOOP("Command", "Add coincident constraint"));
// go through the selected subelements
for (unsigned int i=0; i<(SubNames.size()-1); i++ ) {
// only handle edges
if (SubNames[i].size() > 4 && SubNames[i].substr(0,4) == "Edge" &&
SubNames[i+1].size() > 4 && SubNames[i+1].substr(0,4) == "Edge") {
int GeoId1 = std::atoi(SubNames[i].substr(4,4000).c_str()) - 1;
int GeoId2 = std::atoi(SubNames[i+1].substr(4,4000).c_str()) - 1;
const Part::Geometry *geo1 = Obj->getGeometry(GeoId1);
const Part::Geometry *geo2 = Obj->getGeometry(GeoId2);
if ((geo1->getTypeId() != Part::GeomLineSegment::getClassTypeId() &&
geo1->getTypeId() != Part::GeomArcOfCircle::getClassTypeId()) ||
(geo2->getTypeId() != Part::GeomLineSegment::getClassTypeId() &&
geo2->getTypeId() != Part::GeomArcOfCircle::getClassTypeId())) {
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Impossible constraint"),
QObject::tr("One selected edge is not connectable"));
abortCommand();
return;
}
Gui::cmdAppObjectArgs(selection[0].getObject(),"addConstraint(Sketcher.Constraint('Coincident',%d,%d,%d,%d)) ",
GeoId1,static_cast<int>(Sketcher::PointPos::end),GeoId2,static_cast<int>(Sketcher::PointPos::start));
}
}
// finish the transaction and update, and clear the selection (convenience)
commitCommand();
tryAutoRecompute(Obj);
getSelection().clearSelection();
}
bool CmdSketcherConnect::isActive(void)
{
return isSketcherAcceleratorActive(getActiveGuiDocument(), true);
}
// ================================================================================
// Select Constraints of selected elements
DEF_STD_CMD_A(CmdSketcherSelectConstraints)
CmdSketcherSelectConstraints::CmdSketcherSelectConstraints()
:Command("Sketcher_SelectConstraints")
{
sAppModule = "Sketcher";
sGroup = "Sketcher";
sMenuText = QT_TR_NOOP("Select associated constraints");
sToolTipText = QT_TR_NOOP("Select the constraints associated with the selected geometrical elements");
sWhatsThis = "Sketcher_SelectConstraints";
sStatusTip = sToolTipText;
sPixmap = "Sketcher_SelectConstraints";
sAccel = "Z, K";
eType = ForEdit;
}
void CmdSketcherSelectConstraints::activated(int iMsg)
{
Q_UNUSED(iMsg);
// get the selection
std::vector<Gui::SelectionObject> selection;
selection = getSelection().getSelectionEx(0, Sketcher::SketchObject::getClassTypeId());
// Cancel any in-progress operation
Gui::Document* doc = Gui::Application::Instance->activeDocument();
SketcherGui::ReleaseHandler(doc);
// only one sketch with its subelements are allowed to be selected
if (selection.size() != 1) {
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
QObject::tr("Select elements from a single 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::string doc_name = Obj->getDocument()->getName();
std::string obj_name = Obj->getNameInDocument();
getSelection().clearSelection();
std::vector<std::string> constraintSubNames;
// go through the selected subelements
for (std::vector<std::string>::const_iterator it=SubNames.begin(); it != SubNames.end(); ++it) {
// only handle edges
if (it->size() > 4 && it->substr(0,4) == "Edge") {
int GeoId = std::atoi(it->substr(4,4000).c_str()) - 1;
// push all the constraints
int i = 0;
for (std::vector< Sketcher::Constraint * >::const_iterator it= vals.begin();
it != vals.end(); ++it,++i)
{
if ((*it)->First == GeoId || (*it)->Second == GeoId || (*it)->Third == GeoId) {
constraintSubNames.push_back(Sketcher::PropertyConstraintList::getConstraintName(i));
}
}
}
}
if(!constraintSubNames.empty())
Gui::Selection().addSelections(doc_name.c_str(), obj_name.c_str(), constraintSubNames);
}
bool CmdSketcherSelectConstraints::isActive(void)
{
return isSketcherAcceleratorActive(getActiveGuiDocument(), true);
}
// ================================================================================
// Select Origin
DEF_STD_CMD_A(CmdSketcherSelectOrigin)
CmdSketcherSelectOrigin::CmdSketcherSelectOrigin()
:Command("Sketcher_SelectOrigin")
{
sAppModule = "Sketcher";
sGroup = "Sketcher";
sMenuText = QT_TR_NOOP("Select origin");
sToolTipText = QT_TR_NOOP("Select the local origin point of the sketch");
sWhatsThis = "Sketcher_SelectOrigin";
sStatusTip = sToolTipText;
sPixmap = "Sketcher_SelectOrigin";
sAccel = "Z, O";
eType = ForEdit;
}
void CmdSketcherSelectOrigin::activated(int iMsg)
{
Q_UNUSED(iMsg);
Gui::Document * doc= getActiveGuiDocument();
ReleaseHandler(doc);
SketcherGui::ViewProviderSketch* vp = static_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit());
Sketcher::SketchObject* Obj= vp->getSketchObject();
// ViewProviderSketch * vp = static_cast<ViewProviderSketch *>(Gui::Application::Instance->getViewProvider(docobj));
// Sketcher::SketchObject* Obj = vp->getSketchObject();
std::string doc_name = Obj->getDocument()->getName();
std::string obj_name = Obj->getNameInDocument();
std::stringstream ss;
ss << "RootPoint";
if(Gui::Selection().isSelected(doc_name.c_str(), obj_name.c_str(), ss.str().c_str()))
Gui::Selection().rmvSelection(doc_name.c_str(), obj_name.c_str(), ss.str().c_str());
else
Gui::Selection().addSelection(doc_name.c_str(), obj_name.c_str(), ss.str().c_str());
}
bool CmdSketcherSelectOrigin::isActive(void)
{
return isSketcherAcceleratorActive(getActiveGuiDocument(), false);
}
// ================================================================================
// Select Vertical Axis
DEF_STD_CMD_A(CmdSketcherSelectVerticalAxis)
CmdSketcherSelectVerticalAxis::CmdSketcherSelectVerticalAxis()
:Command("Sketcher_SelectVerticalAxis")
{
sAppModule = "Sketcher";
sGroup = "Sketcher";
sMenuText = QT_TR_NOOP("Select vertical axis");
sToolTipText = QT_TR_NOOP("Select the local vertical axis of the sketch");
sWhatsThis = "Sketcher_SelectVerticalAxis";
sStatusTip = sToolTipText;
sPixmap = "Sketcher_SelectVerticalAxis";
sAccel = "Z, V";
eType = ForEdit;
}
void CmdSketcherSelectVerticalAxis::activated(int iMsg)
{
Q_UNUSED(iMsg);
Gui::Document * doc= getActiveGuiDocument();
ReleaseHandler(doc);
SketcherGui::ViewProviderSketch* vp = static_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit());
Sketcher::SketchObject* Obj= vp->getSketchObject();
std::string doc_name = Obj->getDocument()->getName();
std::string obj_name = Obj->getNameInDocument();
std::stringstream ss;
ss << "V_Axis";
if(Gui::Selection().isSelected(doc_name.c_str(), obj_name.c_str(), ss.str().c_str()))
Gui::Selection().rmvSelection(doc_name.c_str(), obj_name.c_str(), ss.str().c_str());
else
Gui::Selection().addSelection(doc_name.c_str(), obj_name.c_str(), ss.str().c_str());
}
bool CmdSketcherSelectVerticalAxis::isActive(void)
{
return isSketcherAcceleratorActive(getActiveGuiDocument(), false);
}
// ================================================================================
// Select Horizontal Axis
DEF_STD_CMD_A(CmdSketcherSelectHorizontalAxis)
CmdSketcherSelectHorizontalAxis::CmdSketcherSelectHorizontalAxis()
:Command("Sketcher_SelectHorizontalAxis")
{
sAppModule = "Sketcher";
sGroup = "Sketcher";
sMenuText = QT_TR_NOOP("Select horizontal axis");
sToolTipText = QT_TR_NOOP("Select the local horizontal axis of the sketch");
sWhatsThis = "Sketcher_SelectHorizontalAxis";
sStatusTip = sToolTipText;
sPixmap = "Sketcher_SelectHorizontalAxis";
sAccel = "Z, H";
eType = ForEdit;
}
void CmdSketcherSelectHorizontalAxis::activated(int iMsg)
{
Q_UNUSED(iMsg);
Gui::Document * doc= getActiveGuiDocument();
ReleaseHandler(doc);
SketcherGui::ViewProviderSketch* vp = static_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit());
Sketcher::SketchObject* Obj= vp->getSketchObject();
std::string doc_name = Obj->getDocument()->getName();
std::string obj_name = Obj->getNameInDocument();
std::stringstream ss;
ss << "H_Axis";
if(Gui::Selection().isSelected(doc_name.c_str(), obj_name.c_str(), ss.str().c_str()))
Gui::Selection().rmvSelection(doc_name.c_str(), obj_name.c_str(), ss.str().c_str());
else
Gui::Selection().addSelection(doc_name.c_str(), obj_name.c_str(), ss.str().c_str());
}
bool CmdSketcherSelectHorizontalAxis::isActive(void)
{
return isSketcherAcceleratorActive(getActiveGuiDocument(), false);
}
// ================================================================================
DEF_STD_CMD_A(CmdSketcherSelectRedundantConstraints)
CmdSketcherSelectRedundantConstraints::CmdSketcherSelectRedundantConstraints()
:Command("Sketcher_SelectRedundantConstraints")
{
sAppModule = "Sketcher";
sGroup = "Sketcher";
sMenuText = QT_TR_NOOP("Select redundant constraints");
sToolTipText = QT_TR_NOOP("Select redundant constraints");
sWhatsThis = "Sketcher_SelectRedundantConstraints";
sStatusTip = sToolTipText;
sPixmap = "Sketcher_SelectRedundantConstraints";
sAccel = "Z, P, R";
eType = ForEdit;
}
void CmdSketcherSelectRedundantConstraints::activated(int iMsg)
{
Q_UNUSED(iMsg);
Gui::Document * doc= getActiveGuiDocument();
ReleaseHandler(doc);
SketcherGui::ViewProviderSketch* vp = static_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit());
Sketcher::SketchObject* Obj= vp->getSketchObject();
std::string doc_name = Obj->getDocument()->getName();
std::string obj_name = Obj->getNameInDocument();
// get the needed lists and objects
const std::vector< int > &solverredundant = vp->getSketchObject()->getLastRedundant();
const std::vector< Sketcher::Constraint * > &vals = Obj->Constraints.getValues();
getSelection().clearSelection();
// push the constraints
std::vector<std::string> constraintSubNames;
int i = 0;
for (std::vector< Sketcher::Constraint * >::const_iterator it= vals.begin();it != vals.end(); ++it,++i) {
for(std::vector< int >::const_iterator itc= solverredundant.begin();itc != solverredundant.end(); ++itc) {
if ((*itc) - 1 == i) {
constraintSubNames.push_back(Sketcher::PropertyConstraintList::getConstraintName(i));
break;
}
}
}
if(!constraintSubNames.empty())
Gui::Selection().addSelections(doc_name.c_str(), obj_name.c_str(), constraintSubNames);
}
bool CmdSketcherSelectRedundantConstraints::isActive(void)
{
return isSketcherAcceleratorActive(getActiveGuiDocument(), false);
}
// ================================================================================
DEF_STD_CMD_A(CmdSketcherSelectMalformedConstraints)
CmdSketcherSelectMalformedConstraints::CmdSketcherSelectMalformedConstraints()
:Command("Sketcher_SelectMalformedConstraints")
{
sAppModule = "Sketcher";
sGroup = "Sketcher";
sMenuText = QT_TR_NOOP("Select malformed constraints");
sToolTipText = QT_TR_NOOP("Select malformed constraints");
sWhatsThis = "Sketcher_SelectMalformedConstraints";
sStatusTip = sToolTipText;
sPixmap = "Sketcher_SelectMalformedConstraints";
sAccel = "Z, P, M";
eType = ForEdit;
}
void CmdSketcherSelectMalformedConstraints::activated(int iMsg)
{
Q_UNUSED(iMsg);
Gui::Document * doc= getActiveGuiDocument();
ReleaseHandler(doc);
SketcherGui::ViewProviderSketch* vp = static_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit());
Sketcher::SketchObject* Obj= vp->getSketchObject();
std::string doc_name = Obj->getDocument()->getName();
std::string obj_name = Obj->getNameInDocument();
// get the needed lists and objects
const std::vector< int > &solvermalformed = vp->getSketchObject()->getLastMalformedConstraints();
const std::vector< Sketcher::Constraint * > &vals = Obj->Constraints.getValues();
getSelection().clearSelection();
// push the constraints
std::vector<std::string> constraintSubNames;
int i = 0;
for (std::vector< Sketcher::Constraint * >::const_iterator it= vals.begin();it != vals.end(); ++it,++i) {
for(std::vector< int >::const_iterator itc= solvermalformed.begin();itc != solvermalformed.end(); ++itc) {
if ((*itc) - 1 == i) {
constraintSubNames.push_back(Sketcher::PropertyConstraintList::getConstraintName(i));
break;
}
}
}
if(!constraintSubNames.empty())
Gui::Selection().addSelections(doc_name.c_str(), obj_name.c_str(), constraintSubNames);
}
bool CmdSketcherSelectMalformedConstraints::isActive(void)
{
return isSketcherAcceleratorActive(getActiveGuiDocument(), false);
}
// ================================================================================
DEF_STD_CMD_A(CmdSketcherSelectPartiallyRedundantConstraints)
CmdSketcherSelectPartiallyRedundantConstraints::CmdSketcherSelectPartiallyRedundantConstraints()
:Command("Sketcher_SelectPartiallyRedundantConstraints")
{
sAppModule = "Sketcher";
sGroup = "Sketcher";
sMenuText = QT_TR_NOOP("Select partially redundant constraints");
sToolTipText = QT_TR_NOOP("Select partially redundant constraints");
sWhatsThis = "Sketcher_SelectPartiallyRedundantConstraints";
sStatusTip = sToolTipText;
sPixmap = "Sketcher_SelectPartiallyRedundantConstraints";
sAccel = "Z, P, P";
eType = ForEdit;
}
void CmdSketcherSelectPartiallyRedundantConstraints::activated(int iMsg)
{
Q_UNUSED(iMsg);
Gui::Document * doc= getActiveGuiDocument();
ReleaseHandler(doc);
SketcherGui::ViewProviderSketch* vp = static_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit());
Sketcher::SketchObject* Obj= vp->getSketchObject();
std::string doc_name = Obj->getDocument()->getName();
std::string obj_name = Obj->getNameInDocument();
// get the needed lists and objects
const std::vector< int > &solverpartiallyredundant = vp->getSketchObject()->getLastPartiallyRedundant();
const std::vector< Sketcher::Constraint * > &vals = Obj->Constraints.getValues();
getSelection().clearSelection();
// push the constraints
std::vector<std::string> constraintSubNames;
int i = 0;
for (std::vector< Sketcher::Constraint * >::const_iterator it= vals.begin();it != vals.end(); ++it,++i) {
for(std::vector< int >::const_iterator itc= solverpartiallyredundant.begin();itc != solverpartiallyredundant.end(); ++itc) {
if ((*itc) - 1 == i) {
constraintSubNames.push_back(Sketcher::PropertyConstraintList::getConstraintName(i));
break;
}
}
}
if(!constraintSubNames.empty())
Gui::Selection().addSelections(doc_name.c_str(), obj_name.c_str(), constraintSubNames);
}
bool CmdSketcherSelectPartiallyRedundantConstraints::isActive(void)
{
return isSketcherAcceleratorActive(getActiveGuiDocument(), false);
}
// ================================================================================
DEF_STD_CMD_A(CmdSketcherSelectConflictingConstraints)
CmdSketcherSelectConflictingConstraints::CmdSketcherSelectConflictingConstraints()
:Command("Sketcher_SelectConflictingConstraints")
{
sAppModule = "Sketcher";
sGroup = "Sketcher";
sMenuText = QT_TR_NOOP("Select conflicting constraints");
sToolTipText = QT_TR_NOOP("Select conflicting constraints");
sWhatsThis = "Sketcher_SelectConflictingConstraints";
sStatusTip = sToolTipText;
sPixmap = "Sketcher_SelectConflictingConstraints";
sAccel = "Z, P, C";
eType = ForEdit;
}
void CmdSketcherSelectConflictingConstraints::activated(int iMsg)
{
Q_UNUSED(iMsg);
Gui::Document * doc= getActiveGuiDocument();
ReleaseHandler(doc);
SketcherGui::ViewProviderSketch* vp = static_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit());
Sketcher::SketchObject* Obj= vp->getSketchObject();
std::string doc_name = Obj->getDocument()->getName();
std::string obj_name = Obj->getNameInDocument();
// get the needed lists and objects
const std::vector< int > &solverconflicting = vp->getSketchObject()->getLastConflicting();
const std::vector< Sketcher::Constraint * > &vals = Obj->Constraints.getValues();
getSelection().clearSelection();
// push the constraints
std::vector<std::string> constraintSubNames;
int i = 0;
for (std::vector< Sketcher::Constraint * >::const_iterator it= vals.begin();it != vals.end(); ++it,++i) {
for (std::vector< int >::const_iterator itc= solverconflicting.begin();itc != solverconflicting.end(); ++itc) {
if ((*itc) - 1 == i) {
constraintSubNames.push_back(Sketcher::PropertyConstraintList::getConstraintName(i));
break;
}
}
}
if(!constraintSubNames.empty())
Gui::Selection().addSelections(doc_name.c_str(), obj_name.c_str(), constraintSubNames);
}
bool CmdSketcherSelectConflictingConstraints::isActive(void)
{
return isSketcherAcceleratorActive(getActiveGuiDocument(), false);
}
// ================================================================================
DEF_STD_CMD_A(CmdSketcherSelectElementsAssociatedWithConstraints)
CmdSketcherSelectElementsAssociatedWithConstraints::CmdSketcherSelectElementsAssociatedWithConstraints()
:Command("Sketcher_SelectElementsAssociatedWithConstraints")
{
sAppModule = "Sketcher";
sGroup = "Sketcher";
sMenuText = QT_TR_NOOP("Select associated geometry");
sToolTipText = QT_TR_NOOP("Select the geometrical elements associated with the selected constraints");
sWhatsThis = "Sketcher_SelectElementsAssociatedWithConstraints";
sStatusTip = sToolTipText;
sPixmap = "Sketcher_SelectElementsAssociatedWithConstraints";
sAccel = "Z, E";
eType = ForEdit;
}
void CmdSketcherSelectElementsAssociatedWithConstraints::activated(int iMsg)
{
Q_UNUSED(iMsg);
std::vector<Gui::SelectionObject> selection = Gui::Selection().getSelectionEx();
Gui::Document * doc= getActiveGuiDocument();
ReleaseHandler(doc);
SketcherGui::ViewProviderSketch* vp = static_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit());
Sketcher::SketchObject* Obj= vp->getSketchObject();
const std::vector<std::string> &SubNames = selection[0].getSubNames();
const std::vector< Sketcher::Constraint * > &vals = Obj->Constraints.getValues();
getSelection().clearSelection();
std::string doc_name = Obj->getDocument()->getName();
std::string obj_name = Obj->getNameInDocument();
std::stringstream ss;
std::vector<std::string> elementSubNames;
// 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);
if(ConstrId < static_cast<int>(vals.size())){
if(vals[ConstrId]->First!=GeoEnum::GeoUndef){
ss.str(std::string());
switch(vals[ConstrId]->FirstPos)
{
case Sketcher::PointPos::none:
ss << "Edge" << vals[ConstrId]->First + 1;
break;
case Sketcher::PointPos::start:
case Sketcher::PointPos::end:
case Sketcher::PointPos::mid:
int vertex = Obj->getVertexIndexGeoPos(vals[ConstrId]->First,vals[ConstrId]->FirstPos);
if(vertex>-1)
ss << "Vertex" << vertex + 1;
break;
}
elementSubNames.push_back(ss.str());
}
if(vals[ConstrId]->Second!=GeoEnum::GeoUndef){
ss.str(std::string());
switch(vals[ConstrId]->SecondPos)
{
case Sketcher::PointPos::none:
ss << "Edge" << vals[ConstrId]->Second + 1;
break;
case Sketcher::PointPos::start:
case Sketcher::PointPos::end:
case Sketcher::PointPos::mid:
int vertex = Obj->getVertexIndexGeoPos(vals[ConstrId]->Second,vals[ConstrId]->SecondPos);
if(vertex>-1)
ss << "Vertex" << vertex + 1;
break;
}
elementSubNames.push_back(ss.str());
}
if(vals[ConstrId]->Third!=GeoEnum::GeoUndef){
ss.str(std::string());
switch(vals[ConstrId]->ThirdPos)
{
case Sketcher::PointPos::none:
ss << "Edge" << vals[ConstrId]->Third + 1;
break;
case Sketcher::PointPos::start:
case Sketcher::PointPos::end:
case Sketcher::PointPos::mid:
int vertex = Obj->getVertexIndexGeoPos(vals[ConstrId]->Third,vals[ConstrId]->ThirdPos);
if(vertex>-1)
ss << "Vertex" << vertex + 1;
break;
}
elementSubNames.push_back(ss.str());
}
}
}
}
if (elementSubNames.empty()) {
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("No constraint selected"),
QObject::tr("At least one constraint must be selected"));
}
else {
Gui::Selection().addSelections(doc_name.c_str(), obj_name.c_str(), elementSubNames);
}
}
bool CmdSketcherSelectElementsAssociatedWithConstraints::isActive(void)
{
return isSketcherAcceleratorActive(getActiveGuiDocument(), true);
}
// ================================================================================
DEF_STD_CMD_A(CmdSketcherSelectElementsWithDoFs)
CmdSketcherSelectElementsWithDoFs::CmdSketcherSelectElementsWithDoFs()
:Command("Sketcher_SelectElementsWithDoFs")
{
sAppModule = "Sketcher";
sGroup = "Sketcher";
sMenuText = QT_TR_NOOP("Select unconstrained DoF");
sToolTipText = QT_TR_NOOP("Select geometrical elements where the solver still detects unconstrained degrees of freedom.");
sWhatsThis = "Sketcher_SelectElementsWithDoFs";
sStatusTip = sToolTipText;
sPixmap = "Sketcher_SelectElementsWithDoFs";
sAccel = "Z, F";
eType = ForEdit;
}
void CmdSketcherSelectElementsWithDoFs::activated(int iMsg)
{
Q_UNUSED(iMsg);
getSelection().clearSelection();
Gui::Document * doc= getActiveGuiDocument();
ReleaseHandler(doc);
SketcherGui::ViewProviderSketch* vp = static_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit());
Sketcher::SketchObject* Obj= vp->getSketchObject();
std::string doc_name = Obj->getDocument()->getName();
std::string obj_name = Obj->getNameInDocument();
std::stringstream ss;
auto geos = Obj->getInternalGeometry();
std::vector<std::string> elementSubNames;
auto testselectvertex = [&Obj, &ss, &elementSubNames](int geoId, PointPos pos) {
ss.str(std::string());
int vertex = Obj->getVertexIndexGeoPos(geoId, pos);
if (vertex > -1) {
ss << "Vertex" << vertex + 1;
elementSubNames.push_back(ss.str());
}
};
auto testselectedge = [&ss, &elementSubNames](int geoId) {
ss.str(std::string());
ss << "Edge" << geoId + 1;
elementSubNames.push_back(ss.str());
};
int geoid = 0;
for (auto geo : geos) {
if(geo) {
if(geo->hasExtension(Sketcher::SolverGeometryExtension::getClassTypeId())) {
auto solvext = std::static_pointer_cast<const Sketcher::SolverGeometryExtension>(
geo->getExtension(Sketcher::SolverGeometryExtension::getClassTypeId()).lock());
if (solvext->getGeometry() == Sketcher::SolverGeometryExtension::NotFullyConstraint) {
// Coded for consistency with getGeometryWithDependentParameters, read the comments
// on that function
if (solvext->getEdge() == SolverGeometryExtension::Dependent)
testselectedge(geoid);
if (solvext->getStart() == SolverGeometryExtension::Dependent)
testselectvertex(geoid, Sketcher::PointPos::start);
if (solvext->getEnd() == SolverGeometryExtension::Dependent)
testselectvertex(geoid, Sketcher::PointPos::end);
if (solvext->getMid() == SolverGeometryExtension::Dependent)
testselectvertex(geoid, Sketcher::PointPos::mid);
}
}
}
geoid++;
}
if (!elementSubNames.empty()) {
Gui::Selection().addSelections(doc_name.c_str(), obj_name.c_str(), elementSubNames);
}
}
bool CmdSketcherSelectElementsWithDoFs::isActive(void)
{
return isSketcherAcceleratorActive(getActiveGuiDocument(), false);
}
// ================================================================================
DEF_STD_CMD_A(CmdSketcherRestoreInternalAlignmentGeometry)
CmdSketcherRestoreInternalAlignmentGeometry::CmdSketcherRestoreInternalAlignmentGeometry()
:Command("Sketcher_RestoreInternalAlignmentGeometry")
{
sAppModule = "Sketcher";
sGroup = "Sketcher";
sMenuText = QT_TR_NOOP("Show/hide internal geometry");
sToolTipText = QT_TR_NOOP("Show all internal geometry or hide unused internal geometry");
sWhatsThis = "Sketcher_RestoreInternalAlignmentGeometry";
sStatusTip = sToolTipText;
sPixmap = "Sketcher_Element_Ellipse_All";
sAccel = "Z, I";
eType = ForEdit;
}
void CmdSketcherRestoreInternalAlignmentGeometry::activated(int iMsg)
{
Q_UNUSED(iMsg);
// Cancel any in-progress operation
Gui::Document* doc = Gui::Application::Instance->activeDocument();
SketcherGui::ReleaseHandler(doc);
// get the selection
std::vector<Gui::SelectionObject> selection;
selection = getSelection().getSelectionEx(0, Sketcher::SketchObject::getClassTypeId());
// only one sketch with its subelements are allowed to be selected
if (selection.size() != 1) {
QMessageBox::warning(Gui::getMainWindow(),
QObject::tr("Wrong selection"),
QObject::tr("Select elements from a single 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());
getSelection().clearSelection();
// go through the selected subelements
for (std::vector<std::string>::const_iterator it=SubNames.begin(); it != SubNames.end(); ++it) {
// only handle edges
if ((it->size() > 4 && it->substr(0,4) == "Edge") ||
(it->size() > 12 && it->substr(0,12) == "ExternalEdge")) {
int GeoId;
if (it->substr(0,4) == "Edge")
GeoId = std::atoi(it->substr(4,4000).c_str()) - 1;
else
GeoId = -std::atoi(it->substr(12,4000).c_str()) - 2;
const Part::Geometry *geo = Obj->getGeometry(GeoId);
// Only for supported types
if (geo->getTypeId() == Part::GeomEllipse::getClassTypeId() ||
geo->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId() ||
geo->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId() ||
geo->getTypeId() == Part::GeomArcOfParabola::getClassTypeId() ||
geo->getTypeId() == Part::GeomBSplineCurve::getClassTypeId()) {
int currentgeoid = Obj->getHighestCurveIndex();
try {
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Exposing Internal Geometry"));
Gui::cmdAppObjectArgs(Obj, "exposeInternalGeometry(%d)", GeoId);
int aftergeoid = Obj->getHighestCurveIndex();
if(aftergeoid == currentgeoid) { // if we did not expose anything, deleteunused
Gui::cmdAppObjectArgs(Obj, "deleteUnusedInternalGeometry(%d)", GeoId);
}
}
catch (const Base::Exception& e) {
Base::Console().Error("%s\n", e.what());
Gui::Command::abortCommand();
tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(Obj));
return;
}
Gui::Command::commitCommand();
tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(Obj));
}
}
}
}
bool CmdSketcherRestoreInternalAlignmentGeometry::isActive(void)
{
return isSketcherAcceleratorActive(getActiveGuiDocument(), true);
}
// ================================================================================
DEF_STD_CMD_A(CmdSketcherSymmetry)
CmdSketcherSymmetry::CmdSketcherSymmetry()
:Command("Sketcher_Symmetry")
{
sAppModule = "Sketcher";
sGroup = "Sketcher";
sMenuText = QT_TR_NOOP("Symmetry");
sToolTipText = QT_TR_NOOP("Creates symmetric geometry with respect to the last selected line or point");
sWhatsThis = "Sketcher_Symmetry";
sStatusTip = sToolTipText;
sPixmap = "Sketcher_Symmetry";
sAccel = "Z, S";
eType = ForEdit;
}
void CmdSketcherSymmetry::activated(int iMsg)
{
Q_UNUSED(iMsg);
// Cancel any in-progress operation
Gui::Document* doc = Gui::Application::Instance->activeDocument();
SketcherGui::ReleaseHandler(doc);
// get the selection
std::vector<Gui::SelectionObject> selection;
selection = getSelection().getSelectionEx(0, Sketcher::SketchObject::getClassTypeId());
// only one sketch with its subelements are allowed to be selected
if (selection.size() != 1) {
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
QObject::tr("Select elements from a single 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 elements from a single sketch."));
return;
}
Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
getSelection().clearSelection();
int LastGeoId = 0;
Sketcher::PointPos LastPointPos = Sketcher::PointPos::none;
const Part::Geometry *LastGeo;
typedef enum { invalid = -1, line = 0, point = 1 } GeoType;
GeoType lastgeotype = invalid;
// create python command with list of elements
std::stringstream stream;
int geoids = 0;
for (std::vector<std::string>::const_iterator it=SubNames.begin(); it != SubNames.end(); ++it) {
// only handle non-external edges
if ((it->size() > 4 && it->substr(0,4) == "Edge") ||
(it->size() > 12 && it->substr(0,12) == "ExternalEdge")) {
if (it->substr(0,4) == "Edge") {
LastGeoId = std::atoi(it->substr(4,4000).c_str()) - 1;
LastPointPos = Sketcher::PointPos::none;
}
else {
LastGeoId = -std::atoi(it->substr(12,4000).c_str()) - 2;
LastPointPos = Sketcher::PointPos::none;
}
// reference can be external or non-external
LastGeo = Obj->getGeometry(LastGeoId);
// Only for supported types
if (LastGeo->getTypeId() == Part::GeomLineSegment::getClassTypeId())
lastgeotype = line;
else
lastgeotype = invalid;
// lines to make symmetric (only non-external)
if (LastGeoId >= 0) {
geoids++;
stream << LastGeoId << ",";
}
}
else if (it->size() > 6 && it->substr(0,6) == "Vertex") {
// only if it is a GeomPoint
int VtId = std::atoi(it->substr(6,4000).c_str()) - 1;
int GeoId;
Sketcher::PointPos PosId;
Obj->getGeoVertexIndex(VtId, GeoId, PosId);
if (Obj->getGeometry(GeoId)->getTypeId() == Part::GeomPoint::getClassTypeId()) {
LastGeoId = GeoId;
LastPointPos = Sketcher::PointPos::start;
lastgeotype = point;
// points to make symmetric
if (LastGeoId >= 0) {
geoids++;
stream << LastGeoId << ",";
}
}
}
}
bool lastvertexoraxis = false;
// check if last selected element is a Vertex, not being a GeomPoint
if (SubNames.rbegin()->size() > 6 && SubNames.rbegin()->substr(0,6) == "Vertex") {
int VtId = std::atoi(SubNames.rbegin()->substr(6,4000).c_str()) - 1;
int GeoId;
Sketcher::PointPos PosId;
Obj->getGeoVertexIndex(VtId, GeoId, PosId);
if (Obj->getGeometry(GeoId)->getTypeId() != Part::GeomPoint::getClassTypeId()) {
LastGeoId = GeoId;
LastPointPos = PosId;
lastgeotype = point;
lastvertexoraxis = true;
}
}
// check if last selected element is horizontal axis
else if (SubNames.rbegin()->size() == 6 && SubNames.rbegin()->substr(0,6) == "H_Axis") {
LastGeoId = Sketcher::GeoEnum::HAxis;
LastPointPos = Sketcher::PointPos::none;
lastgeotype = line;
lastvertexoraxis = true;
}
// check if last selected element is vertical axis
else if (SubNames.rbegin()->size() == 6 && SubNames.rbegin()->substr(0,6) == "V_Axis") {
LastGeoId = Sketcher::GeoEnum::VAxis;
LastPointPos = Sketcher::PointPos::none;
lastgeotype = line;
lastvertexoraxis = true;
}
// check if last selected element is the root point
else if (SubNames.rbegin()->size() == 9 && SubNames.rbegin()->substr(0,9) == "RootPoint") {
LastGeoId = Sketcher::GeoEnum::RtPnt;
LastPointPos = Sketcher::PointPos::start;
lastgeotype = point;
lastvertexoraxis = true;
}
if (geoids == 0 || (geoids == 1 && LastGeoId >= 0 && !lastvertexoraxis)) {
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
QObject::tr("A symmetric construction requires "
"at least two geometric elements, "
"the last geometric element being the reference "
"for the symmetry construction."));
return;
}
if (lastgeotype == invalid) {
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
QObject::tr("The last element must be a point "
"or a line serving as reference "
"for the symmetry construction."));
return;
}
std::string geoIdList = stream.str();
// missing cases:
// 1- Last element is an edge, and is V or H axis
// 2- Last element is a point GeomPoint
// 3- Last element is a point (Vertex)
if (LastGeoId >= 0 && !lastvertexoraxis) {
// if LastGeoId was added remove the last element
int index = geoIdList.rfind(',');
index = geoIdList.rfind(',', index-1);
geoIdList.resize(index);
}
else {
int index = geoIdList.rfind(',');
geoIdList.resize(index);
}
geoIdList.insert(0, 1, '[');
geoIdList.append(1, ']');
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create symmetric geometry"));
try{
Gui::cmdAppObjectArgs(Obj,
"addSymmetric(%s, %d, %d)",
geoIdList.c_str(), LastGeoId, static_cast<int>(LastPointPos));
Gui::Command::commitCommand();
}
catch (const Base::Exception& e) {
Base::Console().Error("%s\n", e.what());
Gui::Command::abortCommand();
}
tryAutoRecomputeIfNotSolve(Obj);
}
bool CmdSketcherSymmetry::isActive(void)
{
return isSketcherAcceleratorActive(getActiveGuiDocument(), true);
}
// ================================================================================
class SketcherCopy : public Gui::Command {
public:
enum Op {
Copy,
Clone,
Move
};
SketcherCopy(const char* name);
void activate(SketcherCopy::Op op);
virtual void activate() = 0;
};
// TODO: replace XPM cursor with SVG file
static const char *cursor_createcopy[]={
"32 32 3 1",
"+ c white",
"# c red",
". c None",
"................................",
".......+........................",
".......+........................",
".......+........................",
".......+........................",
".......+........................",
"................................",
".+++++...+++++..................",
"................................",
".......+........................",
".......+..............###.......",
".......+..............###.......",
".......+..............###.......",
".......+..............###.......",
"......................###.......",
".....###..............###.......",
".....###..............###.......",
".....###..............###.......",
".....###..............###.......",
".....###..............###.......",
".....###..............###.......",
".....###..............###.......",
".....###..............###.......",
".....###..............###.......",
".....###........................",
".....###........................",
".....###........................",
".....###........................",
"................................",
"................................",
"................................",
"................................"};
class DrawSketchHandlerCopy: public DrawSketchHandler
{
public:
DrawSketchHandlerCopy(string geoidlist, int origingeoid,
Sketcher::PointPos originpos, int nelements,
SketcherCopy::Op op)
: Mode(STATUS_SEEK_First)
, snapMode(SnapMode::Free)
, geoIdList(geoidlist)
, Origin()
, OriginGeoId(origingeoid)
, OriginPos(originpos)
, nElements(nelements)
, Op(op)
, EditCurve(2)
{
}
virtual ~DrawSketchHandlerCopy(){}
/// mode table
enum SelectMode {
STATUS_SEEK_First, /**< enum value ----. */
STATUS_End
};
enum class SnapMode {
Free,
Snap5Degree
};
virtual void activated(ViewProviderSketch *sketchgui)
{
setCursor(QPixmap(cursor_createcopy), 7, 7);
Origin = static_cast<Sketcher::SketchObject *>(sketchgui->getObject())->getPoint(OriginGeoId, OriginPos);
EditCurve[0] = Base::Vector2d(Origin.x, Origin.y);
}
virtual void mouseMove(Base::Vector2d onSketchPos)
{
if (Mode == STATUS_SEEK_First) {
if(QApplication::keyboardModifiers() == Qt::ControlModifier)
snapMode = SnapMode::Snap5Degree;
else
snapMode = SnapMode::Free;
float length = (onSketchPos - EditCurve[0]).Length();
float angle = (onSketchPos - EditCurve[0]).Angle();
Base::Vector2d endpoint = onSketchPos;
if (snapMode == SnapMode::Snap5Degree) {
angle = round(angle / (M_PI/36)) * M_PI/36;
endpoint = EditCurve[0] + length * Base::Vector2d(cos(angle),sin(angle));
}
SbString text;
text.sprintf(" (%.1f, %.1fdeg)", length, angle * 180 / M_PI);
setPositionText(endpoint, text);
EditCurve[1] = endpoint;
drawEdit(EditCurve);
if (seekAutoConstraint(sugConstr1, endpoint, Base::Vector2d(0.0, 0.0), AutoConstraint::VERTEX)) {
renderSuggestConstraintsCursor(sugConstr1);
return;
}
}
applyCursor();
}
virtual bool pressButton(Base::Vector2d)
{
if (Mode == STATUS_SEEK_First) {
drawEdit(EditCurve);
Mode = STATUS_End;
}
return true;
}
virtual bool releaseButton(Base::Vector2d onSketchPos)
{
Q_UNUSED(onSketchPos);
if (Mode == STATUS_End)
{
Base::Vector2d vector = EditCurve[1] - EditCurve[0];
unsetCursor();
resetPositionText();
int currentgeoid = static_cast<Sketcher::SketchObject *>(sketchgui->getObject())->getHighestCurveIndex();
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Copy/clone/move geometry"));
try{
if (Op != SketcherCopy::Move) {
Gui::cmdAppObjectArgs(sketchgui->getObject(),
"addCopy(%s, App.Vector(%f, %f, 0), %s)",
geoIdList.c_str(), vector.x, vector.y,
(Op == SketcherCopy::Clone ? "True" : "False"));
}
else {
Gui::cmdAppObjectArgs(sketchgui->getObject(),
"addMove(%s, App.Vector(%f, %f, 0))",
geoIdList.c_str(), vector.x, vector.y);
}
Gui::Command::commitCommand();
}
catch (const Base::Exception& e) {
Base::Console().Error("%s\n", e.what());
Gui::Command::abortCommand();
}
if (Op != SketcherCopy::Move) {
// add auto constraints for the destination copy
if (sugConstr1.size() > 0) {
createAutoConstraints(sugConstr1, currentgeoid+nElements, OriginPos);
sugConstr1.clear();
}
}
else {
if (sugConstr1.size() > 0) {
createAutoConstraints(sugConstr1, OriginGeoId, OriginPos);
sugConstr1.clear();
}
}
tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
EditCurve.clear();
drawEdit(EditCurve);
// no code after this line, Handler gets deleted in ViewProvider
sketchgui->purgeHandler();
}
return true;
}
protected:
SelectMode Mode;
SnapMode snapMode;
string geoIdList;
Base::Vector3d Origin;
int OriginGeoId;
Sketcher::PointPos OriginPos;
int nElements;
SketcherCopy::Op Op;
std::vector<Base::Vector2d> EditCurve;
std::vector<AutoConstraint> sugConstr1;
};
/*---- SketcherCopy definition ----*/
SketcherCopy::SketcherCopy(const char* name): Command(name)
{}
void SketcherCopy::activate(SketcherCopy::Op op)
{
// 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) {
QMessageBox::warning(Gui::getMainWindow(),
QObject::tr("Wrong selection"),
QObject::tr("Select elements from a single 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 elements from a single sketch."));
return;
}
Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
getSelection().clearSelection();
int LastGeoId = 0;
Sketcher::PointPos LastPointPos = Sketcher::PointPos::none;
const Part::Geometry *LastGeo = 0;
// create python command with list of elements
std::stringstream stream;
int geoids = 0;
for (std::vector<std::string>::const_iterator it=SubNames.begin(); it != SubNames.end(); ++it) {
// only handle non-external edges
if (it->size() > 4 && it->substr(0,4) == "Edge") {
LastGeoId = std::atoi(it->substr(4,4000).c_str()) - 1;
LastPointPos = Sketcher::PointPos::none;
LastGeo = Obj->getGeometry(LastGeoId);
// lines to copy
if (LastGeoId >= 0) {
geoids++;
stream << LastGeoId << ",";
}
}
else if (it->size() > 6 && it->substr(0,6) == "Vertex") {
// only if it is a GeomPoint
int VtId = std::atoi(it->substr(6,4000).c_str()) - 1;
int GeoId;
Sketcher::PointPos PosId;
Obj->getGeoVertexIndex(VtId, GeoId, PosId);
if (Obj->getGeometry(GeoId)->getTypeId() == Part::GeomPoint::getClassTypeId()) {
LastGeoId = GeoId;
LastPointPos = Sketcher::PointPos::start;
// points to copy
if (LastGeoId >= 0) {
geoids++;
stream << LastGeoId << ",";
}
}
}
}
// check if last selected element is a Vertex, not being a GeomPoint
if (SubNames.rbegin()->size() > 6 && SubNames.rbegin()->substr(0,6) == "Vertex") {
int VtId = std::atoi(SubNames.rbegin()->substr(6,4000).c_str()) - 1;
int GeoId;
Sketcher::PointPos PosId;
Obj->getGeoVertexIndex(VtId, GeoId, PosId);
if (Obj->getGeometry(GeoId)->getTypeId() != Part::GeomPoint::getClassTypeId()) {
LastGeoId = GeoId;
LastPointPos = PosId;
}
}
if (geoids < 1) {
QMessageBox::warning(Gui::getMainWindow(),
QObject::tr("Wrong selection"),
QObject::tr("A copy requires at least one selected non-external geometric element"));
return;
}
std::string geoIdList = stream.str();
// remove the last added comma and brackets to make the python list
int index = geoIdList.rfind(',');
geoIdList.resize(index);
geoIdList.insert(0, 1, '[');
geoIdList.append(1, ']');
// if the last element is not a point serving as a reference for the copy process
// then make the start point of the last element the copy reference (if it exists, if not the center point)
if (LastPointPos == Sketcher::PointPos::none) {
if (LastGeo->getTypeId() == Part::GeomCircle::getClassTypeId() ||
LastGeo->getTypeId() == Part::GeomEllipse::getClassTypeId()) {
LastPointPos = Sketcher::PointPos::mid;
}
else {
LastPointPos = Sketcher::PointPos::start;
}
}
// Ask the user if they want to clone or to simple copy
/*
int ret = QMessageBox::question(Gui::getMainWindow(), QObject::tr("Dimensional/Geometric constraints"),
QObject::tr("Do you want to clone the object, i.e. substitute dimensional constraints by geometric constraints?"),
QMessageBox::Yes, QMessageBox::No, QMessageBox::Cancel);
// use an equality constraint
if (ret == QMessageBox::Yes) {
clone = true;
}
else if (ret == QMessageBox::Cancel) {
// do nothing
return;
}
*/
ActivateAcceleratorHandler(getActiveGuiDocument(),
new DrawSketchHandlerCopy(geoIdList, LastGeoId, LastPointPos, geoids, op));
}
class CmdSketcherCopy : public SketcherCopy
{
public:
CmdSketcherCopy();
virtual ~CmdSketcherCopy(){}
virtual const char* className() const
{ return "CmdSketcherCopy"; }
virtual void activate();
protected:
virtual void activated(int iMsg);
virtual bool isActive(void);
};
CmdSketcherCopy::CmdSketcherCopy()
:SketcherCopy("Sketcher_Copy")
{
sAppModule = "Sketcher";
sGroup = "Sketcher";
sMenuText = QT_TR_NOOP("Copy");
sToolTipText = QT_TR_NOOP("Creates a simple copy of the geometry taking as reference the last selected point");
sWhatsThis = "Sketcher_Copy";
sStatusTip = sToolTipText;
sPixmap = "Sketcher_Copy";
sAccel = "Z, C";
eType = ForEdit;
}
void CmdSketcherCopy::activated(int iMsg)
{
Q_UNUSED(iMsg);
SketcherCopy::activate(SketcherCopy::Copy);
}
void CmdSketcherCopy::activate()
{
SketcherCopy::activate(SketcherCopy::Copy);
}
bool CmdSketcherCopy::isActive(void)
{
return isSketcherAcceleratorActive(getActiveGuiDocument(), true);
}
// ================================================================================
class CmdSketcherClone : public SketcherCopy
{
public:
CmdSketcherClone();
virtual ~CmdSketcherClone(){}
virtual const char* className() const
{ return "CmdSketcherClone"; }
virtual void activate();
protected:
virtual void activated(int iMsg);
virtual bool isActive(void);
};
CmdSketcherClone::CmdSketcherClone()
:SketcherCopy("Sketcher_Clone")
{
sAppModule = "Sketcher";
sGroup = "Sketcher";
sMenuText = QT_TR_NOOP("Clone");
sToolTipText = QT_TR_NOOP("Creates a clone of the geometry taking as reference the last selected point");
sWhatsThis = "Sketcher_Clone";
sStatusTip = sToolTipText;
sPixmap = "Sketcher_Clone";
sAccel = "Z, L";
eType = ForEdit;
}
void CmdSketcherClone::activated(int iMsg)
{
Q_UNUSED(iMsg);
SketcherCopy::activate(SketcherCopy::Clone);
}
void CmdSketcherClone::activate()
{
SketcherCopy::activate(SketcherCopy::Clone);
}
bool CmdSketcherClone::isActive(void)
{
return isSketcherAcceleratorActive(getActiveGuiDocument(), true);
}
class CmdSketcherMove : public SketcherCopy
{
public:
CmdSketcherMove();
virtual ~CmdSketcherMove(){}
virtual const char* className() const
{ return "CmdSketcherMove"; }
virtual void activate();
protected:
virtual void activated(int iMsg);
virtual bool isActive(void);
};
CmdSketcherMove::CmdSketcherMove()
:SketcherCopy("Sketcher_Move")
{
sAppModule = "Sketcher";
sGroup = "Sketcher";
sMenuText = QT_TR_NOOP("Move");
sToolTipText = QT_TR_NOOP("Moves the geometry taking as reference the last selected point");
sWhatsThis = "Sketcher_Move";
sStatusTip = sToolTipText;
sPixmap = "Sketcher_Move";
sAccel = "Z, M";
eType = ForEdit;
}
void CmdSketcherMove::activated(int iMsg)
{
Q_UNUSED(iMsg);
SketcherCopy::activate(SketcherCopy::Move);
}
void CmdSketcherMove::activate()
{
SketcherCopy::activate(SketcherCopy::Move);
}
bool CmdSketcherMove::isActive(void)
{
return isSketcherAcceleratorActive(getActiveGuiDocument(), true);
}
// ================================================================================
DEF_STD_CMD_ACL(CmdSketcherCompCopy)
CmdSketcherCompCopy::CmdSketcherCompCopy()
: Command("Sketcher_CompCopy")
{
sAppModule = "Sketcher";
sGroup = "Sketcher";
sMenuText = QT_TR_NOOP("Copy");
sToolTipText = QT_TR_NOOP("Creates a clone of the geometry taking as reference the last selected point");
sWhatsThis = "Sketcher_CompCopy";
sStatusTip = sToolTipText;
sAccel = "";
eType = ForEdit;
}
void CmdSketcherCompCopy::activated(int iMsg)
{
if (iMsg<0 || iMsg>2)
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());
if (iMsg == 0){
CmdSketcherClone sc;
sc.activate();
pcAction->setShortcut(QString::fromLatin1(this->getAccel()));
}
else if (iMsg == 1) {
CmdSketcherCopy sc;
sc.activate();
pcAction->setShortcut(QString::fromLatin1(this->getAccel()));
}
else if (iMsg == 2) {
CmdSketcherMove sc;
sc.activate();
pcAction->setShortcut(QString::fromLatin1(""));
}
}
Gui::Action * CmdSketcherCompCopy::createAction(void)
{
Gui::ActionGroup* pcAction = new Gui::ActionGroup(this, Gui::getMainWindow());
pcAction->setDropDownMenu(true);
applyCommandData(this->className(), pcAction);
QAction* clone = pcAction->addAction(QString());
clone->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_Clone"));
QAction* copy = pcAction->addAction(QString());
copy->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_Copy"));
QAction* move = pcAction->addAction(QString());
move->setIcon(Gui::BitmapFactory().iconFromTheme("Sketcher_Move"));
_pcAction = pcAction;
languageChange();
pcAction->setIcon(clone->icon());
int defaultId = 0;
pcAction->setProperty("defaultAction", QVariant(defaultId));
pcAction->setShortcut(QString::fromLatin1(getAccel()));
return pcAction;
}
void CmdSketcherCompCopy::languageChange()
{
Command::languageChange();
if (!_pcAction)
return;
Gui::ActionGroup* pcAction = qobject_cast<Gui::ActionGroup*>(_pcAction);
QList<QAction*> a = pcAction->actions();
QAction* clone = a[0];
clone->setText(QApplication::translate("Sketcher_CompCopy","Clone"));
clone->setToolTip(QApplication::translate("Sketcher_Clone","Creates a clone of the geometry taking as reference the last selected point"));
clone->setStatusTip(QApplication::translate("Sketcher_Clone","Creates a clone of the geometry taking as reference the last selected point"));
QAction* copy = a[1];
copy->setText(QApplication::translate("Sketcher_CompCopy","Copy"));
copy->setToolTip(QApplication::translate("Sketcher_Copy","Creates a simple copy of the geometry taking as reference the last selected point"));
copy->setStatusTip(QApplication::translate("Sketcher_Copy","Creates a simple copy of the geometry taking as reference the last selected point"));
QAction* move = a[2];
move->setText(QApplication::translate("Sketcher_CompCopy","Move"));
move->setToolTip(QApplication::translate("Sketcher_Move","Moves the geometry taking as reference the last selected point"));
move->setStatusTip(QApplication::translate("Sketcher_Move","Moves the geometry taking as reference the last selected point"));
}
bool CmdSketcherCompCopy::isActive(void)
{
return isSketcherAcceleratorActive( getActiveGuiDocument(), true );
}
// ================================================================================
// TODO: replace XPM cursor with SVG file
/* XPM */
static const char *cursor_createrectangulararray[]={
"32 32 3 1",
"+ c white",
"# c red",
". c None",
"................................",
".......+........................",
".......+........................",
".......+........................",
".......+........................",
".......+........................",
"................................",
".+++++...+++++..................",
".......................###......",
".......+...............###......",
".......+...............###......",
".......+...............###......",
".......+......###......###......",
".......+......###......###......",
"..............###......###......",
"..............###......###......",
".....###......###......###......",
".....###......###......###......",
".....###......###......###......",
".....###......###......###......",
".....###......###......###......",
".....###......###......###......",
".....###......###...............",
".....###......###...............",
".....###......###...............",
".....###......###...............",
".....###........................",
".....###........................",
".....###........................",
".....###........................",
"................................",
"................................"};
class DrawSketchHandlerRectangularArray: public DrawSketchHandler
{
public:
DrawSketchHandlerRectangularArray(string geoidlist, int origingeoid,
Sketcher::PointPos originpos, int nelements, bool clone,
int rows, int cols, bool constraintSeparation,
bool equalVerticalHorizontalSpacing)
: Mode(STATUS_SEEK_First)
, snapMode(SnapMode::Free)
, geoIdList(geoidlist)
, OriginGeoId(origingeoid)
, OriginPos(originpos)
, nElements(nelements)
, Clone(clone)
, Rows(rows)
, Cols(cols)
, ConstraintSeparation(constraintSeparation)
, EqualVerticalHorizontalSpacing(equalVerticalHorizontalSpacing)
, EditCurve(2)
{
}
virtual ~DrawSketchHandlerRectangularArray(){}
/// mode table
enum SelectMode {
STATUS_SEEK_First, /**< enum value ----. */
STATUS_End
};
enum class SnapMode {
Free,
Snap5Degree
};
virtual void activated(ViewProviderSketch *sketchgui)
{
setCursor(QPixmap(cursor_createrectangulararray), 7, 7);
Origin = static_cast<Sketcher::SketchObject *>(sketchgui->getObject())->getPoint(OriginGeoId, OriginPos);
EditCurve[0] = Base::Vector2d(Origin.x, Origin.y);
}
virtual void mouseMove(Base::Vector2d onSketchPos)
{
if (Mode==STATUS_SEEK_First) {
if(QApplication::keyboardModifiers() == Qt::ControlModifier)
snapMode = SnapMode::Snap5Degree;
else
snapMode = SnapMode::Free;
float length = (onSketchPos - EditCurve[0]).Length();
float angle = (onSketchPos - EditCurve[0]).Angle();
Base::Vector2d endpoint = onSketchPos;
if (snapMode == SnapMode::Snap5Degree) {
angle = round(angle / (M_PI/36)) * M_PI/36;
endpoint = EditCurve[0] + length * Base::Vector2d(cos(angle),sin(angle));
}
SbString text;
text.sprintf(" (%.1f, %.1fdeg)", length, angle * 180 / M_PI);
setPositionText(endpoint, text);
EditCurve[1] = endpoint;
drawEdit(EditCurve);
if (seekAutoConstraint(sugConstr1, endpoint, Base::Vector2d(0.0, 0.0), AutoConstraint::VERTEX))
{
renderSuggestConstraintsCursor(sugConstr1);
return;
}
}
applyCursor();
}
virtual bool pressButton(Base::Vector2d)
{
if (Mode == STATUS_SEEK_First) {
drawEdit(EditCurve);
Mode = STATUS_End;
}
return true;
}
virtual bool releaseButton(Base::Vector2d onSketchPos)
{
Q_UNUSED(onSketchPos);
if (Mode == STATUS_End) {
Base::Vector2d vector = EditCurve[1] - EditCurve[0];
unsetCursor();
resetPositionText();
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create copy of geometry"));
try {
Gui::cmdAppObjectArgs(sketchgui->getObject(),
"addRectangularArray(%s, App.Vector(%f, %f, 0), %s, %d, %d, %s, %f)",
geoIdList.c_str(), vector.x, vector.y,
(Clone ? "True" : "False"),
Cols, Rows,
(ConstraintSeparation ? "True" : "False"),
(EqualVerticalHorizontalSpacing ? 1.0 : 0.5));
Gui::Command::commitCommand();
}
catch (const Base::Exception& e) {
Base::Console().Error("%s\n", e.what());
Gui::Command::abortCommand();
}
// add auto constraints for the destination copy
if (sugConstr1.size() > 0) {
createAutoConstraints(sugConstr1, OriginGeoId+nElements, OriginPos);
sugConstr1.clear();
}
tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(sketchgui->getObject()));
EditCurve.clear();
drawEdit(EditCurve);
// no code after this line, Handler is deleted in ViewProvider
sketchgui->purgeHandler();
}
return true;
}
protected:
SelectMode Mode;
SnapMode snapMode;
string geoIdList;
Base::Vector3d Origin;
int OriginGeoId;
Sketcher::PointPos OriginPos;
int nElements;
bool Clone;
int Rows;
int Cols;
bool ConstraintSeparation;
bool EqualVerticalHorizontalSpacing;
std::vector<Base::Vector2d> EditCurve;
std::vector<AutoConstraint> sugConstr1;
};
DEF_STD_CMD_A(CmdSketcherRectangularArray)
CmdSketcherRectangularArray::CmdSketcherRectangularArray()
:Command("Sketcher_RectangularArray")
{
sAppModule = "Sketcher";
sGroup = "Sketcher";
sMenuText = QT_TR_NOOP("Rectangular array");
sToolTipText = QT_TR_NOOP("Creates a rectangular array pattern of the geometry taking as reference the last selected point");
sWhatsThis = "Sketcher_RectangularArray";
sStatusTip = sToolTipText;
sPixmap = "Sketcher_RectangularArray";
sAccel = "Z, A";
eType = ForEdit;
}
void CmdSketcherRectangularArray::activated(int iMsg)
{
Q_UNUSED(iMsg);
// get the selection
std::vector<Gui::SelectionObject> selection;
selection = getSelection().getSelectionEx(0, Sketcher::SketchObject::getClassTypeId());
// only one sketch with its subelements are allowed to be selected
if (selection.size() != 1) {
QMessageBox::warning(Gui::getMainWindow(),
QObject::tr("Wrong selection"),
QObject::tr("Select elements from a single 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 elements from a single sketch."));
return;
}
Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
getSelection().clearSelection();
int LastGeoId = 0;
Sketcher::PointPos LastPointPos = Sketcher::PointPos::none;
const Part::Geometry *LastGeo = 0;
// create python command with list of elements
std::stringstream stream;
int geoids = 0;
for (std::vector<std::string>::const_iterator it=SubNames.begin(); it != SubNames.end(); ++it) {
// only handle non-external edges
if (it->size() > 4 && it->substr(0,4) == "Edge") {
LastGeoId = std::atoi(it->substr(4,4000).c_str()) - 1;
LastPointPos = Sketcher::PointPos::none;
LastGeo = Obj->getGeometry(LastGeoId);
// lines to copy
if (LastGeoId >= 0) {
geoids++;
stream << LastGeoId << ",";
}
}
else if (it->size() > 6 && it->substr(0,6) == "Vertex") {
// only if it is a GeomPoint
int VtId = std::atoi(it->substr(6,4000).c_str()) - 1;
int GeoId;
Sketcher::PointPos PosId;
Obj->getGeoVertexIndex(VtId, GeoId, PosId);
if (Obj->getGeometry(GeoId)->getTypeId() == Part::GeomPoint::getClassTypeId()) {
LastGeoId = GeoId;
LastPointPos = Sketcher::PointPos::start;
// points to copy
if (LastGeoId >= 0) {
geoids++;
stream << LastGeoId << ",";
}
}
}
}
// check if last selected element is a Vertex, not being a GeomPoint
if (SubNames.rbegin()->size() > 6 && SubNames.rbegin()->substr(0,6) == "Vertex") {
int VtId = std::atoi(SubNames.rbegin()->substr(6,4000).c_str()) - 1;
int GeoId;
Sketcher::PointPos PosId;
Obj->getGeoVertexIndex(VtId, GeoId, PosId);
if (Obj->getGeometry(GeoId)->getTypeId() != Part::GeomPoint::getClassTypeId()) {
LastGeoId = GeoId;
LastPointPos = PosId;
}
}
if (geoids < 1) {
QMessageBox::warning(Gui::getMainWindow(),
QObject::tr("Wrong selection"),
QObject::tr("A copy requires at least one selected non-external geometric element"));
return;
}
std::string geoIdList = stream.str();
// remove the last added comma and brackets to make the python list
int index = geoIdList.rfind(',');
geoIdList.resize(index);
geoIdList.insert(0, 1, '[');
geoIdList.append(1, ']');
// if the last element is not a point serving as a reference for the copy process
// then make the start point of the last element the copy reference (if it exists, if not the center point)
if (LastPointPos == Sketcher::PointPos::none) {
if (LastGeo->getTypeId() == Part::GeomCircle::getClassTypeId() ||
LastGeo->getTypeId() == Part::GeomEllipse::getClassTypeId()) {
LastPointPos = Sketcher::PointPos::mid;
}
else {
LastPointPos = Sketcher::PointPos::start;
}
}
// Pop-up asking for values
SketchRectangularArrayDialog slad;
if (slad.exec() == QDialog::Accepted) {
ActivateAcceleratorHandler(getActiveGuiDocument(),
new DrawSketchHandlerRectangularArray(geoIdList, LastGeoId, LastPointPos, geoids, slad.Clone,
slad.Rows, slad.Cols, slad.ConstraintSeparation,
slad.EqualVerticalHorizontalSpacing));
}
}
bool CmdSketcherRectangularArray::isActive(void)
{
return isSketcherAcceleratorActive(getActiveGuiDocument(), true);
}
// ================================================================================
DEF_STD_CMD_A(CmdSketcherDeleteAllGeometry)
CmdSketcherDeleteAllGeometry::CmdSketcherDeleteAllGeometry()
:Command("Sketcher_DeleteAllGeometry")
{
sAppModule = "Sketcher";
sGroup = "Sketcher";
sMenuText = QT_TR_NOOP("Delete all geometry");
sToolTipText = QT_TR_NOOP("Delete all geometry and constraints in the current sketch, "
"with the exception of external geometry");
sWhatsThis = "Sketcher_DeleteAllGeometry";
sStatusTip = sToolTipText;
sPixmap = "Sketcher_DeleteGeometry";
sAccel = "";
eType = ForEdit;
}
void CmdSketcherDeleteAllGeometry::activated(int iMsg)
{
Q_UNUSED(iMsg);
int ret = QMessageBox::question(Gui::getMainWindow(), QObject::tr("Delete All Geometry"),
QObject::tr("Are you really sure you want to delete all geometry and constraints?"),
QMessageBox::Yes, QMessageBox::Cancel);
// use an equality constraint
if (ret == QMessageBox::Yes) {
getSelection().clearSelection();
Gui::Document * doc= getActiveGuiDocument();
ReleaseHandler(doc);
SketcherGui::ViewProviderSketch* vp = static_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit());
Sketcher::SketchObject* Obj= vp->getSketchObject();
try {
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Delete all geometry"));
Gui::cmdAppObjectArgs(Obj, "deleteAllGeometry()");
Gui::Command::commitCommand();
}
catch (const Base::Exception& e) {
Base::Console().Error("Failed to delete all geometry: %s\n", e.what());
Gui::Command::abortCommand();
}
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
bool autoRecompute = hGrp->GetBool("AutoRecompute", false);
if (autoRecompute)
Gui::Command::updateActive();
else
Obj->solve();
}
else if (ret == QMessageBox::Cancel) {
// do nothing
return;
}
}
bool CmdSketcherDeleteAllGeometry::isActive(void)
{
return isSketcherAcceleratorActive(getActiveGuiDocument(), false);
}
// ================================================================================
DEF_STD_CMD_A(CmdSketcherDeleteAllConstraints)
CmdSketcherDeleteAllConstraints::CmdSketcherDeleteAllConstraints()
:Command("Sketcher_DeleteAllConstraints")
{
sAppModule = "Sketcher";
sGroup = "Sketcher";
sMenuText = QT_TR_NOOP("Delete all constraints");
sToolTipText = QT_TR_NOOP("Delete all constraints in the sketch");
sWhatsThis = "Sketcher_DeleteAllConstraints";
sStatusTip = sToolTipText;
sPixmap = "Sketcher_DeleteConstraints";
sAccel = "";
eType = ForEdit;
}
void CmdSketcherDeleteAllConstraints::activated(int iMsg)
{
Q_UNUSED(iMsg);
int ret = QMessageBox::question(Gui::getMainWindow(), QObject::tr("Delete All Constraints"),
QObject::tr("Are you really sure you want to delete all the constraints?"),
QMessageBox::Yes, QMessageBox::Cancel);
if (ret == QMessageBox::Yes) {
getSelection().clearSelection();
Gui::Document * doc= getActiveGuiDocument();
ReleaseHandler(doc);
SketcherGui::ViewProviderSketch* vp = static_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit());
Sketcher::SketchObject* Obj= vp->getSketchObject();
try {
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Delete All Constraints"));
Gui::cmdAppObjectArgs(Obj, "deleteAllConstraints()");
Gui::Command::commitCommand();
}
catch (const Base::Exception& e) {
Base::Console().Error("Failed to delete All Constraints: %s\n", e.what());
Gui::Command::abortCommand();
}
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher");
bool autoRecompute = hGrp->GetBool("AutoRecompute",false);
if (autoRecompute)
Gui::Command::updateActive();
else
Obj->solve();
}
else if (ret == QMessageBox::Cancel) {
// do nothing
return;
}
}
bool CmdSketcherDeleteAllConstraints::isActive(void)
{
return isSketcherAcceleratorActive(getActiveGuiDocument(), false);
}
// ================================================================================
DEF_STD_CMD_A(CmdSketcherRemoveAxesAlignment)
CmdSketcherRemoveAxesAlignment::CmdSketcherRemoveAxesAlignment()
:Command("Sketcher_RemoveAxesAlignment")
{
sAppModule = "Sketcher";
sGroup = "Sketcher";
sMenuText = QT_TR_NOOP("Remove axes alignment");
sToolTipText = QT_TR_NOOP("Modifies constraints to remove axes alignment while trying to preserve the constraint relationship of the selection");
sWhatsThis = "Sketcher_RemoveAxesAlignment";
sStatusTip = sToolTipText;
sPixmap = "Sketcher_RemoveAxesAlignment";
sAccel = "Z, R";
eType = ForEdit;
}
void CmdSketcherRemoveAxesAlignment::activated(int iMsg)
{
Q_UNUSED(iMsg);
// get the selection
std::vector<Gui::SelectionObject> selection;
selection = getSelection().getSelectionEx(0, Sketcher::SketchObject::getClassTypeId());
// only one sketch with its subelements are allowed to be selected
if (selection.size() != 1) {
QMessageBox::warning(Gui::getMainWindow(),
QObject::tr("Wrong selection"),
QObject::tr("Select elements from a single 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 elements from a single sketch."));
return;
}
Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
getSelection().clearSelection();
int LastGeoId = 0;
// create python command with list of elements
std::stringstream stream;
int geoids = 0;
for (std::vector<std::string>::const_iterator it=SubNames.begin(); it != SubNames.end(); ++it) {
// only handle non-external edges
if (it->size() > 4 && it->substr(0,4) == "Edge") {
LastGeoId = std::atoi(it->substr(4,4000).c_str()) - 1;
// lines to copy
if (LastGeoId >= 0) {
geoids++;
stream << LastGeoId << ",";
}
}
else if (it->size() > 6 && it->substr(0,6) == "Vertex") {
// only if it is a GeomPoint
int VtId = std::atoi(it->substr(6,4000).c_str()) - 1;
int GeoId;
Sketcher::PointPos PosId;
Obj->getGeoVertexIndex(VtId, GeoId, PosId);
if (Obj->getGeometry(GeoId)->getTypeId() == Part::GeomPoint::getClassTypeId()) {
LastGeoId = GeoId;
// points to copy
if (LastGeoId >= 0) {
geoids++;
stream << LastGeoId << ",";
}
}
}
}
if (geoids < 1) {
QMessageBox::warning(Gui::getMainWindow(),
QObject::tr("Wrong selection"),
QObject::tr("Removal of axes alignment requires at least one selected non-external geometric element"));
return;
}
std::string geoIdList = stream.str();
// remove the last added comma and brackets to make the python list
int index = geoIdList.rfind(',');
geoIdList.resize(index);
geoIdList.insert(0, 1, '[');
geoIdList.append(1, ']');
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Remove Axes Alignment"));
try {
Gui::cmdAppObjectArgs( Obj,
"removeAxesAlignment(%s)",
geoIdList.c_str());
Gui::Command::commitCommand();
}
catch (const Base::Exception& e) {
Base::Console().Error("%s\n", e.what());
Gui::Command::abortCommand();
}
tryAutoRecomputeIfNotSolve(static_cast<Sketcher::SketchObject *>(Obj));
}
bool CmdSketcherRemoveAxesAlignment::isActive(void)
{
return isSketcherAcceleratorActive(getActiveGuiDocument(), true);
}
void CreateSketcherCommandsConstraintAccel(void)
{
Gui::CommandManager &rcCmdMgr = Gui::Application::Instance->commandManager();
rcCmdMgr.addCommand(new CmdSketcherCloseShape());
rcCmdMgr.addCommand(new CmdSketcherConnect());
rcCmdMgr.addCommand(new CmdSketcherSelectConstraints());
rcCmdMgr.addCommand(new CmdSketcherSelectOrigin());
rcCmdMgr.addCommand(new CmdSketcherSelectVerticalAxis());
rcCmdMgr.addCommand(new CmdSketcherSelectHorizontalAxis());
rcCmdMgr.addCommand(new CmdSketcherSelectRedundantConstraints());
rcCmdMgr.addCommand(new CmdSketcherSelectConflictingConstraints());
rcCmdMgr.addCommand(new CmdSketcherSelectMalformedConstraints());
rcCmdMgr.addCommand(new CmdSketcherSelectPartiallyRedundantConstraints());
rcCmdMgr.addCommand(new CmdSketcherSelectElementsAssociatedWithConstraints());
rcCmdMgr.addCommand(new CmdSketcherSelectElementsWithDoFs());
rcCmdMgr.addCommand(new CmdSketcherRestoreInternalAlignmentGeometry());
rcCmdMgr.addCommand(new CmdSketcherSymmetry());
rcCmdMgr.addCommand(new CmdSketcherCopy());
rcCmdMgr.addCommand(new CmdSketcherClone());
rcCmdMgr.addCommand(new CmdSketcherMove());
rcCmdMgr.addCommand(new CmdSketcherCompCopy());
rcCmdMgr.addCommand(new CmdSketcherRectangularArray());
rcCmdMgr.addCommand(new CmdSketcherDeleteAllGeometry());
rcCmdMgr.addCommand(new CmdSketcherDeleteAllConstraints());
rcCmdMgr.addCommand(new CmdSketcherRemoveAxesAlignment());
}