|
 |
9a49d4 |
|
|
 |
9a49d4 |
|
|
 |
d5090c |
#include <tools assistants="" guidelineellipse.h=""></tools>
|
|
 |
9a49d4 |
|
|
 |
9a49d4 |
|
|
 |
f278a5 |
#include <tgl.h></tgl.h>
|
|
 |
f278a5 |
#include <tmathutil.h></tmathutil.h>
|
|
 |
f278a5 |
|
|
 |
f278a5 |
|
|
 |
f278a5 |
|
|
 |
f278a5 |
|
|
 |
f278a5 |
static TPointD findNearestPoint(const TPointD &p, double Rx, double Ry) {
|
|
 |
f278a5 |
Rx = fabs(Rx);
|
|
 |
f278a5 |
Ry = fabs(Ry);
|
|
 |
f278a5 |
TPointD ep;
|
|
 |
f278a5 |
|
|
 |
f278a5 |
if (isAlmostZero(Rx)) {
|
|
 |
f278a5 |
ep.y = p.y < -Ry ? -Ry
|
|
 |
f278a5 |
: p.y > Ry ? Ry : p.y;
|
|
 |
f278a5 |
return ep;
|
|
 |
f278a5 |
}
|
|
 |
f278a5 |
|
|
 |
f278a5 |
if (isAlmostZero(Ry)) {
|
|
 |
f278a5 |
ep.x = p.x < -Rx ? -Rx
|
|
 |
f278a5 |
: p.x > Rx ? Rx : p.x;
|
|
 |
f278a5 |
return ep;
|
|
 |
f278a5 |
}
|
|
 |
f278a5 |
|
|
 |
f278a5 |
double k = 1/Rx;
|
|
 |
f278a5 |
double x0 = p.x*k;
|
|
 |
f278a5 |
double y0 = p.y*k;
|
|
 |
f278a5 |
k *= Ry;
|
|
 |
f278a5 |
k *= k;
|
|
 |
f278a5 |
double l = k - 1;
|
|
 |
f278a5 |
|
|
 |
f278a5 |
double a = l*l;
|
|
 |
f278a5 |
double b = 2*l*x0;
|
|
 |
f278a5 |
double c = x0*x0 + y0*y0*k - l*l;
|
|
 |
f278a5 |
double d = -b;
|
|
 |
f278a5 |
double e = -x0*x0;
|
|
 |
f278a5 |
|
|
 |
f278a5 |
double dist = INFINITY;
|
|
 |
f278a5 |
std::complex<double> roots[4];</double>
|
|
 |
f278a5 |
int cnt = solveEquation4(roots, a, b, c, d, e);
|
|
 |
f278a5 |
for(int i = 0; i < cnt; ++i) {
|
|
 |
f278a5 |
if (!isAlmostZero(roots[i].imag()))
|
|
 |
f278a5 |
continue;
|
|
 |
f278a5 |
|
|
 |
f278a5 |
double x = roots[i].real();
|
|
 |
f278a5 |
double y;
|
|
 |
f278a5 |
if (isAlmostZero(fabs(x) - 1)) {
|
|
 |
f278a5 |
y = 0;
|
|
 |
f278a5 |
} else
|
|
 |
f278a5 |
if (fabs(x) < 1) {
|
|
 |
f278a5 |
y = sqrt(k*(1 - x*x));
|
|
 |
f278a5 |
if (y0 < 0) y = -y;
|
|
 |
f278a5 |
} else {
|
|
 |
f278a5 |
continue;
|
|
 |
f278a5 |
}
|
|
 |
f278a5 |
|
|
 |
f278a5 |
double dd = (x0-x)*(x0-x) + (y0-y)*(y0-y);
|
|
 |
f278a5 |
if (dd < dist)
|
|
 |
f278a5 |
{ ep.x = x*Rx; ep.y = y*Rx; dist = dd; }
|
|
 |
f278a5 |
}
|
|
 |
f278a5 |
return ep;
|
|
 |
f278a5 |
}
|
|
 |
f278a5 |
|
|
 |
9a49d4 |
|
|
 |
9a49d4 |
|
|
 |
9a49d4 |
//*****************************************************************************************
|
|
 |
9a49d4 |
|
|
 |
