8c6c57f1b4
Finish ensuring symbols use headers correctly
661 lines
21 KiB
C++
661 lines
21 KiB
C++
|
|
|
|
// outlineApproximation.cpp: implementation of the outlineApproximation class.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
#include "tstrokeoutline.h"
|
|
#include "tstroke.h"
|
|
#include "tcurves.h"
|
|
#include "tmathutil.h"
|
|
#include "tgl.h"
|
|
|
|
//#include "tcolorfunctions.h"
|
|
|
|
typedef std::pair<TQuadratic *, TQuadratic *> outlineEdge;
|
|
typedef std::vector<outlineEdge> outlineBoundary;
|
|
|
|
const double infDouble = (std::numeric_limits<double>::max)();
|
|
|
|
/*
|
|
ONLY FOT TEST
|
|
|
|
TSegment g_tangEnvelope_1;
|
|
TSegment g_tangEnvelope_2;
|
|
|
|
vector<TQuadratic> g_testOutline;
|
|
*/
|
|
namespace Outline {
|
|
|
|
class infinityCurvature {};
|
|
|
|
class notValidOutline {};
|
|
}
|
|
|
|
namespace {
|
|
|
|
/*
|
|
This formule is derived from Graphic Gems pag. 600
|
|
|
|
e = h^2 |a|/8
|
|
|
|
e = pixel size
|
|
h = step
|
|
a = acceleration of curve (for a quadratic is a costant value)
|
|
*/
|
|
double localComputeStep(const TQuadratic &quad, double pixelSize) {
|
|
double step = 2;
|
|
|
|
TPointD A = quad.getP0() - 2.0 * quad.getP1() +
|
|
quad.getP2(); // 2*A is the acceleration of the curve
|
|
|
|
double A_len = norm(A);
|
|
if (A_len > 0) step = sqrt(2 * pixelSize / A_len);
|
|
|
|
return step;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
// selezionano lo spicchio da calcolare nella costruzione dei tappi
|
|
// (semicirconferenze iniziali e finali)
|
|
const int QUARTER_BEGIN = 1;
|
|
const int QUARTER_END = 0;
|
|
|
|
// selezionano il pezzo d'outline da calcolare (sopra/sotto)
|
|
const int OUTLINE_UP = 1;
|
|
const int OUTLINE_DOWN = 0;
|
|
|
|
// utili
|
|
const double ratio_1_3 = 1.0 / 3.0;
|
|
const double ratio_2_3 = 2.0 / 3.0;
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// torna la curvature per t=0
|
|
template <class T>
|
|
double curvature_t0(const T *curve) {
|
|
assert(curve);
|
|
TPointD v1 = curve->getP1() - curve->getP0();
|
|
TPointD v2 = curve->getP2() - curve->getP1();
|
|
|
|
double v_cross = cross(v1, v2);
|
|
|
|
if (isAlmostZero(v_cross)) return infDouble;
|
|
|
|
return ratio_2_3 * v_cross / pow(norm(v1), ratio_1_3);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// torna la curvature per t=1
|
|
double curvature_t1(const TThickQuadratic *curve) {
|
|
assert(curve);
|
|
TThickQuadratic tmp;
|
|
|
|
tmp.setThickP0(curve->getThickP2());
|
|
tmp.setThickP1(curve->getThickP1());
|
|
tmp.setThickP2(curve->getThickP0());
|
|
|
|
return curvature_t0(&tmp);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// estrae il punto dell'outline per il parametro specificato
|
|
// N.B: e' sbagliata non tiene conto degli inviluppi
|
|
TPointD getPointInOutline(const TThickQuadratic *tq, double t, int upOrDown) {
|
|
assert(tq);
|
|
const TThickPoint &p = tq->getThickPoint(t);
|
|
TPointD n = tq->getSpeed(t);
|
|
if (norm2(n)) {
|
|
n = normalize(n);
|
|
n = upOrDown ? rotate90(n) : rotate270(n);
|
|
}
|
|
|
|
return convert(p) + p.thick * n;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool checkPointInOutline(const TPointD &pointToTest, const TThickQuadratic *tq,
|
|
double t, double error) {
|
|
assert(tq);
|
|
TThickPoint tpnt = tq->getThickPoint(t);
|
|
|
|
if (fabs(sq(pointToTest.x - tpnt.x) + sq(pointToTest.y - tpnt.y) -
|
|
sq(tpnt.thick)) < error)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// costruisce un ramo di outline (sopra o sotto) per una quadratica cicciona
|
|
TQuadratic *makeOutlineForThickQuadratic(const TThickQuadratic *tq,
|
|
int upOrDown) {
|
|
assert(tq);
|
|
// if(!outline) return 0;
|
|
|
|
TThickPoint p0 = tq->getThickP0(),
|
|
// p1 = tq->getThickP0(),
|
|
p2 = tq->getThickP2();
|
|
|
|
TPointD t0 = tq->getP1() - tq->getP0();
|
|
TPointD t1 = tq->getP2() - tq->getP1();
|
|
|
|
if (t0 == t1) return 0;
|
|
|
|
TPointD N0 = tq->getSpeed(0.0), N2 = tq->getSpeed(1.0);
|
|
|
|
if (!norm2(N0) && !norm2(N2)) throw Outline::notValidOutline();
|
|
|
|
if (norm2(N0)) {
|
|
N0 = normalize(N0);
|
|
N0 = upOrDown ? rotate90(N0) : rotate270(N0);
|
|
}
|
|
|
|
if (norm2(N2)) {
|
|
N2 = normalize(N2);
|
|
N2 = upOrDown ? rotate90(N2) : rotate270(N2);
|
|
}
|
|
|
|
TPointD p0aux = (convert(p0) + p0.thick * N0);
|
|
TPointD p2aux = (convert(p2) + p2.thick * N2);
|
|
|
|
TQuadratic radius(TPointD(tq->getThickP0().thick, 0.0),
|
|
TPointD(tq->getThickP1().thick, 0.0),
|
|
TPointD(tq->getThickP2().thick, 0.0));
|
|
|
|
TPointD r0 = radius.getSpeed(0.0);
|
|
TPointD r1 = radius.getSpeed(1.0);
|
|
|
|
TPointD v0, v2;
|
|
|
|
double ct0 = curvature_t0(tq);
|
|
|
|
if (ct0 != infDouble)
|
|
v0 = (1 + p0.thick * ct0) * t0 + 0.5 * r0.x * N0;
|
|
else
|
|
v0 = r0.x * N0;
|
|
|
|
double ct1 = curvature_t1(tq);
|
|
|
|
if (ct1 != infDouble)
|
|
v2 = (1 + p2.thick * ct1) * t1 + 0.5 * r1.x * N2;
|
|
else
|
|
v2 = r1.x * N2;
|
|
|
|
/*
|
|
try {
|
|
v0 = (1 + p0.thick * curvature_t0( tq )) * t0 + 0.5 * r0.x * N0;
|
|
}
|
|
catch( Outline::infinityCurvature& ) {
|
|
}
|
|
|
|
try {
|
|
v2 = (1 + p2.thick * curvature_t1( tq )) * t1 + 0.5 * r1.x * N2;
|
|
}
|
|
catch( Outline::infinityCurvature& ) {
|
|
}
|
|
*/
|
|
// g_tangEnvelope_1.setP0( outline.getP0() );
|
|
// g_tangEnvelope_1.setP1( outline.getP0() + v0 );
|
|
|
|
// g_tangEnvelope_2.setP0( outline.getP2() );
|
|
// g_tangEnvelope_2.setP1( outline.getP2() + v2 );
|
|
|
|
// solve sistem p1 = p0 + k * v1 = p2 + m * v2 to find
|
|
|
|
double det = v0.x * v0.y - v2.x * v2.y;
|
|
if (areAlmostEqual(det, 0.0)) return 0;
|
|
double xsol;
|
|
try {
|
|
xsol = ((p0aux.x - p2aux.x) * v2.y - (p0aux.y - p2aux.y) * v2.x) / det;
|
|
// tsolveSistem( A, 2, b );
|
|
} catch (TMathException &) {
|
|
return new TQuadratic((upOrDown) ? p0aux : p2aux, (p0aux + p2aux) * 0.5,
|
|
(upOrDown) ? p2aux : p0aux);
|
|
} catch (std::exception &e) {
|
|
std::string s(e.what());
|
|
abort();
|
|
} catch (...) {
|
|
abort();
|
|
}
|
|
|
|
return new TQuadratic((upOrDown) ? p0aux : p2aux, p0aux + xsol * v0,
|
|
(upOrDown) ? p2aux : p0aux);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
/*
|
|
costruisce l'outline per una singola quadratica senza
|
|
inserire le semicirconferenze iniziali e finali
|
|
*/
|
|
void makeOutline(/*std::ofstream& cout,*/
|
|
outlineBoundary &outl, const TThickQuadratic &t,
|
|
double error) {
|
|
outlineEdge edge;
|
|
const TThickQuadratic *tq = &t;
|
|
edge.first = edge.second = 0;
|
|
try {
|
|
edge.first = makeOutlineForThickQuadratic(tq, OUTLINE_UP);
|
|
edge.second = makeOutlineForThickQuadratic(tq, OUTLINE_DOWN);
|
|
} catch (Outline::notValidOutline &) {
|
|
delete edge.first;
|
|
delete edge.second;
|
|
return;
|
|
}
|
|
|
|
const TQuadratic *q_up = edge.first;
|
|
const TQuadratic *q_down = edge.second;
|
|
const double parameterTest = 0.5;
|
|
|
|
// forza l'uscita per valori troppo piccoli
|
|
bool isAlmostAPoint =
|
|
areAlmostEqual(tq->getThickP0(), tq->getThickP1(), 1e-2) &&
|
|
areAlmostEqual(tq->getThickP1(), tq->getThickP2(), 1e-2) /*&&
|
|
areAlmostEqual( tq.getThickP0(), tq.getThickP2(), 1e-2 )*/;
|
|
|
|
if (isAlmostAPoint ||
|
|
q_up && checkPointInOutline(q_up->getPoint(parameterTest), tq,
|
|
parameterTest, error) &&
|
|
q_down && checkPointInOutline(q_down->getPoint(parameterTest), tq,
|
|
parameterTest, error)) {
|
|
/* if (edge.first)
|
|
cout << "left: "<< *(edge.first);
|
|
else
|
|
cout << "left: "<< 0;
|
|
if (edge.second)
|
|
cout << "right: "<<*(edge.second);
|
|
else
|
|
cout << "right: "<< 0;
|
|
|
|
cout<<std::endl;*/
|
|
|
|
outl.push_back(edge);
|
|
return;
|
|
} else {
|
|
delete edge.first;
|
|
delete edge.second;
|
|
}
|
|
|
|
TThickQuadratic tq_left, tq_rigth;
|
|
|
|
tq->split(0.5, tq_left, tq_rigth);
|
|
|
|
makeOutline(/*out,*/ outl, tq_left, error);
|
|
makeOutline(/*cout,*/ outl, tq_rigth, error);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
void splitCircularArcIntoQuadraticCurves(const TPointD &Center,
|
|
const TPointD &Pstart,
|
|
const TPointD &Pend,
|
|
std::vector<TQuadratic *> &quadArray) {
|
|
// It splits a circular anticlockwise arc into a sequence of quadratic bezier
|
|
// curves
|
|
// Every quadratic curve can approximate an arc no TLonger than 45 degrees (or
|
|
// 60).
|
|
// It supposes that Pstart and Pend are onto the circumference (so that their
|
|
// lengths
|
|
// are equal to tha radius of the circumference), otherwise the resulting
|
|
// curves could
|
|
// be unpredictable.
|
|
// The last component in quadCurve[] is an ending void curve
|
|
|
|
/* ----------------------------------------------------------------------------------
|
|
*/
|
|
// If you want to split the arc into arcs no TLonger than 45 degrees (so that
|
|
// the whole
|
|
// curve will be splitted into 8 pieces) you have to set these constants as
|
|
// follows:
|
|
// cos_ang ==> cos_45 = 0.5 * sqrt(2);
|
|
// sin_ang ==> sin_45 = 0.5 * sqrt(2);
|
|
// tan_semiang ==> tan_22p5 = 0.4142135623730950488016887242097;
|
|
// N_QUAD = 8;
|
|
|
|
// If you want to split the arc into arcs no TLonger than 60 degrees (so that
|
|
// the whole
|
|
// curve will be splitted into 6 pieces) you have to set these constants as
|
|
// follows:
|
|
// cos_ang ==> cos_60 = 0.5;
|
|
// sin_ang ==> sin_60 = 0.5 * sqrt(3);
|
|
// tan_semiang ==> tan_30 = 0.57735026918962576450914878050196;
|
|
// N_QUAD = 6;
|
|
/* ----------------------------------------------------------------------------------
|
|
*/
|
|
|
|
// Defines some useful constant to split the arc into arcs no TLonger than
|
|
// 'ang' degrees
|
|
// (the whole circumference will be splitted into 360/ang quadratic curves).
|
|
const double cos_ang = 0.5 * sqrt(2.0);
|
|
const double sin_ang = 0.5 * sqrt(2.0);
|
|
const double tan_semiang = 0.4142135623730950488016887242097;
|
|
const int N_QUAD = 8; // it's 360/ang
|
|
|
|
// First of all, it computes the vectors from the center to the circumference,
|
|
// in Pstart and Pend, and their cross and dot products
|
|
TPointD Rstart = Pstart - Center; // its length is R (radius of the circle)
|
|
TPointD Rend = Pend - Center; // its length is R (radius of the circle)
|
|
double cross_prod = cross(Rstart, Rend); // it's Rstart x Rend
|
|
double dot_prod = Rstart * Rend;
|
|
const double sqr_radius = Rstart * Rstart;
|
|
TPointD aliasPstart = Pstart;
|
|
TQuadratic *quad;
|
|
|
|
while ((cross_prod <= 0) ||
|
|
(dot_prod <= cos_ang * sqr_radius)) // the circular arc is TLonger
|
|
// than a 'ang' degrees arc
|
|
{
|
|
if ((int)quadArray.size() == N_QUAD) // this is possible if Pstart or Pend
|
|
// is not onto the circumference
|
|
return;
|
|
TPointD Rstart_rot_ang(cos_ang * Rstart.x - sin_ang * Rstart.y,
|
|
sin_ang * Rstart.x + cos_ang * Rstart.y);
|
|
TPointD Rstart_rot_90(-Rstart.y, Rstart.x);
|
|
quad =
|
|
new TQuadratic(aliasPstart, aliasPstart + tan_semiang * Rstart_rot_90,
|
|
Center + Rstart_rot_ang);
|
|
quadArray.push_back(quad);
|
|
|
|
// quad->computeMinStepAtNormalSize ();
|
|
|
|
// And moves anticlockwise the starting point on the circumference by 'ang'
|
|
// degrees
|
|
Rstart = Rstart_rot_ang;
|
|
aliasPstart = quad->getP2();
|
|
cross_prod = cross(Rstart, Rend); // it's Rstart x Rend
|
|
dot_prod = Rstart * Rend;
|
|
|
|
// after the rotation of 'ang' degrees, the remaining part of the arc could
|
|
// be a 0 degree
|
|
// arc, so it must stop and exit from the function
|
|
if ((cross_prod <= 0) && (dot_prod > 0.95 * sqr_radius)) return;
|
|
}
|
|
|
|
if ((cross_prod > 0) && (dot_prod > 0)) // the last quadratic curve
|
|
// approximates an arc shorter than a
|
|
// 'ang' degrees arc
|
|
{
|
|
TPointD Rstart_rot_90(-Rstart.y, Rstart.x);
|
|
|
|
double deg_index = (sqr_radius - dot_prod) / (sqr_radius + dot_prod);
|
|
|
|
quad = new TQuadratic(aliasPstart,
|
|
(deg_index < 0)
|
|
? 0.5 * (aliasPstart + Pend)
|
|
: aliasPstart + sqrt(deg_index) * Rstart_rot_90,
|
|
Pend);
|
|
quadArray.push_back(quad);
|
|
|
|
} else // the last curve, already computed, is as TLong as a 'ang' degrees
|
|
// arc
|
|
quadArray.back()->setP2(Pend);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// copia arrayUp e arrayDown nel vettore dell'outline
|
|
// se le dimensioni sono diverse il vettore con il numero
|
|
// minore di quadratiche viene riempito con quadratiche degeneri
|
|
// con i punti di controllo coincidenti nell'ultimo estremo valido
|
|
void copy(/*std::ofstream& cout,*/
|
|
const std::vector<TQuadratic *> &arrayUp,
|
|
const std::vector<TQuadratic *> &arrayDown, outlineBoundary &ob) {
|
|
int minSize = std::min(arrayUp.size(), arrayDown.size());
|
|
|
|
assert(minSize > 0);
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < minSize; ++i) {
|
|
// cout<<"left: "<< *(arrayUp[i])<< "right: "<<*(arrayDown[i])<<std::endl;
|
|
// cout<"left: "<< arrayUp[i].getP0()<<", "arrayUp[i].getP1()<<",
|
|
// "arrayUp[i].getP2()<< "right: "<< << arrayDown[i].getP0()<<",
|
|
// "arrayDown[i].getP1()<<", "arrayDown[i].getP2()<<endl;
|
|
ob.push_back(outlineEdge(arrayUp[i], arrayDown[i]));
|
|
}
|
|
if (arrayUp.size() != arrayDown.size()) {
|
|
const std::vector<TQuadratic *> &vMaxSize =
|
|
arrayUp.size() > arrayDown.size() ? arrayUp : arrayDown;
|
|
const std::vector<TQuadratic *> &vMinSize =
|
|
arrayUp.size() < arrayDown.size() ? arrayUp : arrayDown;
|
|
|
|
int delta = vMaxSize.size() - vMinSize.size();
|
|
|
|
if (arrayUp.size() > arrayDown.size())
|
|
while (i < minSize + delta) {
|
|
// cout<<"left: "<< arrayUp[i]<< "right: "<< 0<<std::endl;
|
|
ob.push_back(outlineEdge(arrayUp[i], (TQuadratic *)0));
|
|
i++;
|
|
}
|
|
else
|
|
while (i < minSize + delta) {
|
|
// cout<<"left: "<< 0 << "right: "<< arrayDown[i]<<std::endl;
|
|
ob.push_back(outlineEdge((TQuadratic *)0, arrayDown[i]));
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
inline void changeQuadraticDirection(TQuadratic *q) {
|
|
TPointD p = q->getP2();
|
|
q->setP2(q->getP0());
|
|
q->setP0(p);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// cambia il verso del vettore di quadratiche (vedi changeDirection di
|
|
// tstroke.cpp)
|
|
void changeDirection(std::vector<TQuadratic *> &array, bool onlyQuads = false) {
|
|
UINT chunkCount = array.size();
|
|
UINT to = tfloor(chunkCount * 0.5);
|
|
UINT i;
|
|
|
|
if (chunkCount & 1) changeQuadraticDirection(array[to]);
|
|
|
|
--chunkCount;
|
|
|
|
for (i = 0; i < to; ++i) {
|
|
changeQuadraticDirection(array[i]);
|
|
changeQuadraticDirection(array[chunkCount - i]);
|
|
|
|
if (!onlyQuads) std::swap(array[i], array[chunkCount - i]);
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// estrae i punti necessari a costruire la semicirconferenza
|
|
// iniziale e finale di una curva cicciona
|
|
TQuadratic getCircleQuarter(const TThickQuadratic *tq, int versus) {
|
|
TQuadratic out;
|
|
|
|
TPointD v = versus ? -tq->getSpeed(0.0) : tq->getSpeed(1.0);
|
|
|
|
if (norm2(v)) v = normalize(v);
|
|
|
|
TPointD center = versus ? tq->getP0() : tq->getP2();
|
|
double radius = versus ? tq->getThickP0().thick : tq->getThickP2().thick;
|
|
|
|
out.setP0(center + (versus ? rotate270(v) : rotate90(v)) * radius);
|
|
out.setP1(center + v * radius);
|
|
out.setP2(center + (versus ? rotate90(v) : rotate270(v)) * radius);
|
|
|
|
return out;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
void drawQuadratic(const TQuadratic &quad, double pixelSize) {
|
|
double m_min_step_at_normal_size = localComputeStep(quad, pixelSize);
|
|
|
|
// It draws the curve as a linear piecewise approximation
|
|
|
|
double invSqrtScale = 1.0;
|
|
// First of all, it computes the control circles of the curve in screen
|
|
// coordinates
|
|
TPointD scP0 = quad.getP0();
|
|
TPointD scP1 = quad.getP1();
|
|
TPointD scP2 = quad.getP2();
|
|
|
|
TPointD A = scP0 - 2 * scP1 + scP2;
|
|
TPointD B = scP0 - scP1;
|
|
|
|
double h;
|
|
h = invSqrtScale * m_min_step_at_normal_size;
|
|
double h2 = h * h;
|
|
|
|
TPointD P = scP0, D2 = 2 * h2 * A, D1 = A * h2 - 2 * B * h;
|
|
|
|
if (h < 0 || isAlmostZero(h)) return;
|
|
assert(h > 0);
|
|
|
|
// It draws the whole curve, using forward differencing
|
|
glBegin(GL_LINE_STRIP); // The curve starts from scP0
|
|
glVertex2d(scP0.x, scP0.y);
|
|
|
|
for (double t = h; t < 1; t = t + h) {
|
|
P = P + D1;
|
|
D1 = D1 + D2;
|
|
glVertex2d(P.x, P.y);
|
|
}
|
|
|
|
glVertex2d(scP2.x, scP2.y); // The curve ends in scP2
|
|
glEnd();
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
} // end of unnamed namespace
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static void makeOutline(const TStroke *stroke, int startQuad, int endQuad,
|
|
outlineBoundary &ob, double error2) {
|
|
// std::ofstream cout("c:\\temp\\outline.txt");
|
|
|
|
assert(stroke);
|
|
assert(startQuad >= 0);
|
|
assert(endQuad < stroke->getChunkCount());
|
|
assert(startQuad <= endQuad);
|
|
TThickQuadratic *tq;
|
|
std::vector<TQuadratic *> arrayUp, arrayDown;
|
|
TQuadratic arc;
|
|
|
|
if (!stroke->getChunkCount()) return;
|
|
// if (startQuad==0)
|
|
{
|
|
const TThickQuadratic *tq = stroke->getChunk(startQuad);
|
|
|
|
// trova i punti sul cerchio che corrispondono
|
|
// a due fette di 90 gradi.
|
|
// Ritorna una quadratica invece di tre singoli punti solo per compattezza.
|
|
TQuadratic arc = getCircleQuarter(tq, QUARTER_BEGIN);
|
|
|
|
// estrae le quadratiche che corrispondono ad i due archi...
|
|
splitCircularArcIntoQuadraticCurves(tq->getP0(), arc.getP0(), arc.getP1(),
|
|
arrayUp);
|
|
// e le ordina in modo che l'outline sia composta sempre da
|
|
// una curva superiore ed una inferiore corrispondente
|
|
changeDirection(arrayUp);
|
|
splitCircularArcIntoQuadraticCurves(tq->getP0(), arc.getP1(), arc.getP2(),
|
|
arrayDown);
|
|
changeDirection(arrayDown, true);
|
|
// copia le curve nell'outline; se gli array non hanno la stessa dimensione
|
|
// quello con meno curve viene riempito con curve improprie
|
|
// che hanno i punti di controllo coincidente con l'ultimo estremo valido
|
|
// cout<<"quads del semicerchio left:"<<std::endl;
|
|
copy(/*cout, */ arrayUp, arrayDown, ob);
|
|
}
|
|
|
|
for (int i = startQuad; i <= endQuad; ++i) {
|
|
tq = (TThickQuadratic *)stroke->getChunk(i);
|
|
TThickPoint p0 = tq->getThickP0();
|
|
TThickPoint p1 = tq->getThickP1();
|
|
TThickPoint p2 = tq->getThickP2();
|
|
if (p0.x == p1.x) {
|
|
if (p1.x == p2.x &&
|
|
((p1.y > p0.y && p1.y > p2.y) || (p1.y < p0.y && p1.y < p2.y)))
|
|
tq = new TThickQuadratic(p0, 0.5 * (p0 + p1), p1);
|
|
} else if (p0.y == p1.y) {
|
|
if (p0.y == p2.y &&
|
|
((p1.x > p0.x && p1.x > p2.x) || (p1.x < p0.x && p1.x < p2.x)))
|
|
tq = new TThickQuadratic(p0, 0.5 * (p0 + p1), p1);
|
|
} else {
|
|
double fac1 = 1.0 / (p0.x - p1.x);
|
|
double fac2 = 1.0 / (p0.y - p1.y);
|
|
|
|
double aux1 = fac1 * (p2.x - p1.x);
|
|
double aux2 = fac2 * (p2.y - p1.y);
|
|
double aux3 = fac1 * (p0.x - p2.x);
|
|
double aux4 = fac2 * (p0.y - p2.y);
|
|
|
|
if ((areAlmostEqual(aux1, aux2) && aux1 >= 0) ||
|
|
(areAlmostEqual(aux3, aux4) && aux3 >= 0 && aux3 <= 1))
|
|
tq = new TThickQuadratic(p0, 0.5 * (p0 + p1), p1);
|
|
}
|
|
|
|
// cout<<"quad# "<<i<<":" <<*tq<<std::endl;
|
|
makeOutline(/*cout, */ ob, *tq, error2);
|
|
if (tq != stroke->getChunk(i)) delete tq;
|
|
}
|
|
|
|
arrayUp.clear();
|
|
arrayDown.clear();
|
|
|
|
// come sopra ultimo pezzo di arco
|
|
// if (endQuad==stroke->getChunkCount()-1)
|
|
{
|
|
arc = getCircleQuarter(tq, QUARTER_END);
|
|
splitCircularArcIntoQuadraticCurves(tq->getP2(), arc.getP1(), arc.getP0(),
|
|
arrayUp);
|
|
changeDirection(arrayUp);
|
|
splitCircularArcIntoQuadraticCurves(tq->getP2(), arc.getP2(), arc.getP1(),
|
|
arrayDown);
|
|
changeDirection(arrayDown, true);
|
|
// cout<<"quads del semicerchio right:"<<std::endl;
|
|
|
|
copy(/*cout,*/ arrayUp, arrayDown, ob);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static void drawOutline(const outlineBoundary &ob, double pixelSize) {
|
|
for (UINT i = 0; i < ob.size(); ++i) {
|
|
drawQuadratic(*ob[i].first, pixelSize);
|
|
drawQuadratic(*ob[i].second, pixelSize);
|
|
}
|
|
}
|
|
|
|
void computeOutlines(const TStroke *stroke, int startQuad, int endQuad,
|
|
std::vector<TQuadratic *> &quadArray, double error2) {
|
|
outlineBoundary ob;
|
|
makeOutline(stroke, startQuad, endQuad, ob, error2);
|
|
|
|
assert(quadArray.empty());
|
|
quadArray.resize(ob.size() * 2);
|
|
|
|
int i, count = 0;
|
|
for (i = 0; i < (int)ob.size(); i++)
|
|
if (ob[i].first) quadArray[count++] = ob[i].first;
|
|
|
|
for (i = (int)ob.size() - 1; i >= 0; i--)
|
|
if (ob[i].second) quadArray[count++] = ob[i].second;
|
|
|
|
quadArray.resize(count);
|
|
for (i = 0; i < (int)quadArray.size(); i++) quadArray[i]->reverse();
|
|
|
|
std::reverse(quadArray.begin(), quadArray.end());
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// End Of File
|
|
//-----------------------------------------------------------------------------
|