diff --git a/src/Mod/TechDraw/App/DrawViewPartPy.xml b/src/Mod/TechDraw/App/DrawViewPartPy.xml
index e0fd128a37..7b916213f8 100644
--- a/src/Mod/TechDraw/App/DrawViewPartPy.xml
+++ b/src/Mod/TechDraw/App/DrawViewPartPy.xml
@@ -73,6 +73,16 @@
tag = makeCosmeticCircleArc(center, radius, start, end) - add a CosmeticEdge at center with radius radius(View coordinates) from start angle to end angle. Returns tag of new CosmeticEdge.
+
+
+ tag = makeCosmeticCircle3d(center, radius) - add a CosmeticEdge at center (3d point) with radius. Returns tag of new CosmeticEdge.
+
+
+
+
+ tag = makeCosmeticCircleArc3d(center, radius, start, end) - add a CosmeticEdge at center (3d point) with radius from start angle to end angle. Returns tag of new CosmeticEdge.
+
+
ce = getCosmeticEdge(id) - returns CosmeticEdge with unique id.
diff --git a/src/Mod/TechDraw/App/DrawViewPartPyImp.cpp b/src/Mod/TechDraw/App/DrawViewPartPyImp.cpp
index c1cefa3b7f..90c16212da 100644
--- a/src/Mod/TechDraw/App/DrawViewPartPyImp.cpp
+++ b/src/Mod/TechDraw/App/DrawViewPartPyImp.cpp
@@ -388,9 +388,9 @@ PyObject* DrawViewPartPy::makeCosmeticCircle(PyObject *args)
}
DrawViewPart* dvp = getDrawViewPartPtr();
- Base::Vector3d pnt1 = DrawUtil::invertY(static_cast(pPnt1)->value());
+ Base::Vector3d pnt1 = static_cast(pPnt1)->value();
TechDraw::BaseGeomPtr bg = std::make_shared (pnt1, radius);
- std::string newTag = dvp->addCosmeticEdge(bg);
+ std::string newTag = dvp->addCosmeticEdge(bg->inverted());
TechDraw::CosmeticEdge* ce = dvp->getCosmeticEdge(newTag);
if (ce) {
ce->permaRadius = radius;
@@ -428,9 +428,97 @@ PyObject* DrawViewPartPy::makeCosmeticCircleArc(PyObject *args)
//from here on is almost duplicate of makeCosmeticCircle
DrawViewPart* dvp = getDrawViewPartPtr();
- Base::Vector3d pnt1 = DrawUtil::invertY(static_cast(pPnt1)->value());
+ Base::Vector3d pnt1 = static_cast(pPnt1)->value();
TechDraw::BaseGeomPtr bg = std::make_shared (pnt1, radius, angle1, angle2);
- std::string newTag = dvp->addCosmeticEdge(bg);
+ std::string newTag = dvp->addCosmeticEdge(bg->inverted());
+ TechDraw::CosmeticEdge* ce = dvp->getCosmeticEdge(newTag);
+ if (ce) {
+ ce->permaRadius = radius;
+ ce->m_format.m_style = style;
+ ce->m_format.m_weight = weight;
+ if (!pColor)
+ ce->m_format.m_color = defCol;
+ else
+ ce->m_format.m_color = DrawUtil::pyTupleToColor(pColor);
+ }
+ else {
+ PyErr_SetString(PyExc_RuntimeError, "DVPPI:makeCosmeticCircleArc - arc creation failed");
+ return nullptr;
+ }
+
+ //int link =
+ dvp->add1CEToGE(newTag);
+ dvp->requestPaint();
+
+ return PyUnicode_FromString(newTag.c_str()); //return tag for new CE
+}
+
+PyObject* DrawViewPartPy::makeCosmeticCircle3d(PyObject *args)
+{
+ PyObject* pPnt1 = nullptr;
+ double radius = 5.0;
+ int style = LineFormat::getDefEdgeStyle();
+ double weight = LineFormat::getDefEdgeWidth();
+ App::Color defCol = LineFormat::getDefEdgeColor();
+ PyObject* pColor = nullptr;
+
+ if (!PyArg_ParseTuple(args, "O!d|idO!", &(Base::VectorPy::Type), &pPnt1,
+ &radius,
+ &style, &weight,
+ &PyTuple_Type, &pColor)) {
+ return nullptr;
+ }
+
+ DrawViewPart* dvp = getDrawViewPartPtr();
+ Base::Vector3d pnt1 = static_cast(pPnt1)->value();
+ // center, project and invert the 3d point
+ Base::Vector3d centroid = dvp->getOriginalCentroid();
+ pnt1 = DrawUtil::invertY(dvp->projectPoint(pnt1 - centroid));
+ TechDraw::BaseGeomPtr bg = std::make_shared (pnt1, radius);
+ std::string newTag = dvp->addCosmeticEdge(bg->inverted());
+ TechDraw::CosmeticEdge* ce = dvp->getCosmeticEdge(newTag);
+ if (ce) {
+ ce->permaRadius = radius;
+ ce->m_format.m_style = style;
+ ce->m_format.m_weight = weight;
+ ce->m_format.m_color = pColor ? DrawUtil::pyTupleToColor(pColor) : defCol;
+ }
+ else {
+ PyErr_SetString(PyExc_RuntimeError, "DVPPI:makeCosmeticCircle - circle creation failed");
+ return nullptr;
+ }
+ //int link =
+ dvp->add1CEToGE(newTag);
+ dvp->requestPaint();
+
+ return PyUnicode_FromString(newTag.c_str()); //return tag for new CE
+}
+
+PyObject* DrawViewPartPy::makeCosmeticCircleArc3d(PyObject *args)
+{
+ PyObject* pPnt1 = nullptr;
+ double radius = 5.0;
+ double angle1 = 0.0;
+ double angle2 = 360.0;
+ int style = LineFormat::getDefEdgeStyle();
+ double weight = LineFormat::getDefEdgeWidth();
+ App::Color defCol = LineFormat::getDefEdgeColor();
+ PyObject* pColor = nullptr;
+
+ if (!PyArg_ParseTuple(args, "O!ddd|idO!", &(Base::VectorPy::Type), &pPnt1,
+ &radius, &angle1, &angle2,
+ &style, &weight, &PyTuple_Type, &pColor)) {
+ return nullptr;
+ }
+
+ //from here on is almost duplicate of makeCosmeticCircle
+ DrawViewPart* dvp = getDrawViewPartPtr();
+ Base::Vector3d pnt1 = static_cast(pPnt1)->value();
+ // center, project and invert the 3d point
+ Base::Vector3d centroid = dvp->getOriginalCentroid();
+ pnt1 = DrawUtil::invertY(dvp->projectPoint(pnt1 - centroid));
+ TechDraw::BaseGeomPtr bg = std::make_shared (pnt1, radius, angle1, angle2);
+ std::string newTag = dvp->addCosmeticEdge(bg->inverted());
TechDraw::CosmeticEdge* ce = dvp->getCosmeticEdge(newTag);
if (ce) {
ce->permaRadius = radius;
diff --git a/src/Mod/TechDraw/App/Geometry.cpp b/src/Mod/TechDraw/App/Geometry.cpp
index b0c0d300ae..2a7d2e8138 100644
--- a/src/Mod/TechDraw/App/Geometry.cpp
+++ b/src/Mod/TechDraw/App/Geometry.cpp
@@ -423,7 +423,7 @@ bool BaseGeom::closed()
// return a BaseGeom similar to this, but inverted with respect to Y axis
BaseGeomPtr BaseGeom::inverted()
{
- Base::Console().Message("BG::inverted()\n");
+// Base::Console().Message("BG::inverted()\n");
TopoDS_Shape invertedShape = ShapeUtils::invertGeometry(occEdge);
TopoDS_Edge invertedEdge = TopoDS::Edge(invertedShape);
return baseFactory(invertedEdge);
diff --git a/src/Mod/TechDraw/App/Geometry.h b/src/Mod/TechDraw/App/Geometry.h
index ff13526280..504f722d7e 100644
--- a/src/Mod/TechDraw/App/Geometry.h
+++ b/src/Mod/TechDraw/App/Geometry.h
@@ -147,6 +147,11 @@ class TechDrawExport BaseGeom : public std::enable_shared_from_this
void setCosmeticTag(std::string t) { cosmeticTag = t; }
Part::TopoShape asTopoShape(double scale);
+ virtual double getStartAngle() { return 0.0; }
+ virtual double getEndAngle() { return 0.0; }
+ virtual bool clockwiseAngle() { return false; }
+ virtual void clockwiseAngle(bool direction) { (void) direction; }
+
protected:
void createNewTag();
@@ -217,6 +222,10 @@ class TechDrawExport AOE: public Ellipse
~AOE() override = default;
public:
+ double getStartAngle() override { return startAngle; }
+ double getEndAngle() override { return endAngle; }
+ bool clockwiseAngle() override { return cw; }
+ void clockwiseAngle(bool direction) override { cw = direction; }
Base::Vector3d startPnt; //TODO: The points are used for drawing, the angles for bounding box calcs - seems redundant
Base::Vector3d endPnt;
Base::Vector3d midPnt;
@@ -241,6 +250,11 @@ class TechDrawExport AOC: public Circle
~AOC() override = default;
public:
+ double getStartAngle() override { return startAngle; }
+ double getEndAngle() override { return endAngle; }
+ bool clockwiseAngle() override { return cw; }
+ void clockwiseAngle(bool direction) override { cw = direction; }
+
std::string toString() const override;
void Save(Base::Writer& w) const override;
void Restore(Base::XMLReader& r) override;
@@ -284,6 +298,9 @@ class TechDrawExport BSpline: public BaseGeom
~BSpline() override = default;
public:
+ double getStartAngle() override { return startAngle; }
+ double getEndAngle() override { return endAngle; }
+
Base::Vector3d startPnt;
Base::Vector3d endPnt;
Base::Vector3d midPnt;
diff --git a/src/Mod/TechDraw/Gui/CMakeLists.txt b/src/Mod/TechDraw/Gui/CMakeLists.txt
index fb93a034ba..8329fa4690 100644
--- a/src/Mod/TechDraw/Gui/CMakeLists.txt
+++ b/src/Mod/TechDraw/Gui/CMakeLists.txt
@@ -87,6 +87,7 @@ set(TechDrawGui_UIC_SRCS
TaskProjection.ui
TaskComplexSection.ui
TaskDimRepair.ui
+ TaskCosmeticCircle.ui
)
@@ -228,6 +229,9 @@ SET(TechDrawGui_SRCS
TaskDimRepair.cpp
TaskDimRepair.h
TaskDimRepair.ui
+ TaskCosmeticCircle.cpp
+ TaskCosmeticCircle.h
+ TaskCosmeticCircle.ui
Widgets/CompassDialWidget.cpp
Widgets/CompassDialWidget.h
Widgets/CompassWidget.cpp
diff --git a/src/Mod/TechDraw/Gui/CommandAnnotate.cpp b/src/Mod/TechDraw/Gui/CommandAnnotate.cpp
index b3ed6e0ea3..ef3b6cdcba 100644
--- a/src/Mod/TechDraw/Gui/CommandAnnotate.cpp
+++ b/src/Mod/TechDraw/Gui/CommandAnnotate.cpp
@@ -57,6 +57,7 @@
#include "TaskRichAnno.h"
#include "TaskSurfaceFinishSymbols.h"
#include "TaskWeldingSymbol.h"
+#include "TaskCosmeticCircle.h"
#include "ViewProviderViewPart.h"
@@ -73,6 +74,7 @@ void execCenterLine(Gui::Command* cmd);
void exec2LineCenterLine(Gui::Command* cmd);
void exec2PointCenterLine(Gui::Command* cmd);
void execLine2Points(Gui::Command* cmd);
+void execCosmeticCircle(Gui::Command* cmd);
std::vector getSelectedSubElements(Gui::Command* cmd,
TechDraw::DrawViewPart* &dvp,
std::string subType = "Edge");
@@ -1110,6 +1112,153 @@ void execLine2Points(Gui::Command* cmd)
is3d));
}
+//===========================================================================
+// TechDraw_CosmeticCircle
+//===========================================================================
+
+DEF_STD_CMD_A(CmdTechDrawCosmeticCircle)
+
+CmdTechDrawCosmeticCircle::CmdTechDrawCosmeticCircle()
+ : Command("TechDraw_CosmeticCircle")
+{
+ sAppModule = "TechDraw";
+ sGroup = QT_TR_NOOP("TechDraw");
+ sMenuText = QT_TR_NOOP("Add Cosmetic Circle");
+ sToolTipText = sMenuText;
+ sWhatsThis = "TechDraw_CosmeticCircle";
+ sStatusTip = sToolTipText;
+ sPixmap = "actions/TechDraw_CosmeticCircle";
+}
+
+void CmdTechDrawCosmeticCircle::activated(int iMsg)
+{
+ Q_UNUSED(iMsg);
+
+ Gui::TaskView::TaskDialog *dlg = Gui::Control().activeDialog();
+ if (dlg) {
+ QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Task In Progress"),
+ QObject::tr("Close active task dialog and try again."));
+ return;
+ }
+
+ execCosmeticCircle(this);
+
+ updateActive();
+ Gui::Selection().clearSelection();
+}
+
+bool CmdTechDrawCosmeticCircle::isActive()
+{
+ bool havePage = DrawGuiUtil::needPage(this);
+ bool haveView = DrawGuiUtil::needView(this, true);
+ return (havePage && haveView);
+}
+
+void execCosmeticCircle(Gui::Command* cmd)
+{
+ TechDraw::DrawPage* page = DrawGuiUtil::findPage(cmd);
+ if (!page) {
+ return;
+ }
+
+ std::vector selection = cmd->getSelection().getSelectionEx();
+ TechDraw::DrawViewPart* baseFeat = nullptr;
+ std::vector subNames2D;
+ std::vector< std::pair > objs3D;
+ if (selection.empty()) {
+ QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong Selection"),
+ QObject::tr("Selection is empty."));
+ return;
+ }
+
+ for (auto& so: selection) {
+ if (so.getObject()->isDerivedFrom(TechDraw::DrawViewPart::getClassTypeId())) {
+ baseFeat = static_cast (so.getObject());
+ subNames2D = so.getSubNames();
+ } else if (so.getObject()->isDerivedFrom(Part::Feature::getClassTypeId())) {
+ std::vector subNames3D = so.getSubNames();
+ for (auto& sub3D: subNames3D) {
+ std::pair temp;
+ temp.first = static_cast(so.getObject());
+ temp.second = sub3D;
+ objs3D.push_back(temp);
+ }
+ } else {
+ //garbage
+ }
+ }
+
+ if (!baseFeat) {
+ QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong Selection"),
+ QObject::tr("You must select a base View for the circle."));
+ return;
+ }
+
+ std::vector edgeNames;
+ std::vector vertexNames;
+ for (auto& s: subNames2D) {
+ std::string geomType = DrawUtil::getGeomTypeFromName(s);
+ if (geomType == "Vertex") {
+ vertexNames.push_back(s);
+ } else if (geomType == "Edge") {
+ edgeNames.push_back(s);
+ }
+ }
+
+ //check if editing existing edge
+ if (!edgeNames.empty() && (edgeNames.size() == 1)) {
+ TechDraw::CosmeticEdge* ce = baseFeat->getCosmeticEdgeBySelection(edgeNames.front());
+ if (!ce) {
+ QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong Selection"),
+ QObject::tr("Selection is not a Cosmetic edge."));
+ return;
+ }
+ Gui::Control().showDialog(new TaskDlgCosmeticCircle(baseFeat,
+ edgeNames.front()));
+ return;
+ }
+
+ std::vector points;
+ std::vector is3d;
+ //get the 2D points
+ if (!vertexNames.empty()) {
+ for (auto& v2d: vertexNames) {
+ int idx = DrawUtil::getIndexFromName(v2d);
+ TechDraw::VertexPtr v = baseFeat->getProjVertexByIndex(idx);
+ if (v) {
+ points.push_back(v->point());
+ is3d.push_back(false);
+ }
+ }
+ }
+ //get the 3D points
+ if (!objs3D.empty()) {
+ for (auto& o3D: objs3D) {
+ int idx = DrawUtil::getIndexFromName(o3D.second);
+ Part::TopoShape s = o3D.first->Shape.getShape();
+ TopoDS_Vertex v = TopoDS::Vertex(s.getSubShape(TopAbs_VERTEX, idx));
+ Base::Vector3d p = DrawUtil::vertex2Vector(v);
+ points.push_back(p);
+ is3d.push_back(true);
+ }
+ }
+
+ if (points.empty()) {
+ QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong Selection"),
+ QObject::tr("Please select a center for the circle."));
+ return;
+ }
+
+ bool centerIs3d = false;
+ if (!is3d.empty()) {
+ centerIs3d = is3d.front();
+ }
+
+ Gui::Control().showDialog(new TaskDlgCosmeticCircle(baseFeat,
+ points.front(),
+ centerIs3d));
+}
+
//===========================================================================
// TechDraw_CosmeticEraser
//===========================================================================
@@ -1500,6 +1649,7 @@ void CreateTechDrawCommandsAnnotate()
rcCmdMgr.addCommand(new CmdTechDraw2LineCenterLine());
rcCmdMgr.addCommand(new CmdTechDraw2PointCenterLine());
rcCmdMgr.addCommand(new CmdTechDraw2PointCosmeticLine());
+ rcCmdMgr.addCommand(new CmdTechDrawCosmeticCircle());
rcCmdMgr.addCommand(new CmdTechDrawAnnotation());
rcCmdMgr.addCommand(new CmdTechDrawCosmeticEraser());
rcCmdMgr.addCommand(new CmdTechDrawDecorateLine());
diff --git a/src/Mod/TechDraw/Gui/Resources/TechDraw.qrc b/src/Mod/TechDraw/Gui/Resources/TechDraw.qrc
index 84bc48b256..e19c7dad52 100644
--- a/src/Mod/TechDraw/Gui/Resources/TechDraw.qrc
+++ b/src/Mod/TechDraw/Gui/Resources/TechDraw.qrc
@@ -54,6 +54,7 @@
icons/actions/TechDraw_WeldSymbol.svg
icons/actions/TechDraw_SurfaceFinishSymbols.svg
icons/actions/TechDraw_ComplexSection.svg
+ icons/actions/TechDraw_CosmeticCircle.svg
icons/arrow-ccw.svg
icons/arrow-cw.svg
icons/arrow-down.svg
diff --git a/src/Mod/TechDraw/Gui/Resources/icons/actions/TechDraw_CosmeticCircle.svg b/src/Mod/TechDraw/Gui/Resources/icons/actions/TechDraw_CosmeticCircle.svg
new file mode 100644
index 0000000000..22efc682e8
--- /dev/null
+++ b/src/Mod/TechDraw/Gui/Resources/icons/actions/TechDraw_CosmeticCircle.svg
@@ -0,0 +1,179 @@
+
+
diff --git a/src/Mod/TechDraw/Gui/TaskCosmeticCircle.cpp b/src/Mod/TechDraw/Gui/TaskCosmeticCircle.cpp
new file mode 100644
index 0000000000..07df28f69d
--- /dev/null
+++ b/src/Mod/TechDraw/Gui/TaskCosmeticCircle.cpp
@@ -0,0 +1,304 @@
+/***************************************************************************
+ * Copyright (c) 2023 WandererFan *
+ * *
+ * 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
+# include
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "ui_TaskCosmeticCircle.h"
+#include "TaskCosmeticCircle.h"
+
+
+using namespace Gui;
+using namespace TechDraw;
+using namespace TechDrawGui;
+using DU = DrawUtil;
+
+//ctor for edit
+TaskCosmeticCircle::TaskCosmeticCircle(TechDraw::DrawViewPart* partFeat,
+ std::string circleName) :
+ ui(new Ui_TaskCosmeticCircle),
+ m_partFeat(partFeat),
+ m_circleName(circleName),
+ m_ce(nullptr),
+ m_saveCE(nullptr),
+ m_createMode(false)
+{
+ //existence of partFeat is checked in calling command
+
+ m_ce = m_partFeat->getCosmeticEdgeBySelection(m_circleName);
+ if (!m_ce) {
+ Base::Console().Error("TaskCosmeticCircle - bad parameters. Can not proceed.\n");
+ return;
+ }
+
+ ui->setupUi(this);
+
+ setUiEdit();
+}
+
+//ctor for creation
+TaskCosmeticCircle::TaskCosmeticCircle(TechDraw::DrawViewPart* partFeat,
+ Base::Vector3d center, bool is3d) :
+ ui(new Ui_TaskCosmeticCircle),
+ m_partFeat(partFeat),
+ m_ce(nullptr),
+ m_saveCE(nullptr),
+ m_center(center),
+ m_createMode(true),
+ m_is3d(is3d)
+{
+ //existence of partFeat is checked in calling command
+
+ ui->setupUi(this);
+
+ setUiPrimary();
+}
+
+TaskCosmeticCircle::~TaskCosmeticCircle()
+{
+ if (m_saveCE) {
+ delete m_saveCE;
+ }
+}
+
+void TaskCosmeticCircle::updateTask()
+{
+// blockUpdate = true;
+
+// blockUpdate = false;
+}
+
+void TaskCosmeticCircle::changeEvent(QEvent *e)
+{
+ if (e->type() == QEvent::LanguageChange) {
+ ui->retranslateUi(this);
+ }
+}
+
+void TaskCosmeticCircle::setUiPrimary()
+{
+ setWindowTitle(QObject::tr("Create Cosmetic Line"));
+// Base::Console().Message("TCC::setUiPrimary() - m_center: %s is3d: %d\n",
+// DU::formatVector(m_center).c_str(), m_is3d);
+ double rotDeg = m_partFeat->Rotation.getValue();
+ double rotRad = rotDeg * M_PI / 180.0;
+ Base::Vector3d centroid = m_partFeat->getCurrentCentroid();
+ Base::Vector3d p1;
+ if (m_is3d) {
+ // center, project and invert the 3d point
+ p1 = DrawUtil::invertY(m_partFeat->projectPoint(m_center - centroid));
+ ui->rb2d1->setChecked(false);
+ ui->rb3d1->setChecked(true);
+ } else {
+ // invert, unscale and unrotate the selected 2d point
+ // shift by centroid?
+ p1 = DU::invertY(m_center) / m_partFeat->getScale();
+ if (rotDeg != 0.0) {
+ // we always rotate around the origin.
+ p1.RotateZ(-rotRad);
+ }
+ ui->rb2d1->setChecked(true);
+ ui->rb3d1->setChecked(false);
+ }
+
+ ui->qsbCenterX->setUnit(Base::Unit::Length);
+ ui->qsbCenterX->setValue(p1.x);
+ ui->qsbCenterY->setUnit(Base::Unit::Length);
+ ui->qsbCenterY->setValue(p1.y);
+ ui->qsbCenterY->setUnit(Base::Unit::Length);
+ ui->qsbCenterZ->setValue(p1.z);
+}
+
+void TaskCosmeticCircle::setUiEdit()
+{
+ setWindowTitle(QObject::tr("Edit Cosmetic Line"));
+
+ ui->rb2d1->setChecked(true);
+ ui->rb3d1->setChecked(false);
+
+ Base::Vector3d p1 = DrawUtil::invertY(m_ce->permaStart);
+ ui->qsbCenterX->setValue(p1.x);
+ ui->qsbCenterY->setValue(p1.y);
+ ui->qsbCenterZ->setValue(p1.z);
+
+ ui->qsbRadius->setValue(m_ce->permaRadius);
+
+ double angleDeg = m_ce->m_geometry->getStartAngle() * 180.0 / M_PI;
+ ui->qsbStartAngle->setValue(angleDeg);
+ angleDeg = m_ce->m_geometry->getEndAngle() * 180.0 / M_PI;
+ ui->qsbEndAngle->setValue(angleDeg);
+}
+
+//******************************************************************************
+void TaskCosmeticCircle::createCosmeticCircle(void)
+{
+// Base::Console().Message("TCL::createCosmeticCircle()\n");
+ Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create Cosmetic Line"));
+
+ double x = ui->qsbCenterX->value().getValue();
+ double y = ui->qsbCenterY->value().getValue();
+ double z = ui->qsbCenterZ->value().getValue();
+ Base::Vector3d p0(x, y, z);
+
+ TechDraw::BaseGeomPtr bg;
+ if (ui->qsbStartAngle->value().getValue() == 0.0 &&
+ ui->qsbEndAngle->value().getValue() == 0.0) {
+ bg = std::make_shared (p0, ui->qsbRadius->value().getValue());
+ } else {
+ bg = std::make_shared(p0, ui->qsbRadius->value().getValue(),
+ ui->qsbStartAngle->value().getValue(),
+ ui->qsbEndAngle->value().getValue());
+ }
+
+ // note cEdges are inverted when added to the dvp geometry, so we need to
+ // invert them here
+ m_tag = m_partFeat->addCosmeticEdge(bg->inverted());
+ m_ce = m_partFeat->getCosmeticEdge(m_tag);
+
+ Gui::Command::commitCommand();
+}
+
+void TaskCosmeticCircle::updateCosmeticCircle(void)
+{
+// Base::Console().Message("TCL::updateCosmeticCircle()\n");
+ double x = ui->qsbCenterX->value().getValue();
+ double y = ui->qsbCenterY->value().getValue();
+ double z = ui->qsbCenterZ->value().getValue();
+ Base::Vector3d p0(x, y, z);
+
+ //replace the geometry
+ m_ce->permaRadius = ui->qsbRadius->value().getValue();
+
+ TechDraw::BaseGeomPtr bg;
+ if (ui->qsbStartAngle->value().getValue() == 0.0 &&
+ ui->qsbEndAngle->value().getValue() == 0.0) {
+ bg = std::make_shared (p0, m_ce->permaRadius);
+ } else {
+ bg = std::make_shared(p0, ui->qsbRadius->value().getValue(),
+ ui->qsbStartAngle->value().getValue(),
+ ui->qsbEndAngle->value().getValue());
+ }
+ m_ce->m_geometry = bg->inverted();
+ p0 = DU::invertY(p0);
+ m_ce->permaStart = p0;
+ m_ce->permaEnd = p0;
+}
+
+//******************************************************************************
+
+bool TaskCosmeticCircle::accept()
+{
+ if (m_createMode) {
+ createCosmeticCircle();
+ m_partFeat->add1CEToGE(m_tag);
+ m_partFeat->refreshCEGeoms();
+ m_partFeat->requestPaint();
+ } else {
+ //update mode
+ Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Update CosmeticCircle"));
+ updateCosmeticCircle();
+ m_partFeat->refreshCEGeoms();
+ m_partFeat->requestPaint();
+ Gui::Command::updateActive();
+ Gui::Command::commitCommand();
+ }
+
+ Gui::Command::doCommand(Gui::Command::Gui, "Gui.ActiveDocument.resetEdit()");
+
+ return true;
+}
+
+bool TaskCosmeticCircle::reject()
+{
+ //there's nothing to do.
+ Gui::Command::doCommand(Gui::Command::Gui, "Gui.ActiveDocument.resetEdit()");
+ return false;
+}
+////////////////////////////////////////////////////////////////////////////////
+TaskDlgCosmeticCircle::TaskDlgCosmeticCircle(TechDraw::DrawViewPart* partFeat,
+ Base::Vector3d point,
+ bool is3d)
+ : TaskDialog()
+{
+ widget = new TaskCosmeticCircle(partFeat, point, is3d);
+ taskbox = new Gui::TaskView::TaskBox(Gui::BitmapFactory().pixmap("actions/TechDraw_CosmeticCircle"),
+ widget->windowTitle(), true, nullptr);
+ taskbox->groupLayout()->addWidget(widget);
+ Content.push_back(taskbox);
+}
+
+TaskDlgCosmeticCircle::TaskDlgCosmeticCircle(TechDraw::DrawViewPart* partFeat,
+ std::string circleName)
+ : TaskDialog()
+{
+ widget = new TaskCosmeticCircle(partFeat, circleName);
+ taskbox = new Gui::TaskView::TaskBox(Gui::BitmapFactory().pixmap("actions/TechDraw_CosmeticCircle"),
+ widget->windowTitle(), true, nullptr);
+ taskbox->groupLayout()->addWidget(widget);
+ Content.push_back(taskbox);
+}
+
+TaskDlgCosmeticCircle::~TaskDlgCosmeticCircle()
+{
+}
+
+void TaskDlgCosmeticCircle::update()
+{
+// widget->updateTask();
+}
+
+//==== calls from the TaskView ===============================================================
+void TaskDlgCosmeticCircle::open()
+{
+}
+
+void TaskDlgCosmeticCircle::clicked(int)
+{
+}
+
+bool TaskDlgCosmeticCircle::accept()
+{
+ widget->accept();
+ return true;
+}
+
+bool TaskDlgCosmeticCircle::reject()
+{
+ widget->reject();
+ return true;
+}
+
+#include
+
diff --git a/src/Mod/TechDraw/Gui/TaskCosmeticCircle.h b/src/Mod/TechDraw/Gui/TaskCosmeticCircle.h
new file mode 100644
index 0000000000..12fc33ad8d
--- /dev/null
+++ b/src/Mod/TechDraw/Gui/TaskCosmeticCircle.h
@@ -0,0 +1,128 @@
+/***************************************************************************
+ * Copyright (c) 2023 WandererFan *
+ * *
+ * This file is part of the FreeCAD CAx development system. *
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Library General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ * This library is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU Library General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Library General Public *
+ * License along with this library; see the file COPYING.LIB. If not, *
+ * write to the Free Software Foundation, Inc., 59 Temple Place, *
+ * Suite 330, Boston, MA 02111-1307, USA *
+ * *
+ ***************************************************************************/
+
+#ifndef TECHDRAWGUI_TASKCOSMETICCIRCLE_H
+#define TECHDRAWGUI_TASKCOSMETICCIRCLE_H
+
+#include
+#include
+#include
+#include
+
+
+namespace TechDraw
+{
+class DrawPage;
+class DrawView;
+class DrawViewPart;
+class CosmeticEdge;
+class Face;
+class LineFormat;
+}
+
+namespace TechDrawGui
+{
+class QGSPage;
+class QGVPage;
+class QGIView;
+class QGIPrimPath;
+class MDIViewPage;
+class ViewProviderViewPart;
+class Ui_TaskCosmeticCircle;
+
+class TaskCosmeticCircle : public QWidget
+{
+ Q_OBJECT
+
+public:
+ TaskCosmeticCircle(TechDraw::DrawViewPart* partFeat,
+ Base::Vector3d center, bool is3d);
+ TaskCosmeticCircle(TechDraw::DrawViewPart* partFeat,
+ std::string circleName);
+ ~TaskCosmeticCircle() override;
+
+ virtual bool accept();
+ virtual bool reject();
+ void updateTask();
+
+protected:
+ void changeEvent(QEvent *e) override;
+
+ void setUiPrimary();
+ void setUiEdit();
+
+ void createCosmeticCircle();
+ void updateCosmeticCircle();
+
+private:
+ std::unique_ptr ui;
+
+ TechDraw::DrawViewPart* m_partFeat;
+
+ std::string m_circleName;
+ TechDraw::CosmeticEdge* m_ce;
+ TechDraw::CosmeticEdge* m_saveCE;
+ Base::Vector3d m_center;
+ double m_radius;
+ bool m_createMode;
+ std::string m_tag;
+ bool m_is3d;
+};
+
+class TaskDlgCosmeticCircle : public Gui::TaskView::TaskDialog
+{
+ Q_OBJECT
+
+public:
+ TaskDlgCosmeticCircle(TechDraw::DrawViewPart* partFeat,
+ Base::Vector3d center, bool is3d);
+ TaskDlgCosmeticCircle(TechDraw::DrawViewPart* partFeat,
+ std::string circleName)
+; ~TaskDlgCosmeticCircle() override;
+
+public:
+ /// is called the TaskView when the dialog is opened
+ void open() override;
+ /// is called by the framework if an button is clicked which has no accept or reject role
+ void clicked(int) override;
+ /// is called by the framework if the dialog is accepted (Ok)
+ bool accept() override;
+ /// is called by the framework if the dialog is rejected (Cancel)
+ bool reject() override;
+ /// is called by the framework if the user presses the help button
+ void helpRequested() override { return;}
+ bool isAllowedAlterDocument() const override
+ { return false; }
+ void update();
+
+protected:
+
+private:
+ TaskCosmeticCircle* widget;
+
+ Gui::TaskView::TaskBox* taskbox;
+};
+
+} //namespace TechDrawGui
+
+#endif // #ifndef TECHDRAWGUI_TASKCOSMETICCIRCLE_H
+
diff --git a/src/Mod/TechDraw/Gui/TaskCosmeticCircle.ui b/src/Mod/TechDraw/Gui/TaskCosmeticCircle.ui
new file mode 100644
index 0000000000..12bde53ed7
--- /dev/null
+++ b/src/Mod/TechDraw/Gui/TaskCosmeticCircle.ui
@@ -0,0 +1,252 @@
+
+
+ TechDrawGui::TaskCosmeticCircle
+
+
+
+ 0
+ 0
+ 350
+ 368
+
+
+
+
+ 0
+ 0
+
+
+
+
+ 250
+ 0
+
+
+
+ Cosmetic Circle
+
+
+ -
+
+
-
+
+
+ View
+
+
+
+ -
+
+
+ false
+
+
+ false
+
+
+ Qt::NoFocus
+
+
+ false
+
+
+
+
+
+ -
+
+
-
+
+
+ Treat the center point as a 2d point within the parent View. Z coordinate is ignored.
+
+
+ 2d Point
+
+
+ true
+
+
+ true
+
+
+
+ -
+
+
+ Treat the center point as a 3d point and project it onto the parent View.
+
+
+ 3d Point
+
+
+ true
+
+
+
+ -
+
+
+ Circle Center
+
+
+
+
+
+ -
+
+
-
+
+
+ X:
+
+
+
+ -
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+
+
+
+ -
+
+
+ Y:
+
+
+
+ -
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+
+
+
+ -
+
+
+ Z:
+
+
+
+ -
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+ Radius:
+
+
+
+ -
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+
+
+ 10.000000000000000
+
+
+
+
+
+ -
+
+
-
+
+
+ Start Angle:
+
+
+
+ -
+
+
+ End angle (conventional) of arc in degrees.
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+
+
+
+ -
+
+
+ End Angle:
+
+
+
+ -
+
+
+ Start angle (conventional) of arc in degrees.
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+
+
+
+ -
+
+
+ Arc of Circle
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+ Gui::QuantitySpinBox
+ QWidget
+
+
+
+
+
+
+
+
diff --git a/src/Mod/TechDraw/Gui/Workbench.cpp b/src/Mod/TechDraw/Gui/Workbench.cpp
index 60d11b8105..75d3bc62b9 100644
--- a/src/Mod/TechDraw/Gui/Workbench.cpp
+++ b/src/Mod/TechDraw/Gui/Workbench.cpp
@@ -176,6 +176,7 @@ Gui::MenuItem* Workbench::setupMenuBar() const
*lines << "TechDraw_2LineCenterLine";
*lines << "TechDraw_2PointCenterLine";
*lines << "TechDraw_2PointCosmeticLine";
+ *lines << "TechDraw_CosmeticCircle";
*lines << "Separator";
*lines << "TechDraw_DecorateLine";
*lines << "TechDraw_ShowAll";
@@ -376,6 +377,7 @@ Gui::ToolBarItem* Workbench::setupToolBars() const
*anno << "TechDraw_CosmeticVertexGroup";
*anno << "TechDraw_CenterLineGroup";
*anno << "TechDraw_2PointCosmeticLine";
+ *anno << "TechDraw_CosmeticCircle";
*anno << "TechDraw_CosmeticEraser";
*anno << "TechDraw_DecorateLine";
*anno << "TechDraw_ShowAll";
@@ -485,6 +487,7 @@ Gui::ToolBarItem* Workbench::setupCommandBars() const
*anno << "TechDraw_CosmeticVertexGroup";
*anno << "TechDraw_CenterLineGroup";
*anno << "TechDraw_2PointCosmeticLine";
+ *anno << "TechDraw_CosmeticCircle";
*anno << "TechDraw_CosmeticEraser";
*anno << "TechDraw_DecorateLine";
*anno << "TechDraw_ShowAll";