9a49d4 |
//*****************************************************************************************
|
|
 |
9a49d4 |
|
|
 |
f278a5 |
|
|
 |
9a49d4 |
TGuidelineEllipse::TGuidelineEllipse(
|
|
 |
9a49d4 |
bool enabled,
|
|
 |
9a49d4 |
double magnetism,
|
|
 |
f278a5 |
const TAffine &matrix,
|
|
 |
f278a5 |
const TAffine &matrixInv,
|
|
 |
f278a5 |
double Rx,
|
|
 |
f278a5 |
double Ry
|
|
 |
9a49d4 |
):
|
|
 |
9a49d4 |
TGuideline(enabled, magnetism),
|
|
 |
9a49d4 |
matrix(matrix),
|
|
 |
f278a5 |
matrixInv(matrixInv),
|
|
 |
f278a5 |
Rx(Rx),
|
|
 |
f278a5 |
Ry(Ry)
|
|
 |
f278a5 |
{ }
|
|
 |
9a49d4 |
|
|
 |
9a49d4 |
|
|
 |
9a49d4 |
TGuidelineEllipse::TGuidelineEllipse(
|
|
 |
9a49d4 |
bool enabled,
|
|
 |
9a49d4 |
double magnetism,
|
|
 |
f278a5 |
const TAffine &matrix,
|
|
 |
f278a5 |
const TAffine &matrixInv
|
|
 |
9a49d4 |
):
|
|
 |
f278a5 |
TGuidelineEllipse(
|
|
 |
f278a5 |
enabled, magnetism, matrix, matrixInv,
|
|
 |
f278a5 |
sqrt(matrix.a11*matrix.a11 + matrix.a21*matrix.a21),
|
|
 |
f278a5 |
sqrt(matrix.a12*matrix.a12 + matrix.a22*matrix.a22) )
|
|
 |
f278a5 |
{ }
|
|
 |
f278a5 |
|
|
 |
f278a5 |
|
|
 |
f278a5 |
TGuidelineEllipse::TGuidelineEllipse(
|
|
 |
f278a5 |
bool enabled,
|
|
 |
f278a5 |
double magnetism,
|
|
 |
f278a5 |
const TAffine &matrix
|
|
 |
f278a5 |
):
|
|
 |
f278a5 |
TGuidelineEllipse(enabled, magnetism, matrix, matrix.inv())
|
|
 |
f278a5 |
{ }
|
|
 |
f278a5 |
|
|
 |
9a49d4 |
|
|
 |
9a49d4 |
|
|
 |
9a49d4 |
TTrackPoint
|
|
 |
9a49d4 |
TGuidelineEllipse::transformPoint(const TTrackPoint &point) const
|
|
 |
9a49d4 |
{
|
|
 |
9a49d4 |
TTrackPoint p = point;
|
|
 |
9a49d4 |
TPointD pp = matrixInv*p.position;
|
|
 |
9a49d4 |
double l2 = norm2(pp);
|
|
 |
9a49d4 |
if (l2 > TConsts::epsilon*TConsts::epsilon)
|
|
 |
9a49d4 |
p.position = matrix*(pp*(1.0/sqrt(l2)));
|
|
 |
9a49d4 |
return p;
|
|
 |
9a49d4 |
}
|
|
 |
9a49d4 |
|
|
 |
9a49d4 |
|
|
 |
f278a5 |
TPointD
|
|
 |
f278a5 |
TGuidelineEllipse::nearestPoint(const TPointD &point) const
|
|
 |
f278a5 |
{
|
|
 |
f278a5 |
TPointD p = matrixInv*point;
|
|
 |
f278a5 |
p = findNearestPoint(TPointD(p.x*Rx, p.y*Ry), Rx, Ry);
|
|
 |
f278a5 |
if (!isAlmostZero(Rx)) p.x /= Rx;
|
|
 |
f278a5 |
if (!isAlmostZero(Ry)) p.y /= Ry;
|
|
 |
f278a5 |
return matrix*p;
|
|
 |
f278a5 |
}
|
|
 |
f278a5 |
|
|
 |
f278a5 |
|
|
 |
f278a5 |
|
|
 |
9a49d4 |
bool
|
|
 |
