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.
|
||||
|
||||
Reference in New Issue
Block a user