[TD]Cosmetic function overhaul (#14216)

* [TD]Cosmetic geometry and tools update

- all cosmetics to store geometry in same form
- all cosmetics to survive scaling and rotation
- extension functions to survive scaling and rotation

* [TD]overhaul leader point storage and editing

- add py routine makeLeader(points)

* [TD]add leader conversion utility

* [TD]Set Leader RotateWithView default to true

* [TD]fix intersection vertex position

* [TD]add CosmeticEdge::makeLineFromCanonicalPoints

* [TD]fix 2 Extension tools

- positioning in DrawCosmeticCircle
- mishandling of points in execLineParallelPerpendicular

* [TD]Remove duplicate constexpr

* [TD]fix 2x Cosmetic arc tools

* [TD]refactor LineFormat out of Cosmetic

* [TD]move cosmetic appearance settings to LineFormat

* [TD]remove 2 unused methods

* [TD]apply format to blue line & circle tools

* [TD]fix ballon arrowhead does not rotate with view

* [TD]fix CosmeticCircle3Points

* [TD]allow multiple cosmetic object deletions

* [TD]fix extend/shorten centerline
This commit is contained in:
WandererFan
2024-05-23 09:41:42 -04:00
committed by GitHub
parent a8d093280e
commit 50f970efd7
56 changed files with 1812 additions and 1076 deletions

View File

@@ -149,5 +149,7 @@ PyMOD_INIT_FUNC(TechDraw)
TechDraw::DrawWeldSymbolPython::init();
TechDraw::DrawBrokenViewPython::init();
TechDraw::LineFormat::initCurrentLineFormat();
PyMOD_Return(mod);
}

View File

@@ -72,7 +72,7 @@
#include "ProjectionAlgos.h"
#include "TechDrawExport.h"
#include "CosmeticVertexPy.h"
#include "DrawLeaderLinePy.h"
namespace TechDraw {
//module level static C++ functions go here
@@ -191,6 +191,9 @@ public:
add_varargs_method("makeCanonicalPoint", &Module::makeCanonicalPoint,
"makeCanonicalPoint(DrawViewPart, Vector3d) - Returns the unscaled, unrotated version of the input point)"
);
add_varargs_method("makeLeader", &Module::makeLeader,
"makeLeader(parent - DrawViewPart, points - [Vector], startSymbol - int, endSymbol - int) - Creates a leader line attached to parent. Points are in page coordinates with (0, 0) at lowerleft.s"
);
initialize("This is a module for making drawings"); // register with Python
}
~Module() override {}
@@ -1286,6 +1289,42 @@ private:
cPoint = CosmeticVertex::makeCanonicalPoint(dvp, cPoint, unscale);
return Py::asObject(new Base::VectorPy(cPoint));
}
Py::Object makeLeader(const Py::Tuple& args)
{
PyObject* pDvp(nullptr);
PyObject* pPointList(nullptr);
int iStartSymbol = 0;
int iEndSymbol = 0;
TechDraw::DrawViewPart* dvp = nullptr;
if (!PyArg_ParseTuple(args.ptr(), "OO!|ii", &pDvp, &(PyList_Type), &pPointList, &iStartSymbol, &iEndSymbol)) {
throw Py::TypeError("expected (DrawViewPart, listofpoints, startsymbolindex, endsymbolindex");
}
if (PyObject_TypeCheck(pDvp, &(TechDraw::DrawViewPartPy::Type))) {
App::DocumentObject* obj = static_cast<App::DocumentObjectPy*>(pDvp)->getDocumentObjectPtr();
dvp = static_cast<TechDraw::DrawViewPart*>(obj);
}
std::vector<Base::Vector3d> pointList;
try {
Py::Sequence list(pPointList);
for (Py::Sequence::iterator it = list.begin(); it != list.end(); ++it) {
if (PyObject_TypeCheck((*it).ptr(), &(Base::VectorPy::Type))) {
Base::Vector3d temp = static_cast<Base::VectorPy*>((*it).ptr())->value();
pointList.push_back(temp);
}
}
}
catch (Standard_Failure& e) {
throw Py::Exception(Part::PartExceptionOCCError, e.GetMessageString());
}
auto newLeader = DrawLeaderLine::makeLeader(dvp, pointList, iStartSymbol, iEndSymbol);
// return the new leader as DrawLeaderPy
return Py::asObject(new DrawLeaderLinePy(newLeader));
}
};
PyObject* initModule()

View File

@@ -174,6 +174,8 @@ SET(TechDraw_SRCS
XMLQuery.h
LineGenerator.cpp
LineGenerator.h
LineFormat.cpp
LineFormat.h
)
SET(Geometry_SRCS

View File

@@ -885,10 +885,10 @@ void CenterLine::Save(Base::Writer &writer) const
// style is deprecated in favour of line number, but we still save and restore it
// to avoid problems with old documents.
writer.Stream() << writer.ind() << "<Style value=\"" << m_format.m_style << "\"/>" << std::endl;
writer.Stream() << writer.ind() << "<Weight value=\"" << m_format.m_weight << "\"/>" << std::endl;
writer.Stream() << writer.ind() << "<Color value=\"" << m_format.m_color.asHexString() << "\"/>" << std::endl;
const char v = m_format.m_visible?'1':'0';
writer.Stream() << writer.ind() << "<Style value=\"" << m_format.getStyle() << "\"/>" << std::endl;
writer.Stream() << writer.ind() << "<Weight value=\"" << m_format.getWidth() << "\"/>" << std::endl;
writer.Stream() << writer.ind() << "<Color value=\"" << m_format.getColor().asHexString() << "\"/>" << std::endl;
const char v = m_format.getVisible() ? '1' : '0';
writer.Stream() << writer.ind() << "<Visible value=\"" << v << "\"/>" << std::endl;
//stored geometry
@@ -984,14 +984,16 @@ void CenterLine::Restore(Base::XMLReader &reader)
// style is deprecated in favour of line number, but we still save and restore it
// to avoid problems with old documents.
reader.readElement("Style");
m_format.m_style = reader.getAttributeAsInteger("value");
m_format.setStyle(reader.getAttributeAsInteger("value"));
reader.readElement("Weight");
m_format.m_weight = reader.getAttributeAsFloat("value");
m_format.setWidth(reader.getAttributeAsFloat("value"));
reader.readElement("Color");
std::string temp = reader.getAttribute("value");
m_format.m_color.fromHexString(temp);
std::string tempHex = reader.getAttribute("value");
App::Color tempColor;
tempColor.fromHexString(tempHex);
m_format.setColor(tempColor);
reader.readElement("Visible");
m_format.m_visible = (int)reader.getAttributeAsInteger("value")==0?false:true;
m_format.setVisible( (int)reader.getAttributeAsInteger("value")==0 ? false : true);
//stored geometry
reader.readElement("GeometryType");

View File

@@ -115,10 +115,10 @@ Py::Dict CenterLinePy::getFormat() const
TechDraw::LineFormat* format= &(this->getCenterLinePtr()->m_format);
Py::Dict dict;
dict.setItem("style", Py::Long(format->m_style));
dict.setItem("weight", Py::Float(format->m_weight));
dict.setItem("color", Py::Tuple(DrawUtil::colorToPyTuple(format->m_color), true));
dict.setItem("visible", Py::Boolean(format->m_visible));
dict.setItem("style", Py::Long(format->getStyle()));
dict.setItem("weight", Py::Float(format->getWidth()));
dict.setItem("color", Py::Tuple(DrawUtil::colorToPyTuple(format->getColor()), true));
dict.setItem("visible", Py::Boolean(format->getVisible()));
return dict;
}
@@ -138,10 +138,10 @@ void CenterLinePy::setFormat(Py::Dict arg)
}
TechDraw::LineFormat* format = &(this->getCenterLinePtr()->m_format);
format->m_style = style;
format->m_weight = weight;
format->m_color = DrawUtil::pyTupleToColor(pColor);
format->m_visible = Base::asBoolean(visible);
format->setStyle(style);
format->setWidth(weight);
format->setColor(DrawUtil::pyTupleToColor(pColor));
format->setVisible(Base::asBoolean(visible));
}
Py::String CenterLinePy::getTag() const

View File