9a49d4 |
TGuidelineEllipse::truncateEllipse(
|
|
 |
9a49d4 |
TAngleRangeSet &ranges,
|
|
 |
9a49d4 |
const TAffine &ellipseMatrixInv,
|
|
 |
9a49d4 |
const TRectD &bounds )
|
|
 |
9a49d4 |
{
|
|
 |
9a49d4 |
if (ranges.isEmpty()) return false;
|
|
 |
9a49d4 |
if (bounds.isEmpty()) { ranges.clear(); return false; }
|
|
 |
9a49d4 |
|
|
 |
9a49d4 |
TPointD o = ellipseMatrixInv*bounds.getP00();
|
|
 |
9a49d4 |
TPointD dx = ellipseMatrixInv.transformDirection(TPointD(bounds.getLx(), 0.0));
|
|
 |
9a49d4 |
TPointD dy = ellipseMatrixInv.transformDirection(TPointD(0.0, bounds.getLy()));
|
|
 |
9a49d4 |
double lx2 = norm2(dx);
|
|
 |
9a49d4 |
double ly2 = norm2(dy);
|
|
 |
9a49d4 |
if ( lx2 < TConsts::epsilon*TConsts::epsilon
|
|
 |
9a49d4 |
|| ly2 < TConsts::epsilon*TConsts::epsilon )
|
|
 |
9a49d4 |
{ ranges.clear(); return false; }
|
|
 |
9a49d4 |
TPointD nx = rotate90(dx)*(1.0/sqrt(lx2));
|
|
 |
9a49d4 |
TPointD ny = rotate90(dy)*(1.0/sqrt(ly2));
|
|
 |
9a49d4 |
|
|
 |
9a49d4 |
TAngleI ax = TAngleRangeSet::fromDouble(atan(dx));
|
|
 |
9a49d4 |
TAngleI ay = TAngleRangeSet::fromDouble(atan(dy));
|
|
 |
9a49d4 |
|
|
 |
9a49d4 |
double sign = nx*dy;
|
|
 |
9a49d4 |
if (fabs(sign) <= TConsts::epsilon)
|
|
 |
9a49d4 |
{ ranges.clear(); return false; }
|
|
 |
9a49d4 |
|
|
 |
9a49d4 |
if (sign < 0.0) {
|
|
 |
9a49d4 |
nx = -nx; ny = -ny;
|
|
 |
9a49d4 |
ax ^= TAngleRangeSet::half;
|
|
 |
9a49d4 |
ay ^= TAngleRangeSet::half;
|
|
 |
9a49d4 |
}
|
|
 |
9a49d4 |
|
|
 |
9a49d4 |
TAngleI angles[] = {
|
|
 |
9a49d4 |
ax,
|
|
 |
9a49d4 |
ax^TAngleRangeSet::half,
|
|
 |
9a49d4 |
ay,
|
|
 |
9a49d4 |
ay^TAngleRangeSet::half };
|
|
 |
9a49d4 |
|
|
 |
9a49d4 |
double heights[] = {
|
|
 |
9a49d4 |
o*nx,
|
|
 |
9a49d4 |
-((o+dx+dy)*nx),
|
|
 |
9a49d4 |
(o+dx)*ny,
|
|
 |
9a49d4 |
-((o+dy)*ny) };
|
|
 |
9a49d4 |
|
|
 |
9a49d4 |
for(int i = 0; i < 4; ++i) {
|
|
 |
9a49d4 |
double h = heights[i];
|
|
 |
9a49d4 |
if (heights[i] <= TConsts::epsilon - 1.0)
|
|
 |
9a49d4 |
continue;
|
|
 |
9a49d4 |
if (h >= 1.0 - TConsts::epsilon)
|
|
 |
9a49d4 |
{ ranges.clear(); return false; }
|
|
 |
9a49d4 |
TAngleI a = TAngleRangeSet::fromDouble(asin(h));
|
|
 |
9a49d4 |
TAngleI da = angles[i];
|
|
 |
9a49d4 |
ranges.subtract(da - a, (da + a)^TAngleRangeSet::half );
|
|
 |
9a49d4 |
if (ranges.isEmpty()) return false;
|
|
 |
9a49d4 |
}
|
|
 |
9a49d4 |
|
|
 |
9a49d4 |
return true;
|
|
 |
9a49d4 |
}
|
|
 |
