int isAscending(const TThickQuadratic &q, double t, bool forward) { double y0 = y(q.getP0()), y1 = y(q.getP1()), y2 = y(q.getP2()), y1_y0 = y1 - y0, y2_y1 = y2 - y1; double yspeed_2 = tcg::numeric_ops::lerp(y1_y0, y2_y1, t) * (2 * int(forward) - 1), yaccel = y2_y1 - y1_y0; return (yspeed_2 > 0.0) ? 1 : (yspeed_2 < 0.0) ? -1 : tcg::numeric_ops::sign(yaccel); }
double computeStep(const TThickQuadratic &quad, double pixelSize) { TThickPoint cp0 = quad.getThickP0(), cp1 = quad.getThickP1(), cp2 = quad.getThickP2(); TQuadratic q1(TPointD(cp0.x, cp0.y), TPointD(cp1.x, cp1.y), TPointD(cp2.x, cp2.y)), q2(TPointD(cp0.y, cp0.thick), TPointD(cp1.y, cp1.thick), TPointD(cp2.y, cp2.thick)), q3(TPointD(cp0.x, cp0.thick), TPointD(cp1.x, cp1.thick), TPointD(cp2.x, cp2.thick)); return std::min({computeStep(q1, pixelSize), computeStep(q2, pixelSize), computeStep(q3, pixelSize)}); }
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); } }