Skip to content

Commit

Permalink
Complete remake of drawing dimensions
Browse files Browse the repository at this point in the history
  • Loading branch information
pavltom authored and wwmayer committed Sep 25, 2019
1 parent 29e7f69 commit b75398d
Show file tree
Hide file tree
Showing 11 changed files with 1,777 additions and 1,206 deletions.
356 changes: 356 additions & 0 deletions src/Mod/TechDraw/App/DrawUtil.cpp
Expand Up @@ -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.
Expand Down

0 comments on commit b75398d

Please sign in to comment.