9a49d4 |
|
|
 |
9a49d4 |
|
|
 |
9a49d4 |
int
|
|
 |
9a49d4 |
TGuidelineEllipse::calcSegmentsCount(const TAffine &ellipseMatrix, double pixelSize) {
|
|
 |
9a49d4 |
const TAffine &em = ellipseMatrix;
|
|
 |
9a49d4 |
const int min = 4, max = 1000;
|
|
 |
9a49d4 |
double r = sqrt(0.5*(norm2(TPointD(em.a11, em.a21)) + norm2(TPointD(em.a12, em.a22))));
|
|
 |
9a49d4 |
double h = 0.5*pixelSize/r;
|
|
 |
9a49d4 |
if (h <= TConsts::epsilon) return max;
|
|
 |
9a49d4 |
if (h >= 1.0 - TConsts::epsilon) return min;
|
|
 |
9a49d4 |
double segments = round(M_2PI/acos(1.0 - h));
|
|
 |
9a49d4 |
return segments <= (double)min ? min
|
|
 |
9a49d4 |
: segments >= (double)max ? max
|
|
 |
9a49d4 |
: (int)segments;
|
|
 |
9a49d4 |
}
|
|
 |
9a49d4 |
|
|
 |
9a49d4 |
|
|
 |
9a49d4 |
void
|
|
 |
9a49d4 |
TGuidelineEllipse::draw(bool active, bool enabled) const {
|
|
 |
9a49d4 |
TAffine4 modelview, projection;
|
|
 |
9a49d4 |
glGetDoublev(GL_MODELVIEW_MATRIX, modelview.a);
|
|
 |
9a49d4 |
glGetDoublev(GL_PROJECTION_MATRIX, projection.a);
|
|
 |
9a49d4 |
TAffine screenMatrix = (projection*modelview).get2d();
|
|
 |
9a49d4 |
TAffine screenMatrixInv = screenMatrix.inv();
|
|
 |
9a49d4 |
double pixelSize = sqrt(tglGetPixelSize2());
|
|
 |
9a49d4 |
|
|
 |
9a49d4 |
const TRectD oneBox(-1.0, -1.0, 1.0, 1.0);
|
|
 |
9a49d4 |
TAngleRangeSet ranges(true);
|
|
 |
9a49d4 |
if (!truncateEllipse(ranges, matrixInv*screenMatrixInv, oneBox))
|
|
 |
9a49d4 |
return;
|
|
 |
9a49d4 |
|
|
 |
9a49d4 |
int segments = calcSegmentsCount(matrix, pixelSize);
|
|
 |
9a49d4 |
double da = M_2PI/segments;
|
|
 |
9a49d4 |
double s = sin(da);
|
|
 |
9a49d4 |
double c = cos(da);
|
|
 |
9a49d4 |
|
|
 |
9a49d4 |
for(TAngleRangeSet::Iterator i(ranges); i; ++i) {
|
|
 |
9a49d4 |
double a0 = i.d0();
|
|
 |
9a49d4 |
double a1 = i.d1greater();
|
|
 |
9a49d4 |
TPointD r(cos(a0), sin(a0));
|
|
 |
9a49d4 |
TPointD p0 = matrix*r;
|
|
 |
9a49d4 |
int cnt = (int)floor((a1 - a0)/da);
|
|
 |
9a49d4 |
for(int j = 0; j < cnt; ++j) {
|
|
 |
9a49d4 |
r = TPointD(r.x*c - r.y*s, r.y*c + r.x*s);
|
|
 |
9a49d4 |
TPointD p1 = matrix*r;
|
|
 |
9a49d4 |
drawSegment(p0, p1, pixelSize, active, enabled);
|
|
 |
9a49d4 |
p0 = p1;
|
|
 |
9a49d4 |
}
|
|
 |
9a49d4 |
drawSegment(p0, matrix*TPointD(cos(a1), sin(a1)), pixelSize, active, enabled);
|
|
 |
9a49d4 |
}
|
|
 |
9a49d4 |
}
|
|
 |
9a49d4 |
|