@@ -37,11 +37,8 @@
#include "DrawUtil.h"
#include "DrawViewPart.h"
#include "GeometryObject.h"
#include "LineGroup.h"
#include "LineGenerator.h"
#include "Preferences.h"
using namespace TechDraw;
using namespace std;
using DU = DrawUtil;
@@ -50,67 +47,11 @@ using DU = DrawUtil;
#define COSMETICEDGE 1
#define CENTERLINE 2
LineFormat::LineFormat()
{
m_style = getDefEdgeStyle();
m_weight = getDefEdgeWidth();
m_color= getDefEdgeColor();
m_visible = true;
m_lineNumber = LineGenerator::fromQtStyle((Qt::PenStyle)m_style);
}
LineFormat::LineFormat(const int style,
const double weight,
const App::Color& color,
const bool visible) :
m_style(style),
m_weight(weight),
m_color(color),
m_visible(visible),
m_lineNumber(LineGenerator::fromQtStyle((Qt::PenStyle)m_style))
{
}
void LineFormat::dump(const char* title)
{
Base::Console().Message("LF::dump - %s \n", title);
Base::Console().Message("LF::dump - %s \n", toString().c_str());
}
std::string LineFormat::toString() const
{
std::stringstream ss;
ss << m_style << ", " <<
m_weight << ", " <<
m_color.asHexString() << ", " <<
m_visible;
return ss.str();
}
//static preference getters.
double LineFormat::getDefEdgeWidth()
{
return TechDraw::LineGroup::getDefaultWidth("Graphic");
}
App::Color LineFormat::getDefEdgeColor()
{
return Preferences::normalColor();
}
int LineFormat::getDefEdgeStyle()
{
return Preferences::getPreferenceGroup("Decorations")->GetInt("CenterLineStyle", 2); //dashed
}
//******************************************
TYPESYSTEM_SOURCE(TechDraw::CosmeticEdge, Base::Persistence)
//note this ctor has no occEdge or first/last point for geometry!
CosmeticEdge::CosmeticEdge()
{
// Base::Console().Message("CE::CE()\n");
permaRadius = 0.0;
m_geometry = std::make_shared<TechDraw::BaseGeom> ();
initialize();
@@ -118,7 +59,6 @@ CosmeticEdge::CosmeticEdge()
CosmeticEdge::CosmeticEdge(const CosmeticEdge* ce)
{
// Base::Console().Message("CE::CE(ce)\n");
TechDraw::BaseGeomPtr newGeom = ce->m_geometry->copy();
//these endpoints are already YInverted
permaStart = ce->permaStart;
@@ -130,13 +70,12 @@ CosmeticEdge::CosmeticEdge(const CosmeticEdge* ce)
}
CosmeticEdge::CosmeticEdge(const Base::Vector3d& pt1, const Base::Vector3d& pt2) :
// 🠓 returns TopoDS_Edge
CosmeticEdge::CosmeticEdge(TopoDS_EdgeFromVectors(pt1, pt2))
CosmeticEdge::CosmeticEdge(TopoDS_EdgeFromVectors(pt1, pt2))
{
}
// 🠓 returns TechDraw::BaseGeomPtr
CosmeticEdge::CosmeticEdge(const TopoDS_Edge& e) : CosmeticEdge(TechDraw::BaseGeom::baseFactory(e))
CosmeticEdge::CosmeticEdge(const TopoDS_Edge& e) :
CosmeticEdge(TechDraw::BaseGeom::baseFactory(e))
{
}
@@ -153,6 +92,15 @@ CosmeticEdge::CosmeticEdge(const TechDraw::BaseGeomPtr g)
permaStart = circ->center;
permaEnd = circ->center;
permaRadius = circ->radius;
if (g->getGeomType() == TechDraw::GeomType::ARCOFCIRCLE) {
TechDraw::AOCPtr aoc = std::static_pointer_cast<TechDraw::AOC>(circ);
aoc->clockwiseAngle(g->clockwiseAngle());
aoc->startPnt = g->getStartPoint();
aoc->startAngle = g->getStartAngle();
aoc->endPnt = g->getEndPoint();
aoc->endAngle = g->getEndAngle();
// aoc->largeArc = g->largeArc;
}
}
initialize();
}
@@ -173,15 +121,10 @@ void CosmeticEdge::initialize()
m_geometry->setCosmeticTag(getTagAsString());
}
// TODO: not sure that this method should be doing the inversion. CV for example
// accepts input point as is. The caller should have figured out the correct points.
TopoDS_Edge CosmeticEdge::TopoDS_EdgeFromVectors(const Base::Vector3d& pt1, const Base::Vector3d& pt2)
{
// Base::Console().Message("CE::CE(p1, p2)\n");
Base::Vector3d p1 = DrawUtil::invertY(pt1);
Base::Vector3d p2 = DrawUtil::invertY(pt2);
gp_Pnt gp1(p1.x, p1.y, p1.z);
gp_Pnt gp2(p2.x, p2.y, p2.z);
gp_Pnt gp1(pt1.x, pt1.y, pt1.z);
gp_Pnt gp2(pt2.x, pt2.y, pt2.z);
return BRepBuilderAPI_MakeEdge(gp1, gp2);
}
@@ -202,7 +145,7 @@ TechDraw::BaseGeomPtr CosmeticEdge::scaledGeometry(const double scale)
TechDraw::BaseGeomPtr CosmeticEdge::scaledAndRotatedGeometry(const double scale, const double rotDegrees)
{
TopoDS_Edge e = m_geometry->getOCCEdge();
// TopoDS_Shape s = TechDraw::scaleShape(e, scale);
bool saveCW = m_geometry->clockwiseAngle();
// Mirror shape in Y and scale
TopoDS_Shape s = ShapeUtils::mirrorShape(e, gp_Pnt(0.0, 0.0, 0.0), scale);
// rotate using OXYZ as the coordinate system
@@ -215,11 +158,11 @@ TechDraw::BaseGeomPtr CosmeticEdge::scaledAndRotatedGeometry(const double scale,
newGeom->setCosmetic(true);
newGeom->source(COSMETICEDGE);
newGeom->setCosmeticTag(getTagAsString());
newGeom->clockwiseAngle(saveCW);
return newGeom;
}
//! makes an unscaled, unrotated line from two scaled & rotated end points. If points is Gui space coordinates,
//! they should be inverted (DU::invertY) before calling this method.
//! makes an unscaled, unrotated line from two scaled & rotated end points.
//! the result of this method should be used in addCosmeticEdge().
TechDraw::BaseGeomPtr CosmeticEdge::makeCanonicalLine(DrawViewPart* dvp, Base::Vector3d start, Base::Vector3d end)
{
@@ -228,7 +171,16 @@ TechDraw::BaseGeomPtr CosmeticEdge::makeCanonicalLine(DrawViewPart* dvp, Base::V
gp_Pnt gStart = DU::togp_Pnt(cStart);
gp_Pnt gEnd = DU::togp_Pnt(cEnd);
TopoDS_Edge edge = BRepBuilderAPI_MakeEdge(gStart, gEnd);
return TechDraw::BaseGeom::baseFactory(edge)->inverted();
return TechDraw::BaseGeom::baseFactory(edge);
}
//! makes an unscaled, unrotated line from two canonical points.
TechDraw::BaseGeomPtr CosmeticEdge::makeLineFromCanonicalPoints(Base::Vector3d start, Base::Vector3d end)
{
gp_Pnt gStart = DU::togp_Pnt(start);
gp_Pnt gEnd = DU::togp_Pnt(end);
TopoDS_Edge edge = BRepBuilderAPI_MakeEdge(gStart, gEnd);
return TechDraw::BaseGeom::baseFactory(edge);
}
std::string CosmeticEdge::toString() const
@@ -261,10 +213,10 @@ void CosmeticEdge::Save(Base::Writer &writer) const
{
// TODO: this should be using m_format->Save(writer) instead of saving the individual
// fields.
writer.Stream() << writer.ind() << "<Style value=\"" << m_format.m_style << "\"/>" << endl;
writer.Stream() << writer.ind() << "<Weight value=\"" << m_format.m_weight << "\"/>" << endl;
writer.Stream() << writer.ind() << "<Color value=\"" << m_format.m_color.asHexString() << "\"/>" << endl;
const char v = m_format.m_visible?'1':'0';
writer.Stream() << writer.ind() << "<Style value=\"" << m_format.getStyle() << "\"/>" << endl;
writer.Stream() << writer.ind() << "<Weight value=\"" << m_format.getWidth() << "\"/>" << endl;
writer.Stream() << writer.ind() << "<Color value=\"" << m_format.getColor().asHexString() << "\"/>" << endl;
const char v = m_format.getVisible() ? '1' : '0';
writer.Stream() << writer.ind() << "<Visible value=\"" << v << "\"/>" << endl;
writer.Stream() << writer.ind() << "<GeometryType value=\"" << m_geometry->getGeomType() <<"\"/>" << endl;
@@ -292,14 +244,16 @@ void CosmeticEdge::Restore(Base::XMLReader &reader)
}
// Base::Console().Message("CE::Restore - reading elements\n");
reader.readElement("Style");
m_format.m_style = reader.getAttributeAsInteger("value");
m_format.setStyle(reader.getAttributeAsInteger("value"));
reader.readElement("Weight");
m_format.m_weight = reader.getAttributeAsFloat("value");
m_format.setWidth(reader.getAttributeAsFloat("value"));
reader.readElement("Color");
std::string temp = reader.getAttribute("value");
m_format.m_color.fromHexString(temp);
std::string tempHex = reader.getAttribute("value");
App::Color tempColor;
tempColor.fromHexString(tempHex);
m_format.setColor(tempColor);
reader.readElement("Visible");
m_format.m_visible = reader.getAttributeAsInteger("value") != 0;
m_format.setVisible(reader.getAttributeAsInteger("value") != 0);
reader.readElement("GeometryType");
TechDraw::GeomType gType = static_cast<TechDraw::GeomType>(reader.getAttributeAsInteger("value"));
@@ -378,20 +332,12 @@ void CosmeticEdge::assignTag(const TechDraw::CosmeticEdge* ce)
throw Base::TypeError("CosmeticEdge tag can not be assigned as types do not match.");
}
CosmeticEdge* CosmeticEdge::copy() const
{
// Base::Console().Message("CE::copy()\n");
CosmeticEdge* newCE = new CosmeticEdge();
TechDraw::BaseGeomPtr newGeom = m_geometry->copy();
newCE->m_geometry = newGeom;
newCE->m_format = m_format;
return newCE;
}
CosmeticEdge* CosmeticEdge::clone() const
{
// Base::Console().Message("CE::clone()\n");
CosmeticEdge* cpy = this->copy();
Base::Console().Message("CE::clone()\n");
CosmeticEdge* cpy = new CosmeticEdge();
cpy->m_geometry = m_geometry->copy();
cpy->m_format = m_format;
cpy->tag = this->tag;
return cpy;
}
@@ -412,10 +358,10 @@ TYPESYSTEM_SOURCE(TechDraw::GeomFormat, Base::Persistence)
GeomFormat::GeomFormat() :
m_geomIndex(-1)
{
m_format.m_style = LineFormat::getDefEdgeStyle();
m_format.m_weight = LineFormat::getDefEdgeWidth();
m_format.m_color = LineFormat::getDefEdgeColor();
m_format.m_visible = true;
m_format.setStyle(LineFormat::getDefEdgeStyle());
m_format.setWidth(LineFormat::getDefEdgeWidth());
m_format.setColor(LineFormat::getDefEdgeColor());
m_format.setVisible(true);
m_format.setLineNumber(LineFormat::InvalidLine);
createNewTag();
@@ -424,10 +370,10 @@ GeomFormat::GeomFormat() :
GeomFormat::GeomFormat(const GeomFormat* gf)
{
m_geomIndex = gf->m_geomIndex;
m_format.m_style = gf->m_format.m_style;
m_format.m_weight = gf->m_format.m_weight;
m_format.m_color = gf->m_format.m_color;
m_format.m_visible = gf->m_format.m_visible;
m_format.setStyle(gf->m_format.getStyle());
m_format.setWidth(gf->m_format.getWidth());
m_format.setColor(gf->m_format.getColor());
m_format.setVisible(gf->m_format.getVisible());
m_format.setLineNumber(gf->m_format.getLineNumber());
createNewTag();
@@ -437,10 +383,10 @@ GeomFormat::GeomFormat(const int idx,
const TechDraw::LineFormat& fmt) :
m_geomIndex(idx)
{
m_format.m_style = fmt.m_style;
m_format.m_weight = fmt.m_weight;
m_format.m_color = fmt.m_color;
m_format.m_visible = fmt.m_visible;
m_format.setStyle(fmt.getStyle());
m_format.setWidth(fmt.getWidth());
m_format.setColor(fmt.getColor());
m_format.setVisible(fmt.getVisible());
m_format.setLineNumber(fmt.getLineNumber());
createNewTag();
@@ -472,13 +418,13 @@ unsigned int GeomFormat::getMemSize () const
void GeomFormat::Save(Base::Writer &writer) const
{
const char v = m_format.m_visible?'1':'0';
const char v = m_format.getVisible() ? '1' : '0';
writer.Stream() << writer.ind() << "<GeomIndex value=\"" << m_geomIndex << "\"/>" << endl;
// style is deprecated in favour of line number, but we still save and restore it
// to avoid problems with old documents.
writer.Stream() << writer.ind() << "<Style value=\"" << m_format.m_style << "\"/>" << endl;
writer.Stream() << writer.ind() << "<Weight value=\"" << m_format.m_weight << "\"/>" << endl;
writer.Stream() << writer.ind() << "<Color value=\"" << m_format.m_color.asHexString() << "\"/>" << endl;
writer.Stream() << writer.ind() << "<Style value=\"" << m_format.getStyle() << "\"/>" << endl;
writer.Stream() << writer.ind() << "<Weight value=\"" << m_format.getWidth() << "\"/>" << endl;
writer.Stream() << writer.ind() << "<Color value=\"" << m_format.getColor().asHexString() << "\"/>" << endl;
writer.Stream() << writer.ind() << "<Visible value=\"" << v << "\"/>" << endl;
writer.Stream() << writer.ind() << "<LineNumber value=\"" << m_format.getLineNumber() << "\"/>" << endl;
}
@@ -496,14 +442,16 @@ void GeomFormat::Restore(Base::XMLReader &reader)
// style is deprecated in favour of line number, but we still save and restore it
// to avoid problems with old documents.
reader.readElement("Style");
m_format.m_style = reader.getAttributeAsInteger("value");
m_format.setStyle(reader.getAttributeAsInteger("value"));
reader.readElement("Weight");
m_format.m_weight = reader.getAttributeAsFloat("value");
m_format.setWidth(reader.getAttributeAsFloat("value"));
reader.readElement("Color");
std::string temp = reader.getAttribute("value");
m_format.m_color.fromHexString(temp);
std::string tempHex = reader.getAttribute("value");
App::Color tempColor;
tempColor.fromHexString(tempHex);
m_format.setColor(tempColor);
reader.readElement("Visible");
m_format.m_visible = (int)reader.getAttributeAsInteger("value")==0?false:true;
m_format.setVisible((int)reader.getAttributeAsInteger("value") == 0 ? false : true);
// older documents may not have the LineNumber element, so we need to check the
// next entry. if it is a start element, then we check if it is a start element
// for LineNumber.
@@ -566,10 +514,10 @@ GeomFormat* GeomFormat::copy() const
{
GeomFormat* newFmt = new GeomFormat();
newFmt->m_geomIndex = m_geomIndex;
newFmt->m_format.m_style = m_format.m_style;
newFmt->m_format.m_weight = m_format.m_weight;
newFmt->m_format.m_color = m_format.m_color;
newFmt->m_format.m_visible = m_format.m_visible;
newFmt->m_format.setStyle(m_format.getStyle());
newFmt->m_format.setWidth(m_format.getWidth());
newFmt->m_format.setColor(m_format.getColor());
newFmt->m_format.setVisible(m_format.getVisible());
newFmt->m_format.setLineNumber(m_format.getLineNumber());
return newFmt;
}

View File

@@ -31,54 +31,13 @@
#include <Base/Vector3D.h>
#include "Geometry.h"
#include "LineFormat.h"
class TopoDS_Edge;
namespace TechDraw {
class DrawViewPart;
//general purpose line format specifier
class TechDrawExport LineFormat
{
public:
static constexpr size_t InvalidLine{0};
LineFormat();
LineFormat(const int style,
const double weight,
const App::Color& color,
const bool visible);
~LineFormat() = default;
int getStyle() const { return m_style; }
void setStyle(int style) { m_style = style; }
double getWidth() const { return m_weight; }
void setWidth(double width) {m_weight = width; }
App::Color getColor() const { return m_color; }
void setColor(App::Color color) { m_color = color; }
QColor getQColor() const { return m_color.asValue<QColor>(); }
void setQColor(QColor qColor) { m_color.set(qColor.redF(), qColor.greenF(), qColor.blueF(), 1.0 - qColor.alphaF()); }
bool getVisible() const { return m_visible; }
void setVisible(bool viz) { m_visible = viz; }
int getLineNumber() const { return m_lineNumber; }
void setLineNumber(int number) { m_lineNumber = number; }
int m_style;
double m_weight;
App::Color m_color;
bool m_visible;
int m_lineNumber {1};
static double getDefEdgeWidth();
static App::Color getDefEdgeColor();
static int getDefEdgeStyle();
void dump(const char* title);
std::string toString() const;
};
//********** CosmeticEdge ******************************************************
class TechDrawExport CosmeticEdge : public Base::Persistence, public TechDraw::BaseGeom
@@ -99,6 +58,11 @@ public:
TechDraw::BaseGeomPtr scaledAndRotatedGeometry(const double scale, const double rotDegrees);
static TechDraw::BaseGeomPtr makeCanonicalLine(DrawViewPart* dvp, Base::Vector3d start, Base::Vector3d end);
static TechDraw::BaseGeomPtr makeLineFromCanonicalPoints(Base::Vector3d start, Base::Vector3d end);
LineFormat format() const { return m_format; }
void setFormat(LineFormat newFormat) { m_format = newFormat; }
std::string toString() const override;
void dump(const char* title) const;
@@ -108,7 +72,6 @@ public:
void Restore(Base::XMLReader &/*reader*/) override;
PyObject *getPyObject() override;
CosmeticEdge* copy() const;
CosmeticEdge* clone() const;
Base::Vector3d permaStart; //persistent unscaled start/end points in View coords

View File

@@ -15,16 +15,6 @@
<Author Licence="LGPL" Name="WandererFan" EMail="wandererfan@gmail.com" />
<UserDocu>CosmeticEdge specifies an extra (cosmetic) edge in Views</UserDocu>
</Documentation>
<Methode Name="clone" Const="true">
<Documentation>
<UserDocu>Create a clone of this CosmeticEdge</UserDocu>
</Documentation>
</Methode>
<Methode Name="copy" Const="true">
<Documentation>
<UserDocu>Create a copy of this CosmeticEdge</UserDocu>
</Documentation>
</Methode>
<Attribute Name="Tag" ReadOnly="true">
<Documentation>
<UserDocu>Gives the tag of the CosmeticEdge as string.</UserDocu>

View File

@@ -62,59 +62,59 @@ int CosmeticEdgePy::PyInit(PyObject* /*args*/, PyObject* /*kwd*/)
}
//From Part::GeometryPy.cpp
PyObject* CosmeticEdgePy::clone(PyObject *args)
{
if (!PyArg_ParseTuple(args, ""))
return nullptr;
// PyObject* CosmeticEdgePy::clone(PyObject *args)
// {
// if (!PyArg_ParseTuple(args, ""))
// return nullptr;
TechDraw::CosmeticEdge* geom = this->getCosmeticEdgePtr();
PyTypeObject* type = this->GetType();
PyObject* cpy = nullptr;
// let the type object decide
if (type->tp_new)
cpy = type->tp_new(type, this, nullptr);
if (!cpy) {
PyErr_SetString(PyExc_RuntimeError, "failed to create clone of CosmeticEdge");
return nullptr;
}
// TechDraw::CosmeticEdge* geom = this->getCosmeticEdgePtr();
// PyTypeObject* type = this->GetType();
// PyObject* cpy = nullptr;
// // let the type object decide
// if (type->tp_new)
// cpy = type->tp_new(type, this, nullptr);
// if (!cpy) {
// PyErr_SetString(PyExc_RuntimeError, "failed to create clone of CosmeticEdge");
// return nullptr;
// }
TechDraw::CosmeticEdgePy* geompy = static_cast<TechDraw::CosmeticEdgePy*>(cpy);
// the PyMake function must have created the corresponding instance of the 'CosmeticEdge' subclass
// so delete it now to avoid a memory leak
if (geompy->_pcTwinPointer) {
TechDraw::CosmeticEdge* clone = static_cast<TechDraw::CosmeticEdge*>(geompy->_pcTwinPointer);
delete clone;
}
geompy->_pcTwinPointer = geom->clone();
return cpy;
}
// TechDraw::CosmeticEdgePy* geompy = static_cast<TechDraw::CosmeticEdgePy*>(cpy);
// // the PyMake function must have created the corresponding instance of the 'CosmeticEdge' subclass
// // so delete it now to avoid a memory leak
// if (geompy->_pcTwinPointer) {
// TechDraw::CosmeticEdge* clone = static_cast<TechDraw::CosmeticEdge*>(geompy->_pcTwinPointer);
// delete clone;
// }
// geompy->_pcTwinPointer = geom->clone();
// return cpy;
// }
PyObject* CosmeticEdgePy::copy(PyObject *args)
{
if (!PyArg_ParseTuple(args, ""))
return nullptr;
// PyObject* CosmeticEdgePy::copy(PyObject *args)
// {
// if (!PyArg_ParseTuple(args, ""))
// return nullptr;
TechDraw::CosmeticEdge* geom = this->getCosmeticEdgePtr();
PyTypeObject* type = this->GetType();
PyObject* cpy = nullptr;
// let the type object decide
if (type->tp_new)
cpy = type->tp_new(type, this, nullptr);
if (!cpy) {
PyErr_SetString(PyExc_RuntimeError, "failed to create copy of CosmeticEdge");
return nullptr;
}
// TechDraw::CosmeticEdge* ce = this->getCosmeticEdgePtr();
// PyTypeObject* type = this->GetType();
// PyObject* cpy = nullptr;
// // let the type object decide
// if (type->tp_new)
// cpy = type->tp_new(type, this, nullptr);
// if (!cpy) {
// PyErr_SetString(PyExc_RuntimeError, "failed to create copy of CosmeticEdge");
// return nullptr;
// }
TechDraw::CosmeticEdgePy* geompy = static_cast<TechDraw::CosmeticEdgePy*>(cpy);
// the PyMake function must have created the corresponding instance of the 'CosmeticEdge' subclass
// so delete it now to avoid a memory leak
if (geompy->_pcTwinPointer) {
TechDraw::CosmeticEdge* copy = static_cast<TechDraw::CosmeticEdge*>(geompy->_pcTwinPointer);
delete copy;
}
geompy->_pcTwinPointer = geom->copy();
return cpy;
}
// TechDraw::CosmeticEdgePy* geompy = static_cast<TechDraw::CosmeticEdgePy*>(cpy);
// // the PyMake function must have created the corresponding instance of the 'CosmeticEdge' subclass
// // so delete it now to avoid a memory leak
// if (geompy->_pcTwinPointer) {
// TechDraw::CosmeticEdge* copy = static_cast<TechDraw::CosmeticEdge*>(geompy->_pcTwinPointer);
// delete copy;
// }
// geompy->_pcTwinPointer = ce->copy();
// return cpy;
// }
void CosmeticEdgePy::setFormat(Py::Dict arg)
{
@@ -131,10 +131,10 @@ void CosmeticEdgePy::setFormat(Py::Dict arg)
}
TechDraw::LineFormat* format = &(this->getCosmeticEdgePtr()->m_format);
format->m_style = style;
format->m_weight = weight;
format->m_color = DrawUtil::pyTupleToColor(pColor);
format->m_visible = Base::asBoolean(visible);
format->setStyle(style);
format->setWidth(weight);
format->setColor(DrawUtil::pyTupleToColor(pColor));
format->setVisible(Base::asBoolean(visible));
}
Py::Dict CosmeticEdgePy::getFormat() const
@@ -142,10 +142,10 @@ Py::Dict CosmeticEdgePy::getFormat() const
TechDraw::LineFormat* format= &(this->getCosmeticEdgePtr()->m_format);
Py::Dict dict;
dict.setItem("style", Py::Long(format->m_style));
dict.setItem("weight", Py::Float(format->m_weight));
dict.setItem("color", Py::Tuple(DrawUtil::colorToPyTuple(format->m_color), true));
dict.setItem("visible", Py::Boolean(format->m_visible));
dict.setItem("style", Py::Long(format->getStyle()));
dict.setItem("weight", Py::Float(format->getWidth()));
dict.setItem("color", Py::Tuple(DrawUtil::colorToPyTuple(format->getColor()), true));
dict.setItem("visible", Py::Boolean(format->getVisible()));
return dict;
}

View File

@@ -62,6 +62,35 @@ TechDraw::DrawViewPart* CosmeticExtension::getOwner()
return static_cast<TechDraw::DrawViewPart*>(getExtendedObject());
}
//! remove cosmetic elements for a list of subelement names
void CosmeticExtension::deleteCosmeticElements(std::vector<std::string> removables)
{
// Base::Console().Message("CEx::deleteCosmeticElements(%d removables)\n", removables.size());
for (auto& name : removables) {
if (DU::getGeomTypeFromName(name) == "Vertex" &&
DU::isCosmeticVertex(getOwner(), name)) {
CosmeticVertex* vert = getCosmeticVertexBySelection(name);
removeCosmeticVertex(vert->getTagAsString());
continue;
}
if (DU::getGeomTypeFromName(name) == "Edge" &&
( DU::isCosmeticEdge(getOwner(), name) ||
DU::isCenterLine(getOwner(), name) ) ) {
CosmeticEdge* edge = getCosmeticEdgeBySelection(name);
if (edge) {
// if not edge, something has gone very wrong!
removeCosmeticEdge(edge->getTagAsString());
continue;
}
CenterLine* line = getCenterLineBySelection(name);
if (line) {
removeCenterLine(line->getTagAsString());
continue;
}
}
}
}
//==============================================================================
//CosmeticVertex x, y are stored as unscaled, but mirrored (inverted Y) values.
//if you are creating a CV based on calculations of scaled geometry, you need to
@@ -161,13 +190,18 @@ int CosmeticExtension::getCVIndex(const std::string& tag)
}
/// adds a cosmetic vertex to the property list. does not add to display geometry until dvp executes.
/// returns unique CV id
std::string CosmeticExtension::addCosmeticVertex(const Base::Vector3d& pos)
/// returns unique CV id. if the pos parameter is in real world coordinates, then invert should be true
/// (the default). if pos is in TD geometry or scene coordinates, then it is already inverted, and
/// invert should be set to false.
std::string CosmeticExtension::addCosmeticVertex(const Base::Vector3d& pos, bool invert)
{
// Base::Console().Message("CEx::addCosmeticVertex(%s)\n",
// DrawUtil::formatVector(pos).c_str());
std::vector<CosmeticVertex*> verts = CosmeticVertexes.getValues();
Base::Vector3d tempPos = DrawUtil::invertY(pos);
Base::Vector3d tempPos = pos;
if (invert) {
tempPos = DrawUtil::invertY(pos);
}
TechDraw::CosmeticVertex* cv = new TechDraw::CosmeticVertex(tempPos);
verts.push_back(cv);
CosmeticVertexes.setValues(verts);
@@ -393,6 +427,8 @@ void CosmeticExtension::removeCosmeticEdge(const std::string& delTag)
/// remove the cosmetic edges with the given tags from the list property
void CosmeticExtension::removeCosmeticEdge(const std::vector<std::string>& delTags)
{
// Base::Console().Message("DVP::removeCE(%d tages)\n", delTags.size());
std::vector<CosmeticEdge*> cEdges = CosmeticEdges.getValues();
for (auto& t: delTags) {
removeCosmeticEdge(t);
}

View File

@@ -56,7 +56,7 @@ public:
virtual CosmeticVertex* getCosmeticVertex(const std::string& tag) const;
virtual int add1CVToGV(const std::string& tag);
virtual int getCVIndex(const std::string& tag);
virtual std::string addCosmeticVertex(const Base::Vector3d& pos);
virtual std::string addCosmeticVertex(const Base::Vector3d& pos, bool invert = true);
virtual void addCosmeticVertexesToGeom();
virtual void clearCosmeticVertexes();
virtual void refreshCVGeoms();
@@ -95,6 +95,8 @@ public:
virtual void removeGeomFormat(const std::string& tag);
virtual void clearGeomFormats();
void deleteCosmeticElements(std::vector<std::string> removables);
TechDraw::DrawViewPart* getOwner();
PyObject* getExtensionPyObject() override;

View File

@@ -21,6 +21,8 @@
* *
***************************************************************************/
//! CosmeticVertex point is stored in unscaled, unrotated form
#include "PreCompiled.h"
#ifndef _PreComp_
#include <boost/uuid/uuid_generators.hpp>
@@ -46,14 +48,9 @@ TYPESYSTEM_SOURCE(TechDraw::CosmeticVertex, Base::Persistence)
CosmeticVertex::CosmeticVertex() : TechDraw::Vertex()
{
point(Base::Vector3d(0.0, 0.0, 0.0));
permaPoint = Base::Vector3d(0.0, 0.0, 0.0);
linkGeom = -1;
color = Preferences::vertexColor();
size = Preferences::vertexScale() *
LineGroup::getDefaultWidth("Thin");
style = 1;
visible = true;
hlrVisible = true;
cosmetic = true;
@@ -76,7 +73,6 @@ CosmeticVertex::CosmeticVertex(const TechDraw::CosmeticVertex* cv) : TechDraw::V
CosmeticVertex::CosmeticVertex(const Base::Vector3d& loc) : TechDraw::Vertex(loc)
{
// Base::Console().Message("CV::CV(%s)\n", DU::formatVector(loc).c_str());
permaPoint = loc;
linkGeom = -1;
color = Preferences::vertexColor();
@@ -93,21 +89,17 @@ CosmeticVertex::CosmeticVertex(const Base::Vector3d& loc) : TechDraw::Vertex(loc
void CosmeticVertex::move(const Base::Vector3d& newPos)
{
permaPoint = newPos;
point(newPos);
}
void CosmeticVertex::moveRelative(const Base::Vector3d& movement)
{
permaPoint += movement;
point( point() += movement);
}
std::string CosmeticVertex::toString() const
{
std::stringstream ss;
ss << permaPoint.x << ", " <<
permaPoint.y << ", " <<
permaPoint.z << ", " <<
" / ";
ss << point().x << ", " <<
point().y << ", " <<
point().z << ", " <<
@@ -181,13 +173,15 @@ Base::Vector3d CosmeticVertex::scaled(const double factor)
return permaPoint * factor;
}
//! returns a transformed version of our coordinates (permaPoint)
Base::Vector3d CosmeticVertex::rotatedAndScaled(const double scale, const double rotDegrees)
{
Base::Vector3d scaledPoint = scaled(scale);
if (rotDegrees != 0.0) {
// invert the Y coordinate so the rotation math works out
// the stored point is inverted
scaledPoint = DU::invertY(scaledPoint);
scaledPoint.RotateZ(rotDegrees * M_PI / 180.0);
scaledPoint.RotateZ(rotDegrees * M_PI / DegreesHalfCircle);
scaledPoint = DU::invertY(scaledPoint);
}
return scaledPoint;
@@ -198,13 +192,12 @@ Base::Vector3d CosmeticVertex::rotatedAndScaled(const double scale, const double
//! inverted back on return.
Base::Vector3d CosmeticVertex::makeCanonicalPoint(DrawViewPart* dvp, Base::Vector3d point, bool unscale)
{
// Base::Console().Message("CV::makeCanonicalPoint(%s)\n", DU::formatVector(point).c_str());
double rotDeg = dvp->Rotation.getValue();
Base::Vector3d result = point;
if (rotDeg != 0.0) {
// unrotate the point
double rotRad = rotDeg * M_PI / 180.0;
double rotRad = rotDeg * M_PI / DegreesHalfCircle;
// we always rotate around the origin.
result.RotateZ(-rotRad);
}
@@ -219,6 +212,16 @@ Base::Vector3d CosmeticVertex::makeCanonicalPoint(DrawViewPart* dvp, Base::Vecto
return result;
}
//! a version of makeCanonicalPoint that accepts and returns an invertedPoint.
Base::Vector3d CosmeticVertex::makeCanonicalPointInverted(DrawViewPart* dvp, Base::Vector3d invertedPoint, bool unscale)
{
Base::Vector3d result = makeCanonicalPoint(dvp,
DU::invertY(invertedPoint),
unscale);
return DU::invertY(result);
}
boost::uuids::uuid CosmeticVertex::getTag() const
{
return tag;

View File

@@ -47,6 +47,8 @@ public:
CosmeticVertex(const Base::Vector3d& loc);
~CosmeticVertex() override = default;
Base::Vector3d point() const { return permaPoint; };
void point(Base::Vector3d newPoint) { permaPoint = newPoint; }
void move(const Base::Vector3d& newPos);
void moveRelative(const Base::Vector3d& movement);
@@ -56,6 +58,7 @@ public:
Base::Vector3d rotatedAndScaled(const double scale, const double rotDegrees);
static Base::Vector3d makeCanonicalPoint(DrawViewPart* dvp, Base::Vector3d point, bool unscale = true);
static Base::Vector3d makeCanonicalPointInverted(DrawViewPart* dvp, Base::Vector3d invertedPoint, bool unscale = true);
static bool restoreCosmetic();
// Persistence implementer ---------------------
@@ -67,13 +70,13 @@ public:
CosmeticVertex* copy() const;
CosmeticVertex* clone() const;
Base::Vector3d permaPoint; //permanent, unscaled value
int linkGeom; //connection to corresponding "geom" Vertex (fragile - index based!)
Base::Vector3d permaPoint{Base::Vector3d()}; //permanent, unscaled value
int linkGeom{-1}; //connection to corresponding "geom" Vertex (fragile - index based!)
//better to do reverse search for CosmeticTag in vertex geometry
App::Color color;
double size;
int style;
bool visible; //base class vertex also has visible property
App::Color color{App::Color()};
double size{1.0};
int style{1};
bool visible{true}; //base class vertex also has visible property
boost::uuids::uuid getTag() const;
std::string getTagAsString() const override;

View File

@@ -20,19 +20,29 @@
* *
***************************************************************************/
//! DrawLeaderLine - a class for storing leader line attributes and providing methods to apply transformations on leader geometry.
//! Waypoints are to be stored as displacements from the first Waypoint in printed page coordinates (mm, conventional
//! X and Y axes, (0, 0) at lower left). The first Waypoint is set to (0,0) after the displacements are calculated.
//! The leader's X,Y position is relative to the parent's origin. The X,Y position is unrotated and unscaled.
#include "PreCompiled.h"
#include <App/Document.h>
#include <Base/Console.h>
#include <Base/Parameter.h>
#include "DrawViewPart.h"
#include "DrawPage.h"
#include "DrawLeaderLine.h"
#include "DrawLeaderLinePy.h" // generated from DrawLeaderLinePy.xml
#include "ArrowPropEnum.h"
#include "DrawView.h"
#include "Preferences.h"
#include "DrawUtil.h"
using namespace TechDraw;
using DU = DrawUtil;
//===========================================================================
// DrawLeaderLine - Base class for drawing leader based features
@@ -66,26 +76,26 @@ DrawLeaderLine::DrawLeaderLine()
{
static const char *group = "Leader";
constexpr long int FilledArrow{0l};
constexpr long int NoEnd{7l};
ADD_PROPERTY_TYPE(LeaderParent, (nullptr), group, (App::PropertyType)(App::Prop_None),
"View to which this leader is attached");
LeaderParent.setScope(App::LinkScope::Global);
ADD_PROPERTY_TYPE(WayPoints, (Base::Vector3d()) ,group, App::Prop_None,
"Intermediate points for Leader line");
// EndType.setEnums(ArrowTypeEnums);
// ADD_PROPERTY(EndType, (prefEnd()));
StartSymbol.setEnums(ArrowPropEnum::ArrowTypeEnums);
ADD_PROPERTY(StartSymbol, (0l)); //filled arrow
ADD_PROPERTY(StartSymbol, (FilledArrow)); //filled arrow
// ADD_PROPERTY_TYPE(StartSymbol, (0), group, App::Prop_None, "Symbol (arrowhead) for start of line");
EndSymbol.setEnums(ArrowPropEnum::ArrowTypeEnums);
ADD_PROPERTY(EndSymbol, (7l)); //no symbol
// ADD_PROPERTY_TYPE(EndSymbol, (0), group, App::Prop_None, "Symbol (arrowhead) for end of line");
ADD_PROPERTY(EndSymbol, (NoEnd)); //no symbol
ADD_PROPERTY_TYPE(Scalable ,(false), group, App::Prop_None, "Scale line with LeaderParent");
ADD_PROPERTY_TYPE(AutoHorizontal ,(getDefAuto()), group, App::Prop_None, "Forces last line segment to be horizontal");
ADD_PROPERTY_TYPE(RotatesWithParent ,(true), group, App::Prop_None,
"If true, leader rotates around parent. If false, only first segment of leader changes with parent rotation.");
//hide the DrawView properties that don't apply to Leader
ScaleType.setStatus(App::Property::ReadOnly, true);
@@ -108,12 +118,16 @@ void DrawLeaderLine::onChanged(const App::Property* prop)
short DrawLeaderLine::mustExecute() const
{
if (!isRestoring() && LeaderParent.isTouched()) {
return true; // Property changed
return 1; // Property changed
}
const App::DocumentObject* docObj = getBaseObject();
if (docObj && docObj->isTouched()) {
return true; // Object property points to is touched
return 1; // Object property points to is touched
}
if (WayPoints.isTouched()) {
return 1;
}
return DrawView::mustExecute();
@@ -121,11 +135,13 @@ short DrawLeaderLine::mustExecute() const
App::DocumentObjectExecReturn *DrawLeaderLine::execute()
{
// Base::Console().Message("DLL::execute()\n");
// Base::Console().Message("DLL::execute()\n");
if (!keepUpdated()) {
return App::DocumentObject::StdReturn;
}
adjustLastSegment();
// is horizLastSegment something that should be done only at draw time?
horizLastSegment();
overrideKeepUpdated(false);
return DrawView::execute();
}
@@ -137,7 +153,7 @@ DrawView* DrawLeaderLine::getBaseView() const
return nullptr;
}
DrawView* cast = dynamic_cast<DrawView*>(baseObj);
auto cast = dynamic_cast<DrawView*>(baseObj);
return cast;
}
@@ -155,8 +171,6 @@ bool DrawLeaderLine::keepUpdated()
return view->keepUpdated();
}
//need separate getParentScale()???
double DrawLeaderLine::getBaseScale() const
{
// Base::Console().Message("DLL::getBaseScale()\n");
@@ -174,11 +188,7 @@ double DrawLeaderLine::getScale() const
return 1.0;
}
DrawView* parent = getBaseView();
if (!parent) {
return 1.0;
}
return parent->getScale();
return getBaseScale();
}
Base::Vector3d DrawLeaderLine::getAttachPoint()
@@ -190,23 +200,37 @@ Base::Vector3d DrawLeaderLine::getAttachPoint()
);
}
void DrawLeaderLine::adjustLastSegment()
//! unit agnostic conversion of last segment to horizontal. need to do this at drawing time otherwise
//! we just realign the canonical form.
void DrawLeaderLine::horizLastSegment()
{
// Base::Console().Message("DLL::adjustLastSegment()\n");
bool adjust = AutoHorizontal.getValue();
std::vector<Base::Vector3d> wp = WayPoints.getValues();
if (adjust && wp.size() > 1) {
int iLast = wp.size() - 1;
int iPen = wp.size() - 2;
// Base::Console().Message("DLL::horizLastSegment() - auto: %d\n", AutoHorizontal.getValue());
bool adjust = AutoHorizontal.getValue();
if (!adjust) {
return;
}
auto temp = horizLastSegment(WayPoints.getValues());
WayPoints.setValues(temp);
}
std::vector<Base::Vector3d> DrawLeaderLine::horizLastSegment(const std::vector<Base::Vector3d>& inDeltas)
{
// Base::Console().Message("DLL::horizLastSegment(in: %d)\n", inDeltas.size());
std::vector<Base::Vector3d> wp = inDeltas;
if (wp.size() > 1) {
size_t iLast = wp.size() - 1;
size_t iPen = wp.size() - 2;
Base::Vector3d last = wp.at(iLast);
Base::Vector3d penUlt = wp.at(iPen);
last.y = penUlt.y;
wp.at(iLast) = last;
}
WayPoints.setValues(wp);
return wp;
}
//middle of last line segment
//! returns the mid point of last segment. used by leader decorators like weld symbol.
Base::Vector3d DrawLeaderLine::getTileOrigin() const
{
std::vector<Base::Vector3d> wp = WayPoints.getValues();
@@ -215,17 +239,17 @@ Base::Vector3d DrawLeaderLine::getTileOrigin() const
Base::Vector3d second = wp.rbegin()[1];
return (last + second) / 2.0;
}
Base::Console().Warning("DLL::getTileOrigin - no waypoints\n");
return Base::Vector3d();
}
//start of last line segment
//! returns start of last line segment
Base::Vector3d DrawLeaderLine::getKinkPoint() const
{
std::vector<Base::Vector3d> wp = WayPoints.getValues();
if (wp.size() > 1) {
return wp.rbegin()[1]; // Second
return wp.rbegin()[1]; // second point from end
}
Base::Console().Warning("DLL::getKinkPoint - no waypoints\n");
@@ -239,12 +263,180 @@ Base::Vector3d DrawLeaderLine::getTailPoint() const
if (!wp.empty()) {
return wp.rbegin()[0]; // Last
}
Base::Console().Warning("DLL::getTailPoint - no waypoints\n");
return Base::Vector3d();
}
//! create a new leader feature from parameters. Used by python method makeLeader.
//! pagePoints are in mm from bottom left of page.
DrawLeaderLine* DrawLeaderLine::makeLeader(DrawViewPart* parent, std::vector<Base::Vector3d> pagePoints, int iStartSymbol, int iEndSymbol)
{
// Base::Console().Message("DLL::makeLeader(%s, %d, %d, %d)\n", parent->getNameInDocument(), pagePoints.size(), iStartSymbol, iEndSymbol);
if (pagePoints.size() < 2) {
Base::Console().Message("DLL::makeLeader - not enough pagePoints\n");
return {};
}
// this is +/- the same code as in TaskLeaderLine::createLeaderFeature()
const std::string objectName{"LeaderLine"};
const std::string leaderType = "TechDraw::DrawLeaderLine";
std::string leaderName = parent->getDocument()->getUniqueObjectName(objectName.c_str());
std::string pageName = parent->findParentPage()->getNameInDocument();
std::string parentName = parent->getNameInDocument();
Base::Interpreter().runStringArg("App.activeDocument().addObject('%s', '%s')",
leaderType.c_str(), leaderName.c_str());
Base::Interpreter().runStringArg("App.activeDocument().%s.translateLabel('DrawLeaderLine', 'LeaderLine', '%s')",
leaderName.c_str(), leaderName.c_str());
Base::Interpreter().runStringArg("App.activeDocument().%s.addView(App.activeDocument().%s)",
pageName.c_str(), leaderName.c_str());
Base::Interpreter().runStringArg("App.activeDocument().%s.LeaderParent = App.activeDocument().%s",
leaderName.c_str(), parentName.c_str());
// we assume here that the caller will handle AutoHorizontal, Scalable and Rotatable as required
App::DocumentObject* obj = parent->getDocument()->getObject(leaderName.c_str());
if (!obj || !obj->isDerivedFrom(TechDraw::DrawLeaderLine::getClassTypeId())) {
throw Base::RuntimeError("DrawLeaderLine::makeLeader - new object not found");
}
// set leader x,y position
auto leaderFeature = static_cast<TechDraw::DrawLeaderLine*>(obj);
Base::Vector3d parentPagePos{ parent->X.getValue(), parent->Y.getValue(), 0.0};
Base::Vector3d leaderPagePos = pagePoints.front() - parentPagePos;
bool force = true; // update position even though leaders default to locked.
leaderFeature->setPosition(leaderPagePos.x, leaderPagePos.y, force);
// page positions to deltas
std::vector<Base::Vector3d> pageDeltas;
for (auto& point : pagePoints) {
auto temp = point - pagePoints.front();
pageDeltas.emplace_back(temp);
}
// deltas to unscaled, unrotated form
auto leaderPoints = leaderFeature->makeCanonicalPoints(pageDeltas);
// invert the canonical points
std::vector<Base::Vector3d> inverted;
inverted.reserve(leaderPoints.size());
for (auto& point : leaderPoints) {
inverted.push_back(DU::invertY(point));
}
leaderFeature->WayPoints.setValues(inverted);
leaderFeature->StartSymbol.setValue(iStartSymbol);
leaderFeature->EndSymbol.setValue(iEndSymbol);
parent->touch();
return leaderFeature;
}
//! return scaled and rotated copies of the WayPoints (page position deltas from 1st point)
//! used by QGILL.
std::vector<Base::Vector3d> DrawLeaderLine::getScaledAndRotatedPoints(bool doScale, bool doRotate) const
{
// Base::Console().Message("DLL::getScaledAndRotatedPoints(%d, %d)\n", doScale, doRotate);
auto dvp = getBaseView();
if (!dvp) {
// document is restoring?
// Base::Console().Message("DLL::getScaledAndRotatedPoints - no DV\n");
return {};
}
double scale{1.0};
if (Scalable.getValue() && doScale) {
scale = dvp->getScale();
}
double rotationRad{0.0};
if (doRotate) {
rotationRad = dvp->Rotation.getValue() * M_PI / DegreesHalfCircle;
}
std::vector<Base::Vector3d> pointsAll = WayPoints.getValues();
std::vector<Base::Vector3d> result;
for (auto& point : pointsAll) {
Base::Vector3d newPoint = DU::invertY(point * scale);
if (rotationRad != 0.0) {
// the waypoints use conventional coords
newPoint.RotateZ(rotationRad);
}
result.push_back(DU::invertY(newPoint));
}
return result;
}
//! return unscaled and unrotated versions of the input points. input is expected to in mm (Rez::appX()),
//! and conventional Y axis (+ up)
//! used by QGILL.
std::vector<Base::Vector3d>
DrawLeaderLine::makeCanonicalPoints(const std::vector<Base::Vector3d>& inPoints,
bool doScale,
bool doRotate) const
{
// Base::Console().Message("DLL::makeCanonicalPoints(%d, %d, %d)\n", inPoints.size(), doScale, doRotate);
auto dvp = getBaseView();
double scale{1.0};
if (Scalable.getValue() && doScale) {
scale = dvp->getScale();
}
double rotationRad{0.0};
if (doRotate) {
rotationRad = - dvp->Rotation.getValue() * M_PI / DegreesHalfCircle;
}
std::vector<Base::Vector3d> result;
for (auto& point : inPoints) {
Base::Vector3d newPoint = point / scale;
if (rotationRad != 0.0) {
newPoint.RotateZ(rotationRad);
}
result.push_back(newPoint);
}
return result;
}
//! as makeCanonicalPoints, but accepts and returns inverted points
std::vector<Base::Vector3d>
DrawLeaderLine::makeCanonicalPointsInverted(const std::vector<Base::Vector3d>& inPoints,
bool doScale,
bool doRotate) const
{
std::vector<Base::Vector3d> conventionalPoints;
conventionalPoints.reserve(inPoints.size());
for (auto& point : inPoints) {
conventionalPoints.push_back(DU::invertY(point));
}
auto conventionalCanon = makeCanonicalPoints(inPoints, doScale, doRotate);
std::vector<Base::Vector3d> invertedPoints;
invertedPoints.reserve(inPoints.size());
for (auto& point : conventionalCanon) {
invertedPoints.push_back(DU::invertY(point));
}
return invertedPoints;
}
//! returns true if parent exists. if parent is a DVP it must have geometry.
bool DrawLeaderLine::isParentReady() const
{
TechDraw::DrawView* parent = getBaseView();
auto dvp = dynamic_cast<TechDraw::DrawViewPart*>(parent);
if (!parent || (dvp && !dvp->hasGeometry())) {
// still restoring or
// we are attached to a dvp that has no geometry, so don't bother trying to draw yet
Base::Console().Message("DLL:: - no parent or geometry\n");
return false;
}
return true;
}
bool DrawLeaderLine::getDefAuto() const
{
return Preferences::getPreferenceGroup("LeaderLine")->GetBool("AutoHorizontal", true);

View File

@@ -33,6 +33,7 @@
namespace TechDraw
{
class DrawViewPart;
class TechDrawExport DrawLeaderLine : public TechDraw::DrawView
{
@@ -47,14 +48,13 @@ public:
App::PropertyEnumeration StartSymbol;
App::PropertyEnumeration EndSymbol;
/* App::PropertyInteger StartSymbol; //see Gui/QGIArrow for values*/
/* App::PropertyInteger EndSymbol;*/
App::PropertyBool Scalable;
App::PropertyBool AutoHorizontal;
App::PropertyBool RotatesWithParent;
short mustExecute() const override;
App::DocumentObjectExecReturn *execute() override;
App::PropertyLink *getOwnerProperty() override { return &LeaderParent; }
const char* getViewProviderName() const override {
return "TechDrawGui::ViewProviderLeader";
@@ -65,18 +65,29 @@ public:
Base::Vector3d getAttachPoint();
DrawView* getBaseView() const;
virtual App::DocumentObject* getBaseObject() const;
App::PropertyLink *getOwnerProperty() override { return &LeaderParent; }
bool keepUpdated() override;
double getScale() const override;
double getBaseScale() const;
void adjustLastSegment();
void horizLastSegment();
static std::vector<Base::Vector3d> horizLastSegment(const std::vector<Base::Vector3d>& inDeltas);
bool getDefAuto() const;
Base::Vector3d getTileOrigin() const;
Base::Vector3d getKinkPoint() const;
Base::Vector3d getTailPoint() const;
static DrawLeaderLine* makeLeader(DrawViewPart* parent, std::vector<Base::Vector3d> points, int iStartSymbol = 0, int iEndSymbol = 0);
std::vector<Base::Vector3d> getScaledAndRotatedPoints(bool doScale = true, bool doRotate = true) const;
std::vector<Base::Vector3d> makeCanonicalPoints(const std::vector<Base::Vector3d>& inPoints,
bool doScale = true,
bool doRotate = true) const;
std::vector<Base::Vector3d> makeCanonicalPointsInverted(const std::vector<Base::Vector3d>& inPoints,
bool doScale = true,
bool doRotate = true) const;
bool isParentReady() const;
protected:
void onChanged(const App::Property* prop) override;

View File

@@ -52,6 +52,8 @@
#define M_2PI ((M_PI)*2.0)
#endif
constexpr double DegreesHalfCircle{180.0};
#define VERTEXTOLERANCE (2.0 * Precision::Confusion())
#define VECTORTOLERANCE (Precision::Confusion())

View File

@@ -52,7 +52,7 @@
using namespace TechDraw;
using DU = DrawUtil;
// returns a string which represents the object e.g. when printed in python
std::string DrawViewPartPy::representation() const
{
@@ -229,6 +229,7 @@ PyObject* DrawViewPartPy::makeCosmeticVertex(PyObject *args)
return PyUnicode_FromString(id.c_str()); //return tag for new CV
}
//! make a cosmetic vertex from a 3d point
PyObject* DrawViewPartPy::makeCosmeticVertex3d(PyObject *args)
{
PyObject* pPnt1 = nullptr;
@@ -239,9 +240,12 @@ PyObject* DrawViewPartPy::makeCosmeticVertex3d(PyObject *args)
DrawViewPart* dvp = getDrawViewPartPtr();
Base::Vector3d pnt1 = static_cast<Base::VectorPy*>(pPnt1)->value();
Base::Vector3d centroid = dvp->getOriginalCentroid();
// center the point
pnt1 = pnt1 - centroid;
Base::Vector3d projected = DrawUtil::invertY(dvp->projectPoint(pnt1));
projected = CosmeticVertex::makeCanonicalPoint(dvp, projected);
// project but do not invert
Base::Vector3d projected = dvp->projectPoint(pnt1);
// this is a real world point, it is not scaled or rotated, so so it is in canonical form
// add and invert the point.
std::string id = dvp->addCosmeticVertex(projected);
//int link =
dvp->add1CVToGV(id);
@@ -341,6 +345,8 @@ PyObject* DrawViewPartPy::removeCosmeticVertex(PyObject *args)
PyObject* DrawViewPartPy::makeCosmeticLine(PyObject *args)
{
// the input points are expected to use conventional coordinates (Y up) and need to be inverted
// before building the line
PyObject* pPnt1 = nullptr;
PyObject* pPnt2 = nullptr;
int style = LineFormat::getDefEdgeStyle();
@@ -358,12 +364,12 @@ PyObject* DrawViewPartPy::makeCosmeticLine(PyObject *args)
DrawViewPart* dvp = getDrawViewPartPtr();
Base::Vector3d pnt1 = static_cast<Base::VectorPy*>(pPnt1)->value();
Base::Vector3d pnt2 = static_cast<Base::VectorPy*>(pPnt2)->value();
std::string newTag = dvp->addCosmeticEdge(pnt1, pnt2);
std::string newTag = dvp->addCosmeticEdge(DU::invertY(pnt1), DU::invertY(pnt2));
TechDraw::CosmeticEdge* ce = dvp->getCosmeticEdge(newTag);
if (ce) {
ce->m_format.m_style = style;
ce->m_format.m_weight = weight;
ce->m_format.m_color = pColor ? DrawUtil::pyTupleToColor(pColor) : defCol;
ce->m_format.setStyle(style);
ce->m_format.setWidth(weight);
ce->m_format.setColor(pColor ? DrawUtil::pyTupleToColor(pColor) : defCol);
}
else {
PyErr_SetString(PyExc_RuntimeError, "DVPPI:makeCosmeticLine - line creation failed");
@@ -378,6 +384,7 @@ PyObject* DrawViewPartPy::makeCosmeticLine(PyObject *args)
PyObject* DrawViewPartPy::makeCosmeticLine3D(PyObject *args)
{
// input points are expected to be conventional 3d points
PyObject* pPnt1 = nullptr;
PyObject* pPnt2 = nullptr;
int style = LineFormat::getDefEdgeStyle();
@@ -397,18 +404,18 @@ PyObject* DrawViewPartPy::makeCosmeticLine3D(PyObject *args)
Base::Vector3d pnt1 = static_cast<Base::VectorPy*>(pPnt1)->value();
pnt1 = pnt1 - centroid;
pnt1 = DrawUtil::invertY(dvp->projectPoint(pnt1));
pnt1 = dvp->projectPoint(pnt1);
Base::Vector3d pnt2 = static_cast<Base::VectorPy*>(pPnt2)->value();
pnt2 = pnt2 - centroid;
pnt2 = DrawUtil::invertY(dvp->projectPoint(pnt2));
pnt2 = dvp->projectPoint(pnt2);
std::string newTag = dvp->addCosmeticEdge(pnt1, pnt2);
TechDraw::CosmeticEdge* ce = dvp->getCosmeticEdge(newTag);
if (ce) {
ce->m_format.m_style = style;
ce->m_format.m_weight = weight;
ce->m_format.m_color = pColor ? DrawUtil::pyTupleToColor(pColor) : defCol;
ce->m_format.setStyle(style);
ce->m_format.setWidth(weight);
ce->m_format.setColor(pColor ? DrawUtil::pyTupleToColor(pColor) : defCol);
}
else {
PyErr_SetString(PyExc_RuntimeError, "DVPPI:makeCosmeticLine - line creation failed");
@@ -444,9 +451,9 @@ PyObject* DrawViewPartPy::makeCosmeticCircle(PyObject *args)
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;
ce->m_format.setStyle(style);
ce->m_format.setWidth(weight);
ce->m_format.setColor(pColor ? DrawUtil::pyTupleToColor(pColor) : defCol);
}
else {
PyErr_SetString(PyExc_RuntimeError, "DVPPI:makeCosmeticCircle - circle creation failed");
@@ -484,12 +491,12 @@ PyObject* DrawViewPartPy::makeCosmeticCircleArc(PyObject *args)
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.setStyle(style);
ce->m_format.setWidth(weight);
if (!pColor)
ce->m_format.m_color = defCol;
ce->m_format.setColor(defCol);
else
ce->m_format.m_color = DrawUtil::pyTupleToColor(pColor);
ce->m_format.setColor(DrawUtil::pyTupleToColor(pColor));
}
else {
PyErr_SetString(PyExc_RuntimeError, "DVPPI:makeCosmeticCircleArc - arc creation failed");
@@ -529,9 +536,9 @@ PyObject* DrawViewPartPy::makeCosmeticCircle3d(PyObject *args)
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;
ce->m_format.setStyle(style);
ce->m_format.setWidth(weight);
ce->m_format.setColor(pColor ? DrawUtil::pyTupleToColor(pColor) : defCol);
}
else {
PyErr_SetString(PyExc_RuntimeError, "DVPPI:makeCosmeticCircle - circle creation failed");
@@ -572,12 +579,12 @@ PyObject* DrawViewPartPy::makeCosmeticCircleArc3d(PyObject *args)
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.setStyle(style);
ce->m_format.setWidth(weight);
if (!pColor)
ce->m_format.m_color = defCol;
ce->m_format.setColor(defCol);
else
ce->m_format.m_color = DrawUtil::pyTupleToColor(pColor);
ce->m_format.setColor(DrawUtil::pyTupleToColor(pColor));
}
else {
PyErr_SetString(PyExc_RuntimeError, "DVPPI:makeCosmeticCircleArc - arc creation failed");
@@ -763,10 +770,10 @@ PyObject* DrawViewPartPy::formatGeometricEdge(PyObject *args)
DrawViewPart* dvp = getDrawViewPartPtr();
TechDraw::GeomFormat* gf = dvp->getGeomFormatBySelection(idx);
if (gf) {
gf->m_format.m_style = style;
gf->m_format.m_color = color;
gf->m_format.m_weight = weight;
gf->m_format.m_visible = visible;
gf->m_format.setStyle(style);
gf->m_format.setColor(color);
gf->m_format.setWidth(weight);
gf->m_format.setVisible(visible);
}
else {
TechDraw::LineFormat fmt(style, weight, color, visible);

View File

@@ -621,7 +621,7 @@ std::vector<Base::Vector3d> BaseGeom::intersection(TechDraw::BaseGeomPtr geom2)
TopExp_Explorer explorer(sectionShape, TopAbs_VERTEX);
while (explorer.More()) {
Base::Vector3d pt(DrawUtil::toVector3d(BRep_Tool::Pnt(TopoDS::Vertex(explorer.Current()))));
interPoints.push_back(DrawUtil::invertY(pt));
interPoints.push_back(pt);
explorer.Next();
}
}
@@ -802,16 +802,20 @@ AOC::AOC(const TopoDS_Edge &e) : Circle(e)
gp_Vec v1(m, s); //vector mid to start
gp_Vec v2(m, ePt); //vector mid to end
gp_Vec v3(0, 0, 1); //stdZ
// this is the wrong determination of cw/ccw. needs to be determined by edge.
double a = v3.DotCross(v1, v2); //error if v1 = v2?
startAngle = fmod(f, 2.0*M_PI);
endAngle = fmod(l, 2.0*M_PI);
cw = (a < 0) ? true: false;
largeArc = (fabs(l-f) > M_PI) ? true : false;
startPnt = Base::Vector3d(s.X(), s.Y(), s.Z());
endPnt = Base::Vector3d(ePt.X(), ePt.Y(), s.Z());
midPnt = Base::Vector3d(m.X(), m.Y(), s.Z());
startPnt = DU::toVector3d(s);
endPnt = DU::toVector3d(ePt);
midPnt = DU::toVector3d(m);
if (e.Orientation() == TopAbs_REVERSED) {
reversed = true;
}
@@ -845,6 +849,11 @@ AOC::AOC(Base::Vector3d c, double r, double sAng, double eAng) : Circle()
gp_Vec v1(m, s); //vector mid to start
gp_Vec v2(m, ePt); //vector mid to end
gp_Vec v3(0, 0, 1); //stdZ
// this is a bit of an arcane method of determining if v2 is clockwise from v1 or counter clockwise from v1.
// The v1 x v2 points up if v2 is ccw from v1 and points down if v2 is cw from v1. Taking (v1 x v2) * stdZ
// gives 1 for parallel with stdZ (v2 is ccw from v1) or -1 for antiparallel with stdZ (v2 is clockwise from v1).
// this cw flag is a problem. we should just declare that arcs are always ccw and flip the start and end angles.
double a = v3.DotCross(v1, v2); //error if v1 = v2?
startAngle = fmod(f, 2.0*M_PI);
@@ -852,9 +861,9 @@ AOC::AOC(Base::Vector3d c, double r, double sAng, double eAng) : Circle()
cw = (a < 0) ? true: false;
largeArc = (fabs(l-f) > M_PI) ? true : false;
startPnt = Base::Vector3d(s.X(), s.Y(), s.Z());
endPnt = Base::Vector3d(ePt.X(), ePt.Y(), s.Z());
midPnt = Base::Vector3d(m.X(), m.Y(), s.Z());
startPnt = DU::toVector3d(s);
endPnt = DU::toVector3d(ePt);
midPnt = DU::toVector3d(m);
if (edge.Orientation() == TopAbs_REVERSED) {
reversed = true;
}
@@ -890,6 +899,22 @@ bool AOC::isOnArc(Base::Vector3d p)
return false;
}
BaseGeomPtr AOC::copy()
{
auto base = BaseGeom::copy();
TechDraw::CirclePtr circle = std::static_pointer_cast<TechDraw::Circle>(base);
TechDraw::AOCPtr aoc = std::static_pointer_cast<TechDraw::AOC>(circle);
if (aoc) {
aoc->clockwiseAngle(clockwiseAngle());
aoc->startPnt = startPnt;
aoc->startAngle = startAngle;
aoc->endPnt = endPnt;
aoc->endAngle = endAngle;
aoc->largeArc = largeArc;
}
return base;
}
double AOC::distToArc(Base::Vector3d p)
{
return minDist(p);

View File

@@ -113,7 +113,7 @@ class TechDrawExport BaseGeom : public std::enable_shared_from_this<BaseGeom>
static bool validateEdge(TopoDS_Edge edge);
static TopoDS_Edge completeEdge(const TopoDS_Edge &edge);
bool closed();
BaseGeomPtr copy();
virtual BaseGeomPtr copy();
std::string dump();
virtual std::string toString() const;
std::vector<Base::Vector3d> intersection(TechDraw::BaseGeomPtr geom2);
@@ -241,6 +241,7 @@ class TechDrawExport AOC: public Circle
~AOC() override = default;
public:
BaseGeomPtr copy() override;
double getStartAngle() override { return startAngle; }
double getEndAngle() override { return endAngle; }
bool clockwiseAngle() override { return cw; }

View File

@@ -624,14 +624,13 @@ void GeometryObject::addEdge(TechDraw::BaseGeomPtr bg) { edgeGeom.push_back(bg);
//adds a new GeomVert surrogate for CV
//returns GeomVert selection index ("Vertex3")
// insertGeomForCV(cv)
// is this ever used?
int GeometryObject::addCosmeticVertex(CosmeticVertex* cv)
{
// Base::Console().Message("GO::addCosmeticVertex(%X)\n", cv);
double scale = m_parent->getScale();
Base::Vector3d pos = cv->scaled(scale);
TechDraw::VertexPtr v(std::make_shared<TechDraw::Vertex>(pos.x, pos.y));
v->setCosmetic(true);
// v->setCosmeticLink = -1;//obs??
v->setCosmeticTag(cv->getTagAsString());
v->setHlrVisible(true);
int idx = vertexGeom.size();
@@ -643,7 +642,6 @@ int GeometryObject::addCosmeticVertex(CosmeticVertex* cv)
//should probably be called addVertex since not connect to CV by tag
int GeometryObject::addCosmeticVertex(Base::Vector3d pos)
{
Base::Console().Message("GO::addCosmeticVertex() 1 - deprec?\n");
TechDraw::VertexPtr v(std::make_shared<TechDraw::Vertex>(pos.x, pos.y));
v->setCosmetic(true);
v->setCosmeticTag("tbi");//not connected to CV
@@ -655,7 +653,6 @@ int GeometryObject::addCosmeticVertex(Base::Vector3d pos)
int GeometryObject::addCosmeticVertex(Base::Vector3d pos, std::string tagString)
{
// Base::Console().Message("GO::addCosmeticVertex() 2\n");
TechDraw::VertexPtr v(std::make_shared<TechDraw::Vertex>(pos.x, pos.y));
v->setCosmetic(true);
v->setCosmeticTag(tagString);//connected to CV

View File

@@ -0,0 +1,117 @@
// SPDX-License-Identifier: LGPL-2.0-or-later
/***************************************************************************
* Copyright (c) 2024 wandererfan <wandererfan at gmail dot 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_
#endif
#include "LineGroup.h"
#include "LineGenerator.h"
#include "Preferences.h"
#include "LineFormat.h"
using namespace TechDraw;
//! general purpose line format specifier
LineFormat::LineFormat()
{
m_style = getDefEdgeStyle();
m_weight = getDefEdgeWidth();
m_color= getDefEdgeColor();
m_visible = true;
m_lineNumber = LineGenerator::fromQtStyle((Qt::PenStyle)m_style);
}
// static loader of default format
void LineFormat::initCurrentLineFormat()
{
getCurrentLineFormat().setStyle(getDefEdgeStyle());
getCurrentLineFormat().setWidth(getDefEdgeWidth());
getCurrentLineFormat().setColor(getDefEdgeColor());
getCurrentLineFormat().setVisible(true);
getCurrentLineFormat().setLineNumber(LineGenerator::fromQtStyle((Qt::PenStyle)getCurrentLineFormat().getStyle()));
}
LineFormat& LineFormat::getCurrentLineFormat()
{
static TechDraw::LineFormat currentLineFormat;
return currentLineFormat;
}
void LineFormat::setCurrentLineFormat(LineFormat& newFormat)
{
getCurrentLineFormat().setStyle(newFormat.getStyle());
getCurrentLineFormat().setWidth(newFormat.getWidth());
getCurrentLineFormat().setColor(newFormat.getColor());
getCurrentLineFormat().setVisible(newFormat.getVisible());
getCurrentLineFormat().setLineNumber(newFormat.getLineNumber());
}
LineFormat::LineFormat(const int style,
const double weight,
const App::Color& color,
const bool visible) :
m_style(style),
m_weight(weight),
m_color(color),
m_visible(visible),
m_lineNumber(LineGenerator::fromQtStyle((Qt::PenStyle)m_style))
{
}
void LineFormat::dump(const char* title)
{
Base::Console().Message("LF::dump - %s \n", title);
Base::Console().Message("LF::dump - %s \n", toString().c_str());
}
std::string LineFormat::toString() const
{
std::stringstream ss;
ss << m_style << ", " <<
m_weight << ", " <<
m_color.asHexString() << ", " <<
m_visible;
return ss.str();
}
//static preference getters.
double LineFormat::getDefEdgeWidth()
{
return TechDraw::LineGroup::getDefaultWidth("Graphic");
}
App::Color LineFormat::getDefEdgeColor()
{
return Preferences::normalColor();
}
int LineFormat::getDefEdgeStyle()
{
return Preferences::getPreferenceGroup("Decorations")->GetInt("CenterLineStyle", 2); //dashed
}

View File

@@ -0,0 +1,96 @@
// SPDX-License-Identifier: LGPL-2.0-or-later
/***************************************************************************
* Copyright (c) 2024 wandererfan <wandererfan at gmail dot com> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#ifndef TECHDRAW_LINEFORMAT_H
#define TECHDRAW_LINEFORMAT_H
#include <Mod/TechDraw/TechDrawGlobal.h>
#include <QColor>
#include <App/Color.h>
namespace TechDraw {
//! general purpose line format specifier
class TechDrawExport LineFormat
{
public:
static constexpr size_t InvalidLine{0};
LineFormat();
LineFormat(const int style,
const double weight,
const App::Color& color,
const bool visible,
const int lineNumber);
// TODO: phase out the old 4 parameter constructor
LineFormat(const int style,
const double weight,
const App::Color& color,
const bool visible);
~LineFormat() = default;
// style was used to specify QPen styles. line number (from LineGenerator) should be used now.
int getStyle() const { return m_style; }
void setStyle(int style) { m_style = style; }
double getWidth() const { return m_weight; }
void setWidth(double width) {m_weight = width; }
App::Color getColor() const { return m_color; }
void setColor(App::Color color) { m_color = color; }
QColor getQColor() const { return m_color.asValue<QColor>(); }
void setQColor(QColor qColor) { m_color.set(qColor.redF(), qColor.greenF(), qColor.blueF(), 1.0 - qColor.alphaF()); }
bool getVisible() const { return m_visible; }
void setVisible(bool viz) { m_visible = viz; }
int getLineNumber() const { return m_lineNumber; }
void setLineNumber(int number) { m_lineNumber = number; }
static double getDefEdgeWidth();
static App::Color getDefEdgeColor();
static int getDefEdgeStyle();
void dump(const char* title);
std::string toString() const;
static void initCurrentLineFormat();
static LineFormat& getCurrentLineFormat();
static void setCurrentLineFormat(LineFormat& newformat);
private:
int m_style;
double m_weight;
App::Color m_color;
bool m_visible;
int m_lineNumber {1};
};
} //end namespace TechDraw
#endif //TECHDRAW_LINEFORMAT_H

View File

@@ -27,6 +27,8 @@
#include <Mod/TechDraw/TechDrawGlobal.h>
#include <string>
#include <vector>
#include <string>
namespace TechDraw