+++ /dev/null
-// ellipse.cpp
-//
-// Part of the Architektonas Project
-// Originally part of QCad Community Edition by Andrew Mustun
-// Extensively rewritten and refactored by James L. Hammons
-// Portions copyright (C) 2001-2003 RibbonSoft
-// Copyright (C) 2010 Underground Software
-// See the README and GPLv2 files for licensing and warranty information
-//
-// JLH = James L. Hammons <jlhamm@acm.org>
-//
-// Who When What
-// --- ---------- -----------------------------------------------------------
-// JLH 05/28/2010 Added this text. :-)
-//
-
-#include "ellipse.h"
-
-#include "drawing.h"
-#include "graphicview.h"
-#include "information.h"
-#include "linetypepattern.h"
-#include "paintinterface.h"
-
-/**
- * Constructor.
- */
-Ellipse::Ellipse(EntityContainer * parent, const EllipseData & d):
- AtomicEntity(parent), data(d)
-{
- //calculateEndpoints();
- calculateBorders();
-}
-
-/*virtual*/ Ellipse::~Ellipse()
-{
-}
-
-/*virtual*/ Entity * Ellipse::clone()
-{
- Ellipse * e = new Ellipse(*this);
- e->initId();
- return e;
-}
-
-/** @return RS2::EntityEllipse */
-/*virtual*/ RS2::EntityType Ellipse::rtti() const
-{
- return RS2::EntityEllipse;
-}
-
-/**
- * @return Start point of the entity.
- */
-/*virtual*/ Vector Ellipse::getStartpoint() const
-{
- Vector p;
- p.set(data.center.x + cos(data.angle1) * getMajorRadius(),
- data.center.y + sin(data.angle1) * getMinorRadius());
- p.rotate(data.center, getAngle());
- return p;
-}
-
-/**
- * @return End point of the entity.
- */
-/*virtual*/ Vector Ellipse::getEndpoint() const
-{
- Vector p;
- p.set(data.center.x + cos(data.angle2) * getMajorRadius(),
- data.center.y + sin(data.angle2) * getMinorRadius());
- p.rotate(data.center, getAngle());
- return p;
-}
-
-void Ellipse::moveStartpoint(const Vector & pos)
-{
- data.angle1 = getEllipseAngle(pos);
- //data.angle1 = data.center.angleTo(pos);
- //calculateEndpoints();
- calculateBorders();
-}
-
-void Ellipse::moveEndpoint(const Vector & pos)
-{
- data.angle2 = getEllipseAngle(pos);
- //data.angle2 = data.center.angleTo(pos);
- //calculateEndpoints();
- calculateBorders();
-}
-
-RS2::Ending Ellipse::getTrimPoint(const Vector & coord, const Vector & trimPoint)
-{
- double angEl = getEllipseAngle(trimPoint);
- double angM = getEllipseAngle(coord);
-
- if (Math::getAngleDifference(angM, angEl) > M_PI)
- //if (data.reversed) {
- // return RS2::EndingEnd;
- //}
- //else {
- return RS2::EndingStart;
- //}
- else
- //if (data.reversed) {
- // return RS2::EndingStart;
- //}
- //else {
- return RS2::EndingEnd;
- //}
-}
-
-double Ellipse::getEllipseAngle(const Vector & pos)
-{
- Vector m = pos;
- m.rotate(data.center, -data.majorP.angle());
- Vector v = m - data.center;
- v.scale(Vector(1.0, 1.0 / data.ratio));
- return v.angle();
-}
-
-/** @return Copy of data that defines the ellipse. **/
-EllipseData Ellipse::getData()
-{
- return data;
-}
-
-VectorSolutions Ellipse::getRefPoints()
-{
- VectorSolutions ret(getStartpoint(), getEndpoint(), data.center);
- return ret;
-}
-
-/**
- * @retval true if the arc is reversed (clockwise),
- * @retval false otherwise
- */
-bool Ellipse::isReversed() const
-{
- return data.reversed;
-}
-
-/** sets the reversed status. */
-void Ellipse::setReversed(bool r)
-{
- data.reversed = r;
-}
-
-/** @return The rotation angle of this ellipse */
-double Ellipse::getAngle() const
-{
- return data.majorP.angle();
-}
-
-/** @return The start angle of this arc */
-double Ellipse::getAngle1()
-{
- return data.angle1;
-}
-
-/** Sets new start angle. */
-void Ellipse::setAngle1(double a1)
-{
- data.angle1 = a1;
-}
-
-/** @return The end angle of this arc */
-double Ellipse::getAngle2()
-{
- return data.angle2;
-}
-
-/** Sets new end angle. */
-void Ellipse::setAngle2(double a2)
-{
- data.angle2 = a2;
-}
-
-/** @return The center point (x) of this arc */
-Vector Ellipse::getCenter()
-{
- return data.center;
-}
-
-/** Sets new center. */
-void Ellipse::setCenter(const Vector & c)
-{
- data.center = c;
-}
-
-/** @return The endpoint of the major axis (relative to center). */
-Vector Ellipse::getMajorP()
-{
- return data.majorP;
-}
-
-/** Sets new major point (relative to center). */
-void Ellipse::setMajorP(const Vector & p)
-{
- data.majorP = p;
-}
-
-/** @return The ratio of minor to major axis */
-double Ellipse::getRatio()
-{
- return data.ratio;
-}
-
-/** Sets new ratio. */
-void Ellipse::setRatio(double r)
-{
- data.ratio = r;
-}
-
-/**
- * @return Angle length in rad.
- */
-/*virtual*/ double Ellipse::getAngleLength() const
-{
- if (isReversed())
- return data.angle1 - data.angle2;
- else
- return data.angle2 - data.angle1;
-}
-
-/** @return The major radius of this ellipse. Same as getRadius() */
-double Ellipse::getMajorRadius() const
-{
- return data.majorP.magnitude();
-}
-
-/** @return The minor radius of this ellipse */
-double Ellipse::getMinorRadius() const
-{
- return data.majorP.magnitude() * data.ratio;
-}
-
-/**
- * Recalculates the endpoints using the angles and the radius.
- */
-/*
- void Ellipse::calculateEndpoints() {
- double angle = data.majorP.angle();
- double radius1 = getMajorRadius();
- double radius2 = getMinorRadius();
-
- startpoint.set(data.center.x + cos(data.angle1) * radius1,
- data.center.y + sin(data.angle1) * radius2);
- startpoint.rotate(data.center, angle);
- endpoint.set(data.center.x + cos(data.angle2) * radius1,
- data.center.y + sin(data.angle2) * radius2);
- endpoint.rotate(data.center, angle);
- }
- */
-
-/**
- * Calculates the boundary box of this ellipse.
- *
- * @todo Fix that - the algorithm used is really bad / slow.
- */
-void Ellipse::calculateBorders()
-{
- DEBUG->print("Ellipse::calculateBorders");
-
- double radius1 = getMajorRadius();
- double radius2 = getMinorRadius();
- double angle = getAngle();
- double a1 = ((!isReversed()) ? data.angle1 : data.angle2);
- double a2 = ((!isReversed()) ? data.angle2 : data.angle1);
- Vector startpoint = getStartpoint();
- Vector endpoint = getEndpoint();
-
- double minX = std::min(startpoint.x, endpoint.x);
- double minY = std::min(startpoint.y, endpoint.y);
- double maxX = std::max(startpoint.x, endpoint.x);
- double maxY = std::max(startpoint.y, endpoint.y);
-
- // kind of a brute force. TODO: exact calculation
- Vector vp;
- double a = a1;
-
- do
- {
- vp.set(data.center.x + radius1 * cos(a),
- data.center.y + radius2 * sin(a));
- vp.rotate(data.center, angle);
-
- minX = std::min(minX, vp.x);
- minY = std::min(minY, vp.y);
- maxX = std::max(maxX, vp.x);
- maxY = std::max(maxY, vp.y);
-
- a += 0.03;
- }
- while (Math::isAngleBetween(Math::correctAngle(a), a1, a2, false)
- && a < 4 * M_PI);
-
- minV.set(minX, minY);
- maxV.set(maxX, maxY);
- DEBUG->print("Ellipse::calculateBorders: OK");
-}
-
-Vector Ellipse::getNearestEndpoint(const Vector & coord, double * dist)
-{
- double dist1, dist2;
- Vector nearerPoint;
- Vector startpoint = getStartpoint();
- Vector endpoint = getEndpoint();
-
- dist1 = startpoint.distanceTo(coord);
- dist2 = endpoint.distanceTo(coord);
-
- if (dist2 < dist1)
- {
- if (dist != NULL)
- *dist = dist2;
- nearerPoint = endpoint;
- }
- else
- {
- if (dist != NULL)
- *dist = dist1;
- nearerPoint = startpoint;
- }
-
- return nearerPoint;
-}
-
-Vector Ellipse::getNearestPointOnEntity(const Vector & coord, bool onEntity, double * dist, Entity * * entity)
-{
- DEBUG->print("Ellipse::getNearestPointOnEntity");
-
- Vector ret(false);
-
- if (entity != NULL)
- *entity = this;
- double ang = getAngle();
-
- Vector normalized = (coord - data.center).rotate(-ang);
-
- double dU = normalized.x;
- double dV = normalized.y;
- double dA = getMajorRadius();
- double dB = getMinorRadius();
- double dEpsilon = 1.0e-8;
- int iMax = 32;
- int riIFinal = 0;
- double rdX = 0.0;
- double rdY = 0.0;
- double dDistance;
- bool swap = false;
- bool majorSwap = false;
-
- if (dA < dB)
- {
- double dum = dA;
- dA = dB;
- dB = dum;
- dum = dU;
- dU = dV;
- dV = dum;
- majorSwap = true;
- }
-
- if (dV < 0.0)
- {
- dV *= -1.0;
- swap = true;
- }
-
- // initial guess
- double dT = dB * (dV - dB);
-
- // Newton s method
- int i;
-
- for (i = 0; i < iMax; i++)
- {
- DEBUG->print("Ellipse::getNearestPointOnEntity: i: %d", i);
- double dTpASqr = dT + dA * dA;
- double dTpBSqr = dT + dB * dB;
- double dInvTpASqr = 1.0 / dTpASqr;
- double dInvTpBSqr = 1.0 / dTpBSqr;
- double dXDivA = dA * dU * dInvTpASqr;
- double dYDivB = dB * dV * dInvTpBSqr;
- double dXDivASqr = dXDivA * dXDivA;
- double dYDivBSqr = dYDivB * dYDivB;
- double dF = dXDivASqr + dYDivBSqr - 1.0;
- DEBUG->print("Ellipse::getNearestPointOnEntity: dF: %f", dF);
-
- if (fabs(dF) < dEpsilon)
- {
- // F(t0) is close enough to zero, terminate the iteration:
- rdX = dXDivA * dA;
- rdY = dYDivB * dB;
- riIFinal = i;
- DEBUG->print("Ellipse::getNearestPointOnEntity: rdX,rdY 1: %f,%f", rdX, rdY);
- break;
- }
- double dFDer = 2.0 * (dXDivASqr * dInvTpASqr + dYDivBSqr * dInvTpBSqr);
- double dRatio = dF / dFDer;
- DEBUG->print("Ellipse::getNearestPointOnEntity: dRatio: %f", dRatio);
-
- if (fabs(dRatio) < dEpsilon)
- {
- // t1-t0 is close enough to zero, terminate the iteration:
- rdX = dXDivA * dA;
- rdY = dYDivB * dB;
- riIFinal = i;
- DEBUG->print("Ellipse::getNearestPointOnEntity: rdX,rdY 2: %f,%f", rdX, rdY);
- break;
- }
- dT += dRatio;
- }
-
- if (i == iMax)
- {
- // failed to converge:
- DEBUG->print("Ellipse::getNearestPointOnEntity: failed");
- dDistance = RS_MAXDOUBLE;
- }
- else
- {
- double dDelta0 = rdX - dU;
- double dDelta1 = rdY - dV;
- dDistance = sqrt(dDelta0 * dDelta0 + dDelta1 * dDelta1);
- ret = Vector(rdX, rdY);
- DEBUG->print("Ellipse::getNearestPointOnEntity: rdX,rdY 2: %f,%f", rdX, rdY);
- DEBUG->print("Ellipse::getNearestPointOnEntity: ret: %f,%f", ret.x, ret.y);
- }
-
- if (dist != NULL)
- {
- if (ret.valid)
- *dist = dDistance;
- else
- *dist = RS_MAXDOUBLE;
- }
-
- if (ret.valid)
- {
- if (swap)
- ret.y *= -1.0;
-
-
- if (majorSwap)
- {
- double dum = ret.x;
- ret.x = ret.y;
- ret.y = dum;
- }
- ret = (ret.rotate(ang) + data.center);
-
- if (onEntity)
- {
- double a1 = data.center.angleTo(getStartpoint());
- double a2 = data.center.angleTo(getEndpoint());
- double a = data.center.angleTo(ret);
-
- if (!Math::isAngleBetween(a, a1, a2, data.reversed))
- ret = Vector(false);
- }
- }
-
- return ret;
-}
-
-/**
- * @param tolerance Tolerance.
- *
- * @retval true if the given point is on this entity.
- * @retval false otherwise
- */
-bool Ellipse::isPointOnEntity(const Vector & coord, double tolerance)
-{
- double dist = getDistanceToPoint(coord, NULL, RS2::ResolveNone);
- return (dist <= tolerance);
-}
-
-Vector Ellipse::getNearestCenter(const Vector & coord, double * dist)
-{
- if (dist != NULL)
- *dist = coord.distanceTo(data.center);
- return data.center;
-}
-
-/**
- * @todo Implement this.
- */
-Vector Ellipse::getNearestMiddle(const Vector & /*coord*/, double * dist)
-{
- if (dist != NULL)
- *dist = RS_MAXDOUBLE;
- return Vector(false);
-}
-
-Vector Ellipse::getNearestDist(double /*distance*/, const Vector & /*coord*/, double * dist)
-{
- if (dist != NULL)
- *dist = RS_MAXDOUBLE;
- return Vector(false);
-}
-
-double Ellipse::getDistanceToPoint(const Vector & coord, Entity * * entity, RS2::ResolveLevel, double /*solidDist*/)
-{
- double dist = RS_MAXDOUBLE;
- getNearestPointOnEntity(coord, true, &dist, entity);
-
- return dist;
-}
-
-void Ellipse::move(Vector offset)
-{
- data.center.move(offset);
- //calculateEndpoints();
- calculateBorders();
-}
-
-void Ellipse::rotate(Vector center, double angle)
-{
- data.center.rotate(center, angle);
- data.majorP.rotate(angle);
- //calculateEndpoints();
- calculateBorders();
-}
-
-void Ellipse::scale(Vector center, Vector factor)
-{
- data.center.scale(center, factor);
- data.majorP.scale(factor);
- //calculateEndpoints();
- calculateBorders();
-}
-
-/**
- * @todo deal with angles correctly
- */
-void Ellipse::mirror(Vector axisPoint1, Vector axisPoint2)
-{
- Vector mp = data.center + data.majorP;
-
- data.center.mirror(axisPoint1, axisPoint2);
- mp.mirror(axisPoint1, axisPoint2);
-
- data.majorP = mp - data.center;
-
- double a = axisPoint1.angleTo(axisPoint2);
-
- Vector vec;
- vec.setPolar(1.0, data.angle1);
- vec.mirror(Vector(0.0, 0.0), axisPoint2 - axisPoint1);
- data.angle1 = vec.angle() - 2 * a;
-
- vec.setPolar(1.0, data.angle2);
- vec.mirror(Vector(0.0, 0.0), axisPoint2 - axisPoint1);
- data.angle2 = vec.angle() - 2 * a;
-
- data.reversed = (!data.reversed);
-
- //calculateEndpoints();
- calculateBorders();
-}
-
-void Ellipse::moveRef(const Vector & ref, const Vector & offset)
-{
- Vector startpoint = getStartpoint();
- Vector endpoint = getEndpoint();
-
- if (ref.distanceTo(startpoint) < 1.0e-4)
- moveStartpoint(startpoint + offset);
-
-
- if (ref.distanceTo(endpoint) < 1.0e-4)
- moveEndpoint(endpoint + offset);
-}
-
-void Ellipse::draw(PaintInterface * painter, GraphicView * view, double /*patternOffset*/)
-{
- if (!painter || !view)
- return;
-
- if (getPen().getLineType() == RS2::SolidLine || isSelected()
- || view->getDrawingMode() == RS2::ModePreview)
- painter->drawEllipse(view->toGui(getCenter()),
- getMajorRadius() * view->getFactor().x,
- getMinorRadius() * view->getFactor().x,
- getAngle(), getAngle1(), getAngle2(), isReversed());
- else
- {
- double styleFactor = getStyleFactor(view);
-
- if (styleFactor < 0.0)
- {
- painter->drawEllipse(view->toGui(getCenter()),
- getMajorRadius() * view->getFactor().x,
- getMinorRadius() * view->getFactor().x,
- getAngle(), getAngle1(), getAngle2(), isReversed());
- return;
- }
-
- // Pattern:
- LineTypePattern * pat;
-
- if (isSelected())
- pat = &patternSelected;
- else
- pat = view->getPattern(getPen().getLineType());
-
- if (pat == NULL)
- return;
-
- // Pen to draw pattern is always solid:
- Pen pen = painter->getPen();
- pen.setLineType(RS2::SolidLine);
- painter->setPen(pen);
-
- double * da; // array of distances in x.
- int i; // index counter
-
- double length = getAngleLength();
-
- // create pattern:
- da = new double[pat->num];
-
- double tot = 0.0;
- i = 0;
- bool done = false;
- double curA = getAngle1();
- double curR;
- Vector cp = view->toGui(getCenter());
- double r1 = getMajorRadius() * view->getFactor().x;
- double r2 = getMinorRadius() * view->getFactor().x;
-
- do
- {
- curR = sqrt(Math::pow(getMinorRadius() * cos(curA), 2.0)
- + Math::pow(getMajorRadius() * sin(curA), 2.0));
-
- if (curR > 1.0e-6)
- {
- da[i] = fabs(pat->pattern[i] * styleFactor) / curR;
-
- if (pat->pattern[i] * styleFactor > 0.0)
- {
- if (tot + fabs(da[i]) < length)
- painter->drawEllipse(cp, r1, r2, getAngle(), curA, curA + da[i], false);
- else
- painter->drawEllipse(cp, r1, r2, getAngle(), curA, getAngle2(), false);
- }
- }
-
- curA += da[i];
- tot += fabs(da[i]);
- done = tot > length;
-
- i++;
-
- if (i >= pat->num)
- i = 0;
- }
- while (!done);
-
- delete[] da;
- }
-}
-
-/**
- * Dumps the point's data to stdout.
- */
-std::ostream & operator<<(std::ostream & os, const Ellipse & a)
-{
- os << " Ellipse: " << a.data << "\n";
- return os;
-}
-