Complete remake of drawing dimensions
This commit is contained in:
@@ -622,6 +622,362 @@ PyObject* DrawUtil::colorToPyTuple(App::Color color)
|
||||
return pTuple;
|
||||
}
|
||||
|
||||
// Supplementary mathematical functions
|
||||
// ====================================
|
||||
|
||||
int DrawUtil::sgn(double x)
|
||||
{
|
||||
return (x > +Precision::Confusion()) - (x < -Precision::Confusion());
|
||||
}
|
||||
|
||||
double DrawUtil::sqr(double x)
|
||||
{
|
||||
return x*x;
|
||||
}
|
||||
|
||||
void DrawUtil::angleNormalize(double &fi)
|
||||
{
|
||||
while (fi <= -M_PI) {
|
||||
fi += M_2PI;
|
||||
}
|
||||
while (fi > M_PI) {
|
||||
fi -= M_2PI;
|
||||
}
|
||||
}
|
||||
|
||||
double DrawUtil::angleComposition(double fi, double delta)
|
||||
{
|
||||
fi += delta;
|
||||
|
||||
angleNormalize(fi);
|
||||
return fi;
|
||||
}
|
||||
|
||||
double DrawUtil::angleDifference(double fi1, double fi2, bool reflex)
|
||||
{
|
||||
angleNormalize(fi1);
|
||||
angleNormalize(fi2);
|
||||
|
||||
fi1 -= fi2;
|
||||
|
||||
if (((fi1 > +M_PI) || (fi1 <= -M_PI)) != reflex) {
|
||||
fi1 += fi1 > 0.0 ? -M_2PI : +M_2PI;
|
||||
}
|
||||
|
||||
return fi1;
|
||||
}
|
||||
|
||||
// Interval marking functions
|
||||
// ==========================
|
||||
|
||||
unsigned int DrawUtil::intervalMerge(std::vector<std::pair<double, bool>> &marking,
|
||||
double boundary, bool wraps)
|
||||
{
|
||||
// We will be returning the placement index instead of an iterator, because indices
|
||||
// are still valid after we insert on higher positions, while iterators may be invalidated
|
||||
// due to the insertion triggered reallocation
|
||||
unsigned int i = 0;
|
||||
bool last = false;
|
||||
|
||||
if (wraps && marking.size() > 0) {
|
||||
last = marking.back().second;
|
||||
}
|
||||
|
||||
while (i < marking.size()) {
|
||||
if (marking[i].first == boundary) {
|
||||
return i;
|
||||
}
|
||||
if (marking[i].first > boundary) {
|
||||
break;
|
||||
}
|
||||
|
||||
last = marking[i].second;
|
||||
++i;
|
||||
}
|
||||
|
||||
if (!wraps && i >= marking.size()) {
|
||||
last = false;
|
||||
}
|
||||
|
||||
marking.insert(marking.begin() + i, std::pair<double, bool>(boundary, last));
|
||||
return i;
|
||||
}
|
||||
|
||||
void DrawUtil::intervalMarkLinear(std::vector<std::pair<double, bool>> &marking,
|
||||
double start, double length, bool value)
|
||||
{
|
||||
if (length == 0.0) {
|
||||
return;
|
||||
}
|
||||
if (length < 0.0) {
|
||||
length = -length;
|
||||
start -= length;
|
||||
}
|
||||
|
||||
unsigned int startIndex = intervalMerge(marking, start, false);
|
||||
unsigned int endIndex = intervalMerge(marking, start + length, false);
|
||||
|
||||
while (startIndex < endIndex) {
|
||||
marking[startIndex].second = value;
|
||||
++startIndex;
|
||||
}
|
||||
}
|
||||
|
||||
void DrawUtil::intervalMarkCircular(std::vector<std::pair<double, bool>> &marking,
|
||||
double start, double length, bool value)
|
||||
{
|
||||
if (length == 0.0) {
|
||||
return;
|
||||
}
|
||||
if (length < 0.0) {
|
||||
length = -length;
|
||||
start -= length;
|
||||
}
|
||||
if (length > M_2PI) {
|
||||
length = M_2PI;
|
||||
}
|
||||
|
||||
angleNormalize(start);
|
||||
|
||||
double end = start + length;
|
||||
if (end > M_PI) {
|
||||
end -= M_2PI;
|
||||
}
|
||||
|
||||
// Just make sure the point is stored, its index is read last
|
||||
intervalMerge(marking, end, true);
|
||||
unsigned int startIndex = intervalMerge(marking, start, true);
|
||||
unsigned int endIndex = intervalMerge(marking, end, true);
|
||||
|
||||
do {
|
||||
marking[startIndex].second = value;
|
||||
++startIndex;
|
||||
startIndex %= marking.size();
|
||||
}
|
||||
while (startIndex != endIndex);
|
||||
}
|
||||
|
||||
// Supplementary 2D analytic geometry functions
|
||||
//=============================================
|
||||
|
||||
int DrawUtil::findRootForValue(double Ax2, double Bxy, double Cy2, double Dx, double Ey, double F,
|
||||
double value, bool findX, double roots[])
|
||||
{
|
||||
double qA = 0.0;
|
||||
double qB = 0.0;
|
||||
double qC = 0.0;
|
||||
|
||||
if (findX) {
|
||||
qA = Ax2;
|
||||
qB = Bxy*value + Dx;
|
||||
qC = Cy2*value*value + Ey*value + F;
|
||||
}
|
||||
else {
|
||||
qA = Cy2;
|
||||
qB = Bxy*value + Ey;
|
||||
qC = Ax2*value*value + Dx*value + F;
|
||||
}
|
||||
|
||||
if (fabs(qA) < Precision::Confusion()) {
|
||||
// No quadratic coefficient - the equation is linear
|
||||
if (fabs(qB) < Precision::Confusion()) {
|
||||
// Not even linear coefficient - test for zero
|
||||
if (fabs(qC) > Precision::Confusion()) {
|
||||
// This equation has no solution
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
// Signal infinite number of solutions by returning 2, but do not touch root variables
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
else {
|
||||
roots[0] = -qC/qB;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
double qD = sqr(qB) - 4.0*qA*qC;
|
||||
if (qD < -Precision::Confusion()) {
|
||||
// Negative discriminant => no real roots
|
||||
return 0;
|
||||
}
|
||||
else if (qD > +Precision::Confusion()) {
|
||||
// Two distinctive roots
|
||||
roots[0] = (-qB + sqrt(qD))*0.5/qA;
|
||||
roots[1] = (-qB - sqrt(qD))*0.5/qA;
|
||||
return 2;
|
||||
}
|
||||
else {
|
||||
// Double root
|
||||
roots[0] = -qB*0.5/qA;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool DrawUtil::mergeBoundedPoint(const Base::Vector2d &point, const Base::BoundBox2d &boundary,
|
||||
std::vector<Base::Vector2d> &storage)
|
||||
{
|
||||
if (!boundary.Contains(point, Precision::Confusion())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < storage.size(); ++i) {
|
||||
if (point.IsEqual(storage[i], Precision::Confusion())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
storage.push_back(point);
|
||||
return true;
|
||||
}
|
||||
|
||||
void DrawUtil::findConicRectangleIntersections(double conicAx2, double conicBxy, double conicCy2,
|
||||
double conicDx, double conicEy, double conicF,
|
||||
const Base::BoundBox2d &rectangle,
|
||||
std::vector<Base::Vector2d> &intersections)
|
||||
{
|
||||
double roots[2];
|
||||
int rootCount;
|
||||
|
||||
// Find intersections with rectangle left side line
|
||||
roots[0] = rectangle.MinY;
|
||||
roots[1] = rectangle.MaxY;
|
||||
rootCount = findRootForValue(conicAx2, conicBxy, conicCy2, conicDx, conicEy, conicF,
|
||||
rectangle.MinX, false, roots);
|
||||
if (rootCount > 0) {
|
||||
mergeBoundedPoint(Base::Vector2d(rectangle.MinX, roots[0]), rectangle, intersections);
|
||||
}
|
||||
if (rootCount > 1) {
|
||||
mergeBoundedPoint(Base::Vector2d(rectangle.MinX, roots[1]), rectangle, intersections);
|
||||
}
|
||||
|
||||
// Find intersections with rectangle right side line
|
||||
roots[0] = rectangle.MinY;
|
||||
roots[1] = rectangle.MaxY;
|
||||
rootCount = findRootForValue(conicAx2, conicBxy, conicCy2, conicDx, conicEy, conicF,
|
||||
rectangle.MaxX, false, roots);
|
||||
if (rootCount > 0) {
|
||||
mergeBoundedPoint(Base::Vector2d(rectangle.MaxX, roots[0]), rectangle, intersections);
|
||||
}
|
||||
if (rootCount > 1) {
|
||||
mergeBoundedPoint(Base::Vector2d(rectangle.MaxX, roots[1]), rectangle, intersections);
|
||||
}
|
||||
|
||||
// Find intersections with rectangle top side line
|
||||
roots[0] = rectangle.MinX;
|
||||
roots[1] = rectangle.MaxX;
|
||||
rootCount = findRootForValue(conicAx2, conicBxy, conicCy2, conicDx, conicEy, conicF,
|
||||
rectangle.MinY, true, roots);
|
||||
if (rootCount > 0) {
|
||||
mergeBoundedPoint(Base::Vector2d(roots[0], rectangle.MinY), rectangle, intersections);
|
||||
}
|
||||
if (rootCount > 1) {
|
||||
mergeBoundedPoint(Base::Vector2d(roots[1], rectangle.MinY), rectangle, intersections);
|
||||
}
|
||||
|
||||
// Find intersections with rectangle top side line
|
||||
roots[0] = rectangle.MinX;
|
||||
roots[1] = rectangle.MaxX;
|
||||
rootCount = findRootForValue(conicAx2, conicBxy, conicCy2, conicDx, conicEy, conicF,
|
||||
rectangle.MaxY, true, roots);
|
||||
if (rootCount > 0) {
|
||||
mergeBoundedPoint(Base::Vector2d(roots[0], rectangle.MaxY), rectangle, intersections);
|
||||
}
|
||||
if (rootCount > 1) {
|
||||
mergeBoundedPoint(Base::Vector2d(roots[1], rectangle.MaxY), rectangle, intersections);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawUtil::findLineRectangleIntersections(const Base::Vector2d &linePoint, double lineAngle,
|
||||
const Base::BoundBox2d &rectangle,
|
||||
std::vector<Base::Vector2d> &intersections)
|
||||
{
|
||||
Base::Vector2d lineDirection(Base::Vector2d::FromPolar(1.0, lineAngle));
|
||||
findConicRectangleIntersections(0.0, 0.0, 0.0, +lineDirection.y, -lineDirection.x,
|
||||
lineDirection.x*linePoint.y - lineDirection.y*linePoint.x,
|
||||
rectangle, intersections);
|
||||
}
|
||||
|
||||
void DrawUtil::findCircleRectangleIntersections(const Base::Vector2d &circleCenter, double circleRadius,
|
||||
const Base::BoundBox2d &rectangle,
|
||||
std::vector<Base::Vector2d> &intersections)
|
||||
{
|
||||
findConicRectangleIntersections(1.0, 0.0, 1.0, -2.0*circleCenter.x, -2.0*circleCenter.y,
|
||||
sqr(circleCenter.x) + sqr(circleCenter.y) - sqr(circleRadius),
|
||||
rectangle, intersections);
|
||||
}
|
||||
|
||||
void DrawUtil::findLineSegmentRectangleIntersections(const Base::Vector2d &linePoint, double lineAngle,
|
||||
double segmentBasePosition, double segmentLength,
|
||||
const Base::BoundBox2d &rectangle,
|
||||
std::vector<Base::Vector2d> &intersections)
|
||||
{
|
||||
findLineRectangleIntersections(linePoint, lineAngle, rectangle, intersections);
|
||||
|
||||
if (segmentLength < 0.0) {
|
||||
segmentLength = -segmentLength;
|
||||
segmentBasePosition -= segmentLength;
|
||||
}
|
||||
|
||||
// Dispose the points on rectangle but not within the line segment boundaries
|
||||
Base::Vector2d segmentDirection(Base::Vector2d::FromPolar(1.0, lineAngle));
|
||||
for (unsigned int i = 0; i < intersections.size(); ) {
|
||||
double pointPosition = segmentDirection*(intersections[i] - linePoint);
|
||||
|
||||
if (pointPosition < segmentBasePosition - Precision::Confusion()
|
||||
|| pointPosition > segmentBasePosition + segmentLength + Precision::Confusion()) {
|
||||
intersections.erase(intersections.begin() + i);
|
||||
}
|
||||
else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
// Try to add the line segment end points
|
||||
mergeBoundedPoint(linePoint + segmentBasePosition*segmentDirection,
|
||||
rectangle, intersections);
|
||||
mergeBoundedPoint(linePoint + (segmentBasePosition + segmentLength)*segmentDirection,
|
||||
rectangle, intersections);
|
||||
}
|
||||
|
||||
void DrawUtil::findCircularArcRectangleIntersections(const Base::Vector2d &circleCenter, double circleRadius,
|
||||
double arcBaseAngle, double arcRotation,
|
||||
const Base::BoundBox2d &rectangle,
|
||||
std::vector<Base::Vector2d> &intersections)
|
||||
{
|
||||
findCircleRectangleIntersections(circleCenter, circleRadius, rectangle, intersections);
|
||||
|
||||
if (arcRotation < 0.0) {
|
||||
arcRotation = -arcRotation;
|
||||
arcBaseAngle -= arcRotation;
|
||||
if (arcBaseAngle <= -M_PI) {
|
||||
arcBaseAngle += M_2PI;
|
||||
}
|
||||
}
|
||||
|
||||
// Dispose the points on rectangle but not within the circular arc boundaries
|
||||
for (unsigned int i = 0; i < intersections.size(); ) {
|
||||
double pointAngle = (intersections[i] - circleCenter).Angle();
|
||||
if (pointAngle < arcBaseAngle - Precision::Confusion()) {
|
||||
pointAngle += M_2PI;
|
||||
}
|
||||
|
||||
if (pointAngle > arcBaseAngle + arcRotation + Precision::Confusion()) {
|
||||
intersections.erase(intersections.begin() + i);
|
||||
}
|
||||
else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
// Try to add the circular arc end points
|
||||
mergeBoundedPoint(circleCenter + Base::Vector2d::FromPolar(circleRadius, arcBaseAngle),
|
||||
rectangle, intersections);
|
||||
mergeBoundedPoint(circleCenter + Base::Vector2d::FromPolar(circleRadius, arcBaseAngle + arcRotation),
|
||||
rectangle, intersections);
|
||||
}
|
||||
|
||||
//============================
|
||||
// various debugging routines.
|
||||
|
||||
@@ -49,6 +49,11 @@
|
||||
|
||||
#include "LineGroup.h"
|
||||
|
||||
|
||||
#ifndef M_2PI
|
||||
#define M_2PI ((M_PI)*2.0)
|
||||
#endif
|
||||
|
||||
#define VERTEXTOLERANCE (2.0 * Precision::Confusion())
|
||||
|
||||
namespace TechDraw
|
||||
@@ -103,6 +108,47 @@ class TechDrawExport DrawUtil {
|
||||
static App::Color pyTupleToColor(PyObject* pColor);
|
||||
static PyObject* colorToPyTuple(App::Color color);
|
||||
|
||||
// Supplementary mathematical functions
|
||||
static int sgn(double x);
|
||||
static double sqr(double x);
|
||||
static void angleNormalize(double &fi);
|
||||
static double angleComposition(double fi, double delta);
|
||||
static double angleDifference(double fi1, double fi2, bool reflex = false);
|
||||
|
||||
// Interval marking functions
|
||||
static unsigned int intervalMerge(std::vector<std::pair<double, bool>> &marking,
|
||||
double boundary, bool wraps);
|
||||
static void intervalMarkLinear(std::vector<std::pair<double, bool>> &marking,
|
||||
double start, double length, bool value);
|
||||
static void intervalMarkCircular(std::vector<std::pair<double, bool>> &marking,
|
||||
double start, double length, bool value);
|
||||
|
||||
// Supplementary 2D analytic geometry functions
|
||||
static int findRootForValue(double Ax2, double Bxy, double Cy2, double Dx, double Ey, double F,
|
||||
double value, bool findX, double roots[]);
|
||||
static bool mergeBoundedPoint(const Base::Vector2d &point, const Base::BoundBox2d &boundary,
|
||||
std::vector<Base::Vector2d> &storage);
|
||||
|
||||
static void findConicRectangleIntersections(double conicAx2, double conicBxy, double conicCy2,
|
||||
double conicDx, double conicEy, double conicF,
|
||||
const Base::BoundBox2d &rectangle,
|
||||
std::vector<Base::Vector2d> &intersections);
|
||||
static void findLineRectangleIntersections(const Base::Vector2d &linePoint, double lineAngle,
|
||||
const Base::BoundBox2d &rectangle,
|
||||
std::vector<Base::Vector2d> &intersections);
|
||||
static void findCircleRectangleIntersections(const Base::Vector2d &circleCenter, double circleRadius,
|
||||
const Base::BoundBox2d &rectangle,
|
||||
std::vector<Base::Vector2d> &intersections);
|
||||
|
||||
static void findLineSegmentRectangleIntersections(const Base::Vector2d &linePoint, double lineAngle,
|
||||
double segmentBasePosition, double segmentLength,
|
||||
const Base::BoundBox2d &rectangle,
|
||||
std::vector<Base::Vector2d> &intersections);
|
||||
static void findCircularArcRectangleIntersections(const Base::Vector2d &circleCenter, double circleRadius,
|
||||
double arcBaseAngle, double arcRotation,
|
||||
const Base::BoundBox2d &rectangle,
|
||||
std::vector<Base::Vector2d> &intersections);
|
||||
|
||||
//debugging routines
|
||||
static void dumpVertexes(const char* text, const TopoDS_Shape& s);
|
||||
static void dumpEdge(char* label, int i, TopoDS_Edge e);
|
||||
|
||||
@@ -97,6 +97,7 @@ DrawViewDimension::DrawViewDimension(void)
|
||||
References2D.setScope(App::LinkScope::Global);
|
||||
ADD_PROPERTY_TYPE(References3D,(0,0),"",(App::PropertyType)(App::Prop_None),"3D Geometry References");
|
||||
References3D.setScope(App::LinkScope::Global);
|
||||
|
||||
ADD_PROPERTY_TYPE(FormatSpec,("") , "Format",(App::PropertyType)(App::Prop_None),"Dimension Format");
|
||||
ADD_PROPERTY_TYPE(Arbitrary,(false) ,"Format",(App::PropertyType)(App::Prop_None),"Value overridden by user");
|
||||
|
||||
@@ -104,8 +105,10 @@ DrawViewDimension::DrawViewDimension(void)
|
||||
ADD_PROPERTY(Type,((long)0));
|
||||
MeasureType.setEnums(MeasureTypeEnums);
|
||||
ADD_PROPERTY(MeasureType, ((long)1)); //Projected (or True) measurement
|
||||
ADD_PROPERTY_TYPE(TheoreticalExact,(false),"",(App::PropertyType)(App::Prop_None),"Set for theoretical exact (basic) dimension");
|
||||
ADD_PROPERTY_TYPE(OverTolerance ,(0.0),"",App::Prop_None,"+ Tolerance value");
|
||||
ADD_PROPERTY_TYPE(UnderTolerance ,(0.0),"",App::Prop_None,"- Tolerance value");
|
||||
ADD_PROPERTY_TYPE(Inverted,(false),"",(App::PropertyType)(App::Prop_None),"The dimensional value is displayed inverted");
|
||||
|
||||
//hide the properties the user can't edit in the property editor
|
||||
// References2D.setStatus(App::Property::Hidden,true);
|
||||
@@ -509,7 +512,7 @@ App::DocumentObjectExecReturn *DrawViewDimension::execute(void)
|
||||
return DrawView::execute();
|
||||
}
|
||||
|
||||
std::string DrawViewDimension::getFormatedValue(bool obtuse)
|
||||
std::string DrawViewDimension::getFormatedValue()
|
||||
{
|
||||
// Base::Console().Message("DVD::getFormatedValue()\n");
|
||||
std::string result;
|
||||
@@ -518,7 +521,7 @@ std::string DrawViewDimension::getFormatedValue(bool obtuse)
|
||||
}
|
||||
|
||||
QString specStr = QString::fromUtf8(FormatSpec.getStrValue().data(),FormatSpec.getStrValue().size());
|
||||
double val = std::abs(getDimValue()); //internal units!
|
||||
double val = getDimValue();
|
||||
|
||||
bool angularMeasure = false;
|
||||
Base::Quantity qVal;
|
||||
@@ -527,9 +530,6 @@ std::string DrawViewDimension::getFormatedValue(bool obtuse)
|
||||
(Type.isValue("Angle3Pt")) ) {
|
||||
angularMeasure = true;
|
||||
qVal.setUnit(Base::Unit::Angle);
|
||||
if (obtuse) {
|
||||
qVal.setValue(fabs(360.0 - val));
|
||||
}
|
||||
} else {
|
||||
qVal.setUnit(Base::Unit::Length);
|
||||
}
|
||||
@@ -712,6 +712,17 @@ double DrawViewDimension::getDimValue()
|
||||
result = legAngle;
|
||||
}
|
||||
}
|
||||
|
||||
result = fabs(result);
|
||||
if (Inverted.getValue()) {
|
||||
if (Type.isValue("Angle") || Type.isValue("Angle3Pt")) {
|
||||
result = 360 - result;
|
||||
}
|
||||
else {
|
||||
result = -result;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -97,6 +97,9 @@ public:
|
||||
App::PropertyLinkSubList References2D; //Points to Projection SubFeatures
|
||||
App::PropertyLinkSubList References3D; //Points to 3D Geometry SubFeatures
|
||||
App::PropertyEnumeration Type; //DistanceX,DistanceY,Diameter, etc
|
||||
|
||||
App::PropertyBool TheoreticalExact;
|
||||
App::PropertyBool Inverted;
|
||||
App::PropertyString FormatSpec;
|
||||
App::PropertyBool Arbitrary;
|
||||
App::PropertyFloat OverTolerance;
|
||||
@@ -120,7 +123,7 @@ public:
|
||||
//return PyObject as DrawViewDimensionPy
|
||||
virtual PyObject *getPyObject(void);
|
||||
|
||||
virtual std::string getFormatedValue(bool obtuse = false);
|
||||
virtual std::string getFormatedValue();
|
||||
virtual double getDimValue();
|
||||
DrawViewPart* getViewPart() const;
|
||||
virtual QRectF getRect() const { return QRectF(0,0,1,1);} //pretend dimensions always fit!
|
||||
|
||||
Reference in New Issue
Block a user