void ControlPointEditorStroke::adjustChunkParity() { TStroke *stroke = getStroke(); if (!stroke) return; int firstChunk; int secondChunk = stroke->getChunkCount(); int i; for (i = stroke->getChunkCount() - 1; i > 0; i--) { if (tdistance(stroke->getChunk(i - 1)->getP0(), stroke->getChunk(i)->getP2()) < 0.5) continue; TPointD p0 = stroke->getChunk(i - 1)->getP1(); TPointD p1 = stroke->getChunk(i - 1)->getP2(); TPointD p2 = stroke->getChunk(i)->getP1(); if (isCuspPoint(p0, p1, p2) || isLinearPoint(p0, p1, p2)) { firstChunk = i; insertPoint(stroke, firstChunk, secondChunk); secondChunk = firstChunk; } } insertPoint(stroke, 0, secondChunk); }
void ControlPointEditorStroke::setStroke(const TVectorImageP &vi, int strokeIndex) { m_strokeIndex = strokeIndex; m_vi = vi; if (!vi || strokeIndex == -1) { m_controlPoints.clear(); return; } TStroke *stroke = getStroke(); const TThickQuadratic *chunk = stroke->getChunk(0); if (stroke->getControlPointCount() == 3 && chunk->getP0() == chunk->getP1() && chunk->getP0() == chunk->getP2()) { resetControlPoints(); return; } adjustChunkParity(); resetControlPoints(); }
void ControlPointEditorStroke::resetControlPoints() { TStroke *stroke = getStroke(); if (!stroke) return; m_controlPoints.clear(); int i; int cpCount = stroke->getControlPointCount(); if (cpCount == 3) { const TThickQuadratic *chunk = stroke->getChunk(0); if (chunk->getP0() == chunk->getP1() && chunk->getP0() == chunk->getP2()) // E' un punto { m_controlPoints.push_back( ControlPoint(0, TPointD(0.0, 0.0), TPointD(0.0, 0.0), true)); return; } } for (i = 0; i < cpCount; i = i + 4) { TThickPoint speedIn, speedOut; bool isPickOut = false; TThickPoint p = stroke->getControlPoint(i); TThickPoint precP = stroke->getControlPoint(i - 1); TThickPoint nextP = stroke->getControlPoint(i + 1); if (0 < i && i < cpCount - 1) // calcola speedIn e speedOut { speedIn = p - precP; speedOut = nextP - p; } if (i == 0) // calcola solo lo speedOut { speedOut = nextP - p; if (isSelfLoop()) speedIn = p - stroke->getControlPoint(cpCount - 2); } if (i == cpCount - 1) // calcola solo lo speedIn speedIn = p - precP; if (i == cpCount - 1 && isSelfLoop()) break; // Se lo stroke e' selfLoop inserisco solo il primo dei due punti // coincidenti bool isCusp = ((i != 0 && i != cpCount - 1) || (isSelfLoop() && i == 0)) ? isCuspPoint(precP, p, nextP) : true; m_controlPoints.push_back(ControlPoint(i, speedIn, speedOut, isCusp)); } }
void TRegion::Imp::computeScanlineIntersections(double y, vector<double> &intersections) const { TRectD bbox = getBBox(); if (y <= bbox.y0 || y >= bbox.y1) return; assert(intersections.empty()); UINT i, firstSide = 0; vector<int> sides; for (i = 0; i < m_edge.size(); i++) { TEdge *e = m_edge[i]; TStroke *s = e->m_s; if (s->getBBox().y0 > y || s->getBBox().y1 < y) continue; int chunkIndex0, chunkIndex1; double t0, t1; s->getChunkAndT(e->m_w0, chunkIndex0, t0); s->getChunkAndT(e->m_w1, chunkIndex1, t1); if (chunkIndex0 > chunkIndex1) { findIntersections(y, *s->getChunk(chunkIndex0), t0, 0, intersections, sides); for (int j = chunkIndex0 - 1; j > chunkIndex1; j--) findIntersections(y, *s->getChunk(j), 1, 0, intersections, sides); findIntersections(y, *s->getChunk(chunkIndex1), 1, t1, intersections, sides); } else if (chunkIndex0 < chunkIndex1) { findIntersections(y, *s->getChunk(chunkIndex0), t0, 1, intersections, sides); for (int j = chunkIndex0 + 1; j < chunkIndex1; j++) findIntersections(y, *s->getChunk(j), 0, 1, intersections, sides); findIntersections(y, *s->getChunk(chunkIndex1), 0, t1, intersections, sides); } else { findIntersections(y, *s->getChunk(chunkIndex0), t0, t1, intersections, sides); } } if (intersections.size() > 0 && intersections.front() == intersections.back()) { intersections.pop_back(); if (!sides.empty() && sides.front() == sides.back() && intersections.size() > 0) intersections.erase(intersections.begin()); } std::sort(intersections.begin(), intersections.end()); assert(intersections.size() % 2 == 0); }
void tglDraw(const TVectorRenderData &rd, TRegion *r, bool pushAttribs) { checkErrorsByGL; assert(r); checkErrorsByGL; if (!r) return; bool alphaChannel = rd.m_alphaChannel; checkErrorsByGL; int j = 0; bool visible = false; int colorCount = 0; TColorStyleP style; if (rd.m_paintCheckEnabled && r->getStyle() == rd.m_colorCheckIndex) { static TSolidColorStyle *redColor = new TSolidColorStyle(); redColor->addRef(); redColor->setMainColor(TPixel::Red); style = redColor; } else if (rd.m_tcheckEnabled) { static TSolidColorStyle *color = new TSolidColorStyle(); color->addRef(); color->setMainColor(rd.m_tCheckPaint); style = color; } else style = rd.m_palette->getStyle(r->getStyle()); colorCount = style->getColorParamCount(); if (colorCount == 0) { // for example texture visible = true; } else { visible = false; for (j = 0; j < colorCount && !visible; j++) { TPixel32 color = style->getColorParamValue(j); if (rd.m_cf) color = (*(rd.m_cf))(color); if (color.m != 0) visible = true; } } if (visible) { TRegionProp *prop = r->getProp(/*rd.m_palette*/); /// questo codice satva dentro tregion::getprop///// int styleId = r->getStyle(); if (styleId) { // TColorStyle * style = rd.m_palette->getStyle(styleId); if (!style->isRegionStyle() || style->isEnabled() == false) { prop = 0; } else { // Warning: The same remark of stroke props holds here. if (!prop || style.getPointer() != prop->getColorStyle()) { r->setProp(style->makeRegionProp(r)); prop = r->getProp(); } } } ////// draw if (prop) { if (pushAttribs) glPushAttrib(GL_ALL_ATTRIB_BITS); tglEnableLineSmooth(true); //#define DRAW_EDGE_NUMBERS #ifdef DRAW_EDGE_NUMBERS glPushMatrix(); tglMultMatrix(rd.m_aff); switch (Index % 7) { case 0: tglColor(TPixel::Red); break; case 1: tglColor(TPixel::Green); break; case 2: tglColor(TPixel::Blue); break; case 3: tglColor(TPixel::Cyan); break; case 4: tglColor(TPixel::Magenta); break; case 5: tglColor(TPixel::Yellow); break; case 6: tglColor(TPixel::Black); break; default: tglColor(TPixel::Red); break; } Index++; if (rIndex == 2) { double y = r->getEdge(0) ->m_s ->getThickPoint( (r->getEdge(0)->m_w0 + r->getEdge(0)->m_w1) / 2.0) .y; tglDrawSegment(TPointD(-1000, y), TPointD(1000, y)); } for (int i = 0; i < (int)r->getEdgeCount(); i++) { TEdge *e = r->getEdge(i); TPointD p = e->m_s->getPoint(0.8 * e->m_w0 + 0.2 * e->m_w1); if (i == 0) tglDrawText(p, (QString::number(rIndex) + QString("-0")).toStdString()); else tglDrawText(p, QString::number(i).toStdString()); if (e->m_index == 3) { tglColor(TPixel::Black); TStroke *s = e->m_s; drawPoint(s->getChunk(0)->getP0(), .3); tglColor(TPixel::Red); tglDrawText(s->getChunk(0)->getP0(), QString::number(0).toStdString()); for (int ii = 0; ii < s->getChunkCount(); ii++) { drawPoint(s->getChunk(ii)->getP2(), .3); if (ii < s->getChunkCount() - 1) { tglColor(TPixel::Red); tglDrawText(s->getChunk(ii)->getP2(), QString::number(ii + 1).toStdString()); } } } } glPopMatrix(); #endif if (alphaChannel) { GLboolean red, green, blue, alpha; tglGetColorMask(red, green, blue, alpha); // Draw RGB channels tglEnableBlending(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glColorMask(red, green, blue, GL_FALSE); prop->draw(rd); // Draw Matte channel tglEnableBlending(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, alpha); prop->draw(rd); glColorMask(red, green, blue, alpha); } else { // pezza: in render, le aree fillate dei custom styles sparivano. if (!rd.m_isOfflineRender || !rd.m_isImagePattern) tglRgbOnlyColorMask(); // RGB components only prop->draw(rd); } if (pushAttribs) glPopAttrib(); } } for (UINT i = 0; i < r->getSubregionCount(); i++) tglDraw(rd, r->getSubregion(i), pushAttribs); checkErrorsByGL; }
int TRegion::Imp::leftScanlineIntersections( const TPointD &p, double(TPointD::*h), double(TPointD::*v)) const { struct Locals { const Imp *m_this; double m_x, m_y, m_tol; double TPointD::*m_h, TPointD::*m_v; inline double x(const TPointD &p) const { return p.*m_h; } inline double y(const TPointD &p) const { return p.*m_v; } inline double get(const TQuadratic &q, double t, double(TPointD::*val)) const { double one_t = 1.0 - t; return one_t * (one_t * q.getP0().*val + t * q.getP1().*val) + t * (one_t * q.getP1().*val + t * q.getP2().*val); } inline double getX(const TQuadratic &q, double t) const { return get(q, t, m_h); } inline double getY(const TQuadratic &q, double t) const { return get(q, t, m_v); } void getEdgeData(int e, TEdge *&ed, TStroke *&s, int &chunk0, const TThickQuadratic *&q0, double &t0, int &chunk1, const TThickQuadratic *&q1, double &t1) const { ed = m_this->m_edge[e]; s = ed->m_s; s->getChunkAndT(ed->m_w0, chunk0, t0); s->getChunkAndT(ed->m_w1, chunk1, t1); q0 = s->getChunk(chunk0); q1 = s->getChunk(chunk1); } bool isInYRange(double y0, double y1) const { return (y0 <= m_y && m_y < y1) // Assuming the first endpoint is vertical-including, || (y1 < m_y && m_y <= y0); // while the latter is not. Vertical conditions are EXACT. } bool areInYRange(const TQuadratic &q, double t0, double t1, int(&solIdx)[2]) const { assert(0.0 <= t0 && t0 <= 1.0), assert(0.0 <= t1 && t1 <= 1.0); const TPointD &p0 = q.getP0(), &p1 = q.getP1(), &p2 = q.getP2(); double der[2] = {y(p1) - y(p0), y(p0) - y(p1) + y(p2) - y(p1)}, s; double y0 = getY(q, t0), y1 = getY(q, t1); if (tcg::poly_ops::solve_1(der, &s, m_tol)) { if (t0 <= s && s < t1) { double ys = getY(q, s); solIdx[0] = (ys < m_y && m_y <= y0 || y0 <= m_y && m_y < ys) ? 0 : -1; solIdx[1] = (ys < m_y && m_y < y1 || y1 < m_y && m_y < ys) ? 1 : -1; } else if (t1 < s && s <= t0) { double ys = getY(q, s); solIdx[0] = (ys < m_y && m_y <= y0 || y0 <= m_y && m_y < ys) ? 1 : -1; solIdx[1] = (ys < m_y && m_y < y1 || y1 < m_y && m_y < ys) ? 0 : -1; } else { solIdx[0] = isInYRange(y0, y1) ? (t0 < s) ? 0 : 1 : -1; solIdx[1] = -1; } } else solIdx[1] = solIdx[0] = -1; return (solIdx[0] >= 0 || solIdx[1] >= 0); } int leftScanlineIntersections(const TSegment &seg, bool &ascending) { const TPointD &p0 = seg.getP0(), &p1 = seg.getP1(); bool wasAscending = ascending; ascending = (y(p1) > y(p0)) ? true : (y(p1) < y(p0)) ? false : (wasAscending = !ascending, ascending); // Couples with the cusp check below if (!isInYRange(y(p0), y(p1))) return 0; if (m_y == y(p0)) return int(x(p0) < m_x && ascending == wasAscending); // Cusps treated here double y1_y0 = y(p1) - y(p0), // (x, m_y) in (p0, p1) from here on poly[2] = {(m_y - y(p0)) * (x(p1) - x(p0)), -y1_y0}, sx_x0; return tcg::poly_ops::solve_1(poly, &sx_x0, m_tol) ? int(sx_x0 < m_x - x(p0)) : int(x(p0) < m_x && x(p1) < m_x); // Almost horizontal segments are } // flattened along the axes 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); } int leftScanlineIntersections(const TQuadratic &q, double t0, double t1, bool &ascending) { const TPointD &p0 = q.getP0(), &p1 = q.getP1(), &p2 = q.getP2(); double y1_y0 = y(p1) - y(p0), accel = y(p2) - y(p1) - y1_y0; // Fallback to segment case whenever we have too flat quads if (std::fabs(accel) < m_tol) return leftScanlineIntersections(TSegment(q.getPoint(t0), q.getPoint(t1)), ascending); // Calculate new ascension int ascends = isAscending(q, t1, t0 < t1); bool wasAscending = ascending; ascending = (ascends > 0) ? true : (ascends < 0) ? false : (wasAscending = !ascending, ascending); // Couples with the cusps check below // In case the y coords are not in range, quit int solIdx[2]; if (!areInYRange(q, t0, t1, solIdx)) return 0; // Identify coordinates for which q(t) == y double poly[3] = {y(p0) - m_y, 2.0 * y1_y0, accel}, s[2]; int sCount = tcg::poly_ops::solve_2(poly, s); // Tolerance dealt at the first bailout above if (sCount == 2) { // Calculate result int result = 0; if (solIdx[0] >= 0) { result += int(getX(q, s[solIdx[0]]) < m_x && (getY(q, t0) != m_y || ascending == wasAscending)); // Cusp check } if (solIdx[1] >= 0) result += int(getX(q, s[solIdx[1]]) < m_x); return result; } return (assert(sCount == 0), 0); // Should never happen, since m_y is in range. If it ever happens, // it must be close to the extremal - so quit with no intersections. } } locals = {this, p.*h, p.*v, 1e-4, h, v}; TEdge *ed; TStroke *s; int chunk0, chunk1; const TThickQuadratic *q0, *q1; double t0, t1; UINT e, eCount = m_edge.size(); int leftInters = 0; bool ascending = (locals.getEdgeData(eCount - 1, ed, s, chunk0, q0, t0, chunk1, q1, t1), locals.isAscending(*q1, t1, (t0 < t1))); for (e = 0; e != eCount; ++e) { // Explore current edge { // Retrieve edge data locals.getEdgeData(e, ed, s, chunk0, q0, t0, chunk1, q1, t1); // Compare edge against scanline segment if (chunk0 != chunk1) { if (chunk0 < chunk1) { leftInters += locals.leftScanlineIntersections(*q0, t0, 1.0, ascending); for (int c = chunk0 + 1; c != chunk1; ++c) leftInters += locals.leftScanlineIntersections(*s->getChunk(c), 0.0, 1.0, ascending); leftInters += locals.leftScanlineIntersections(*q1, 0.0, t1, ascending); } else { leftInters += locals.leftScanlineIntersections(*q0, t0, 0.0, ascending); for (int c = chunk0 - 1; c != chunk1; --c) leftInters += locals.leftScanlineIntersections(*s->getChunk(c), 1.0, 0.0, ascending); leftInters += locals.leftScanlineIntersections(*q1, 1.0, t1, ascending); } } else leftInters += locals.leftScanlineIntersections(*q0, t0, t1, ascending); } // Explore autoclose segment at the end of current edge { int nextE = (e + 1) % int(m_edge.size()); const TPointD &p0 = m_edge[e]->m_s->getPoint(m_edge[e]->m_w1), &p1 = m_edge[nextE]->m_s->getPoint(m_edge[nextE]->m_w0); leftInters += locals.leftScanlineIntersections(TSegment(p0, p1), ascending); } } return leftInters; }
//----------------------------------------------------------------------------- bool TRegion::Imp::contains(const TPointD &p) const { bool leftAreOdd = false; if (!getBBox().contains(p)) return false; //printContains(p); UINT i; int side = 0; for (i = 0; i < m_edge.size() * 2; i++) //i pari, esplora gli edge, //i dispari esplora i segmenti di autoclose tra un edge e il successivo { if (i & 0x1) { TPointD p0 = m_edge[i / 2]->m_s->getPoint(m_edge[i / 2]->m_w1); TPointD p1; if (i / 2 < m_edge.size() - 1) p1 = m_edge[i / 2 + 1]->m_s->getPoint(m_edge[i / 2 + 1]->m_w0); else p1 = m_edge[0]->m_s->getPoint(m_edge[0]->m_w0); if (tmin(p0.y, p1.y) > p.y || tmax(p0.y, p1.y) < p.y) continue; if (!areAlmostEqual(p0, p1, 1e-2)) side = findSides(p, TQuadratic(p0, 0.5 * (p0 + p1), p1), 0.0, 1.0, leftAreOdd, side); continue; } TEdge *e = m_edge[i / 2]; TStroke *s = e->m_s; if (s->getBBox().y0 > p.y || s->getBBox().y1 < p.y) continue; double t0, t1; int chunkIndex0, chunkIndex1; const TThickQuadratic *q0, *q1; s->getChunkAndT(e->m_w0, chunkIndex0, t0); s->getChunkAndT(e->m_w1, chunkIndex1, t1); q0 = s->getChunk(chunkIndex0); q1 = s->getChunk(chunkIndex1); if (i == 0 && areAlmostEqual(q0->getPoint(t0).y, p.y)) { double tEnd; int chunkIndexEnd; TEdge *edgeEnd = m_edge.back(); edgeEnd->m_s->getChunkAndT(edgeEnd->m_w1, chunkIndexEnd, tEnd); assert(areAlmostEqual(edgeEnd->m_s->getChunk(chunkIndexEnd)->getPoint(tEnd).y, p.y)); side = edgeEnd->m_s->getChunk(chunkIndexEnd)->getSpeed(tEnd).y > 0 ? 1 : -1; } if (chunkIndex0 != chunkIndex1) { /*if (chunkIndex0>chunkIndex1) { tswap(chunkIndex0, chunkIndex1); tswap(t0, t1); }*/ if (chunkIndex0 > chunkIndex1) { side = findSides(p, *q0, t0, 0, leftAreOdd, side); for (int j = chunkIndex0 - 1; j > chunkIndex1; j--) side = findSides(p, *s->getChunk(j), 1, 0, leftAreOdd, side); side = findSides(p, *q1, 1, t1, leftAreOdd, side); } else { side = findSides(p, *q0, t0, 1, leftAreOdd, side); for (int j = chunkIndex0 + 1; j < chunkIndex1; j++) side = findSides(p, *s->getChunk(j), 0, 1, leftAreOdd, side); side = findSides(p, *q1, 0, t1, leftAreOdd, side); } } else side = findSides(p, *q0, t0, t1, leftAreOdd, side); if (i & 0x1) delete q0; } return leftAreOdd; }