| |
| |
| #include "tregion.h" |
| #include "tregionoutline.h" |
| #include "tellipticbrushP.h" |
| |
| #include "tstrokeoutline.h" |
| |
| using namespace tellipticbrush; |
| |
| #define USE_LENGTH |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| namespace |
| { |
| |
| double getX(const TThickPoint &P0, const TThickPoint &P1, const TThickPoint &P2, double t) |
| { |
| double one_t = 1.0 - t; |
| return P0.x * sq(one_t) + 2.0 * P1.x * t * one_t + P2.x * sq(t); |
| } |
| |
| |
| |
| TPointD normal(const TPointD &p, bool left) |
| { |
| TPointD n(-p.y, p.x); |
| return (1.0 / norm(n)) * (left ? n : -n); |
| } |
| |
| |
| |
| TPointD normal(const TPointD &p) |
| { |
| return (1.0 / norm(p)) * TPointD(-p.y, p.x); |
| } |
| |
| |
| |
| void getHRange(const TThickQuadratic &ttq, double &x0, double &x1) |
| { |
| const TPointD &P0 = ttq.getP0(); |
| const TPointD &P1 = ttq.getP1(); |
| const TPointD &P2 = ttq.getP2(); |
| |
| |
| x0 = tmin(x0, P0.x, P2.x), x1 = tmax(x1, P0.x, P2.x); |
| |
| double t = (P0.x - P1.x) / (P0.x + P2.x - 2.0 * P1.x); |
| if (t > 0.0 && t < 1.0) { |
| double x = getX(P0, P1, P2, t); |
| x0 = tmin(x0, x), x1 = tmax(x1, x); |
| } |
| } |
| |
| void getHRange(const TThickQuadratic &ttq, double t0, double t1, double &x0, double &x1) |
| { |
| const TPointD &P0 = ttq.getP0(); |
| const TPointD &P1 = ttq.getP1(); |
| const TPointD &P2 = ttq.getP2(); |
| |
| double x0_ = getX(P0, P1, P2, t0); |
| double x1_ = getX(P0, P1, P2, t1); |
| |
| |
| x0 = tmin(x0, x0_, x1_), x1 = tmax(x1, x0_, x1_); |
| |
| double t = (P0.x - P1.x) / (P0.x + P2.x - 2.0 * P1.x); |
| if (t > t0 && t < t1) { |
| double x = getX(P0, P1, P2, t); |
| x0 = tmin(x0, x), x1 = tmax(x1, x); |
| } |
| } |
| |
| void getHRange(const TStroke &stroke, double &x0, double &x1) |
| { |
| int i, nChunks = stroke.getChunkCount(); |
| for (i = 0; i < nChunks; ++i) |
| getHRange(*stroke.getChunk(i), x0, x1); |
| } |
| |
| |
| |
| |
| |
| struct StrokeOutlinizationData : public tellipticbrush::OutlinizationData { |
| double m_x0, m_x1, m_xRange; |
| double m_y0, m_yScale; |
| |
| public: |
| StrokeOutlinizationData() |
| : OutlinizationData() {} |
| |
| StrokeOutlinizationData(const TStroke &stroke, const TRectD &strokeBox, |
| const TOutlineUtil::OutlineParameter &options) |
| : OutlinizationData(options), m_x0(strokeBox.x0), m_x1(strokeBox.x1), m_xRange(m_x1 - m_x0), m_y0(0.5 * (strokeBox.y0 + strokeBox.y1)), m_yScale(1.0 / (strokeBox.y1 - strokeBox.y0)) {} |
| |
| void buildPoint(const CenterlinePoint &p, bool isNextD, CenterlinePoint &ref, bool isRefNextD, |
| CenterlinePoint &out); |
| int buildPoints(const CenterlinePoint &p, CenterlinePoint &ref, CenterlinePoint *out); |
| int buildPoints(const TStroke &stroke, const TStroke &path, |
| CenterlinePoint &cp, CenterlinePoint *out); |
| |
| bool getChunkAndT_param(const TStroke &path, double x, int &chunk, double &t); |
| bool getChunkAndT_length(const TStroke &path, double x, int &chunk, double &t); |
| |
| double toW(double x); |
| }; |
| |
| |
| |
| double StrokeOutlinizationData::toW(double x) |
| { |
| return tcrop((x - m_x0) / m_xRange, 0.0, 1.0); |
| } |
| |
| |
| |
| bool StrokeOutlinizationData::getChunkAndT_param(const TStroke &path, double x, int &chunk, double &t) |
| { |
| double w = toW(x); |
| return !path.getChunkAndT(w, chunk, t); |
| } |
| |
| |
| |
| bool StrokeOutlinizationData::getChunkAndT_length(const TStroke &path, double x, int &chunk, double &t) |
| { |
| double s = toW(x) * path.getLength(); |
| return !path.getChunkAndTAtLength(s, chunk, t); |
| } |
| |
| |
| |
| void StrokeOutlinizationData::buildPoint(const CenterlinePoint &p, bool pNextD, |
| CenterlinePoint &ref, bool refNextD, |
| CenterlinePoint &out) |
| { |
| TThickPoint &refD = refNextD ? ref.m_nextD : ref.m_prevD; |
| |
| const TThickPoint *pD; |
| TThickPoint *outD; |
| bool *outHasD; |
| if (pNextD) { |
| pD = &p.m_nextD; |
| outD = &out.m_nextD; |
| outHasD = &out.m_hasNextD; |
| } else { |
| pD = &p.m_prevD; |
| outD = &out.m_prevD; |
| outHasD = &out.m_hasPrevD; |
| } |
| |
| |
| refD = (1.0 / norm(refD)) * refD; |
| TPointD normalDirection(-refD.y, refD.x); |
| |
| double yPercentage = (p.m_p.y - m_y0) * m_yScale; |
| double yRelative = yPercentage * ref.m_p.thick; |
| double yFactor = ref.m_p.thick * m_yScale; |
| |
| out.m_p = TThickPoint( |
| ref.m_p.x + yRelative * normalDirection.x, |
| ref.m_p.y + yRelative * normalDirection.y, |
| p.m_p.thick * yFactor); |
| |
| |
| double stretchedDY = pD->x * yPercentage * refD.thick + pD->y * yFactor; |
| |
| *outD = TThickPoint( |
| refD.x * pD->x - refD.y * stretchedDY, |
| refD.y * pD->x + refD.x * stretchedDY, |
| pD->thick * (1.0 + refD.thick)); |
| |
| bool covered = (sq(outD->x) + sq(outD->y) < sq(outD->thick) + tolPar); |
| out.m_covered = out.m_covered && covered; |
| |
| *outHasD = *outHasD && !covered; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| int StrokeOutlinizationData::buildPoints(const CenterlinePoint &p, CenterlinePoint &ref, |
| CenterlinePoint *out) |
| { |
| out[0] = out[1] = p; |
| out[0].m_covered = out[1].m_covered = true; |
| |
| bool refSymmetric = ref.m_hasPrevD && ref.m_hasNextD && ref.m_nextD == ref.m_prevD; |
| bool pSymmetric = p.m_hasPrevD && p.m_hasNextD && p.m_nextD == p.m_prevD; |
| |
| |
| bool prevSideIsNext = (p.m_prevD.x < 0) ? true : (p.m_prevD.x > 0) ? false : ref.m_hasNextD; |
| bool hasPrev = p.m_hasPrevD && (prevSideIsNext ? ref.m_hasNextD : ref.m_hasPrevD); |
| int prevIdx = hasPrev ? 0 : -1; |
| |
| if (hasPrev) { |
| CenterlinePoint &outPoint = out[prevIdx]; |
| buildPoint(p, false, ref, prevSideIsNext, outPoint); |
| } |
| |
| if (refSymmetric && pSymmetric) { |
| |
| if (hasPrev) { |
| CenterlinePoint &outPoint = out[prevIdx]; |
| |
| outPoint.m_hasNextD = outPoint.m_hasPrevD; |
| outPoint.m_nextD = outPoint.m_prevD; |
| |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| |
| bool nextSideIsNext = (p.m_nextD.x > 0) ? true : (p.m_nextD.x < 0) ? false : ref.m_hasNextD; |
| bool hasNext = p.m_hasNextD && (nextSideIsNext ? ref.m_hasNextD : ref.m_hasPrevD); |
| int nextIdx = hasNext ? hasPrev ? ((int)prevSideIsNext != nextSideIsNext) : 0 : -1; |
| |
| if (hasNext) { |
| CenterlinePoint &outPoint = out[nextIdx]; |
| buildPoint(p, true, ref, nextSideIsNext, outPoint); |
| } |
| |
| |
| if (hasPrev && hasNext && prevIdx != nextIdx) { |
| CenterlinePoint &outPrev = out[prevIdx]; |
| CenterlinePoint &outNext = out[nextIdx]; |
| |
| if (dist(outPrev.m_p, outNext.m_p) > 1e-4) { |
| |
| outPrev.m_nextD = outNext.m_prevD = 0.5 * (outNext.m_p - outPrev.m_p); |
| bool covered = (sq(outPrev.m_nextD.x) + sq(outPrev.m_nextD.y) < |
| sq(outPrev.m_nextD.thick) + tolPar); |
| |
| outPrev.m_hasNextD = outNext.m_hasPrevD = !covered; |
| outPrev.m_covered = outPrev.m_covered && covered; |
| outNext.m_covered = outNext.m_covered && covered; |
| } else { |
| |
| nextIdx = prevIdx; |
| |
| outPrev.m_nextD = outNext.m_nextD; |
| outPrev.m_hasNextD = outNext.m_hasPrevD; |
| outPrev.m_covered = outPrev.m_covered && outNext.m_covered; |
| } |
| } |
| |
| return tmax(prevIdx, nextIdx) + 1; |
| } |
| |
| |
| |
| int StrokeOutlinizationData::buildPoints(const TStroke &stroke, const TStroke &path, |
| CenterlinePoint &cp, CenterlinePoint *out) |
| { |
| const TThickQuadratic &ttq = *stroke.getChunk(cp.m_chunkIdx); |
| |
| const TThickPoint &P0 = ttq.getP0(); |
| const TThickPoint &P1 = ttq.getP1(); |
| const TThickPoint &P2 = ttq.getP2(); |
| |
| double x = getX(P0, P1, P2, cp.m_t); |
| |
| double pathT; |
| int pathChunk; |
| #ifdef USE_LENGTH |
| bool ok = getChunkAndT_length(path, x, pathChunk, pathT); |
| #else |
| bool ok = getChunkAndT_param(path, x, pathChunk, pathT); |
| #endif |
| assert(ok); |
| |
| CenterlinePoint pathCp(pathChunk, pathT); |
| |
| cp.buildPos(stroke); |
| cp.buildDirs(stroke); |
| pathCp.buildPos(path); |
| pathCp.buildDirs(path); |
| |
| return buildPoints(cp, pathCp, out); |
| } |
| |
| |
| |
| |
| |
| class ReferenceLinearizator : public tellipticbrush::StrokeLinearizator |
| { |
| protected: |
| const TStroke *m_path; |
| StrokeOutlinizationData m_data; |
| |
| public: |
| ReferenceLinearizator(const TStroke *stroke, const TStroke *path, const StrokeOutlinizationData &data); |
| |
| virtual void linearize(std::vector<CenterlinePoint> &cPoints, int chunk, double t1) = 0; |
| }; |
| |
| |
| |
| ReferenceLinearizator::ReferenceLinearizator(const TStroke *stroke, const TStroke *path, |
| const StrokeOutlinizationData &data) |
| : StrokeLinearizator(stroke), m_path(path), m_data(data) |
| { |
| } |
| |
| |
| |
| |
| |
| class ReferenceChunksLinearizator : public ReferenceLinearizator |
| { |
| double m_w0, m_w1; |
| |
| public: |
| ReferenceChunksLinearizator(const TStroke *stroke, const TStroke *path, |
| const StrokeOutlinizationData &data) |
| : ReferenceLinearizator(stroke, path, data) {} |
| |
| void linearize(std::vector<CenterlinePoint> &cPoints, int chunk); |
| void linearize(std::vector<CenterlinePoint> &cPoints, int chunk, double t1); |
| |
| void addCenterlinePoints(std::vector<CenterlinePoint> &cPoints, |
| int brushChunk, double x0, double x1); |
| void addCenterlinePoints(std::vector<CenterlinePoint> &cPoints, |
| int strokeChunk, double strokeT, int refChunk); |
| }; |
| |
| |
| |
| void ReferenceChunksLinearizator::linearize(std::vector<CenterlinePoint> &cPoints, int chunk) |
| { |
| |
| const TThickQuadratic &ttq = *this->m_stroke->getChunk(chunk); |
| |
| |
| double x0 = (std::numeric_limits<double>::max)(), x1 = -x0; |
| getHRange(ttq, x0, x1); |
| |
| |
| |
| addCenterlinePoints(cPoints, chunk, x0, x1); |
| } |
| |
| |
| |
| void ReferenceChunksLinearizator::linearize(std::vector<CenterlinePoint> &cPoints, int chunk, |
| double t1) |
| { |
| if (cPoints.empty()) |
| return; |
| |
| |
| const TThickQuadratic &ttq = *this->m_stroke->getChunk(chunk); |
| |
| |
| double x0 = (std::numeric_limits<double>::max)(), x1 = -x0; |
| getHRange(ttq, cPoints[0].m_t, t1, x0, x1); |
| |
| |
| |
| |
| addCenterlinePoints(cPoints, chunk, x0, x1); |
| } |
| |
| |
| |
| void ReferenceChunksLinearizator::addCenterlinePoints(std::vector<CenterlinePoint> &cPoints, |
| int chunk, double x0, double x1) |
| { |
| const TThickQuadratic &ttq = *this->m_stroke->getChunk(chunk); |
| const TStroke &path = *this->m_path; |
| |
| int chunk0, chunk1; |
| double t0, t1; |
| |
| #ifdef USE_LENGTH |
| bool ok0 = m_data.getChunkAndT_length(path, x0, chunk0, t0); |
| bool ok1 = m_data.getChunkAndT_length(path, x1, chunk1, t1); |
| #else |
| bool ok0 = m_data.getChunkAndT_param(path, x0, chunk0, t0); |
| bool ok1 = m_data.getChunkAndT_param(path, x1, chunk1, t1); |
| #endif |
| |
| assert(ok0 && ok1); |
| |
| const TPointD &P0 = ttq.getP0(); |
| const TPointD &P1 = ttq.getP1(); |
| const TPointD &P2 = ttq.getP2(); |
| |
| double A = P0.x + P2.x - 2.0 * P1.x; |
| double B = P1.x - P0.x; |
| double delta_ = sq(B) - P0.x * A; |
| |
| int i, initialSize = cPoints.size(); |
| for (i = chunk0; i < chunk1; ++i) { |
| #ifdef USE_LENGTH |
| double s = tmin(path.getLength(i, 1.0) / path.getLength(), 1.0); |
| double x = m_data.m_x0 + m_data.m_xRange * s; |
| #else |
| double w = path.getW(i, 1.0); |
| double x = m_data.m_x0 + m_data.m_xRange * w; |
| #endif |
| |
| double delta = delta_ + x * A; |
| if (delta < 0) |
| continue; |
| |
| |
| double t = (sqrt(delta) - B) / A; |
| if (t > 0.0 && t < 1.0) |
| addCenterlinePoints(cPoints, chunk, t, i); |
| |
| if (delta < tolPar) |
| continue; |
| |
| |
| t = -(sqrt(delta) + B) / A; |
| if (t > 0.0 && t < 1.0) |
| addCenterlinePoints(cPoints, chunk, t, i); |
| } |
| |
| |
| std::sort(cPoints.begin() + initialSize, cPoints.end()); |
| } |
| |
| |
| |
| void ReferenceChunksLinearizator::addCenterlinePoints( |
| std::vector<CenterlinePoint> &cPoints, int strokeChunk, double strokeT, int refChunk) |
| { |
| CenterlinePoint p(strokeChunk, strokeT); |
| CenterlinePoint ref(refChunk, 1.0); |
| |
| CenterlinePoint newPoints[2]; |
| |
| p.buildPos(*m_stroke); |
| p.buildDirs(*m_stroke); |
| ref.buildPos(*m_path); |
| ref.buildDirs(*m_path); |
| |
| int i, count = m_data.buildPoints(p, ref, newPoints); |
| for (i = 0; i < count; ++i) |
| cPoints.push_back(newPoints[i]); |
| } |
| |
| |
| |
| |
| |
| class RecursiveReferenceLinearizator : public ReferenceLinearizator |
| { |
| public: |
| typedef void (RecursiveReferenceLinearizator::*SubdivisorFuncPtr)(std::vector<CenterlinePoint> &cPoints, |
| CenterlinePoint &cp0, CenterlinePoint &cp1); |
| |
| SubdivisorFuncPtr m_subdivisor; |
| |
| public: |
| void linearize(std::vector<CenterlinePoint> &cPoints, int chunk); |
| void linearize(std::vector<CenterlinePoint> &cPoints, int chunk, double t1); |
| |
| void subdivide(std::vector<CenterlinePoint> &cPoints, |
| CenterlinePoint &cp0, CenterlinePoint &cp1); |
| void subdivideCenterline(std::vector<CenterlinePoint> &cPoints, |
| CenterlinePoint &cp0, CenterlinePoint &cp1); |
| |
| public: |
| RecursiveReferenceLinearizator(const TStroke *stroke, const TStroke *path, |
| const StrokeOutlinizationData &data) |
| : ReferenceLinearizator(stroke, path, data), m_subdivisor(&RecursiveReferenceLinearizator::subdivide) {} |
| }; |
| |
| |
| |
| void RecursiveReferenceLinearizator::linearize( |
| std::vector<CenterlinePoint> &cPoints, int chunk) |
| { |
| linearize(cPoints, chunk, 1.0); |
| } |
| |
| |
| |
| void RecursiveReferenceLinearizator::linearize( |
| std::vector<CenterlinePoint> &cPoints, int chunk, double t1) |
| { |
| if (cPoints.empty()) |
| return; |
| |
| const TStroke &stroke = *this->m_stroke; |
| const TThickQuadratic &ttq = *stroke.getChunk(chunk); |
| |
| const TStroke &path = *this->m_path; |
| |
| |
| std::stable_sort(cPoints.begin(), cPoints.end()); |
| |
| std::vector<CenterlinePoint> addedPoints; |
| |
| unsigned int i, size_1 = cPoints.size() - 1; |
| for (i = 0; i < size_1; ++i) { |
| CenterlinePoint &cp1 = cPoints[i], cp2 = cPoints[i + 1]; |
| if (cp2.m_t - cp1.m_t > 1e-4) |
| (this->*m_subdivisor)(addedPoints, cPoints[i], cPoints[i + 1]); |
| } |
| |
| if (cPoints[size_1].m_t < t1) { |
| double t, x = (t1 == 1.0) ? ttq.getP2().x : getX(ttq.getP0(), ttq.getP1(), ttq.getP2(), t1); |
| int refChunk; |
| |
| #ifdef USE_LENGTH |
| bool ok = m_data.getChunkAndT_length(path, x, refChunk, t); |
| #else |
| bool ok = m_data.getChunkAndT_param(path, x, refChunk, t); |
| #endif |
| |
| CenterlinePoint strokeCpEnd(chunk, t1); |
| CenterlinePoint refCp(refChunk, t); |
| CenterlinePoint newPoints[2]; |
| |
| strokeCpEnd.buildPos(*m_stroke); |
| strokeCpEnd.buildDirs(*m_stroke); |
| refCp.buildPos(*m_path); |
| refCp.buildDirs(*m_path); |
| |
| int count = m_data.buildPoints(strokeCpEnd, refCp, newPoints); |
| if (count == 1) |
| (this->*m_subdivisor)(addedPoints, cPoints[size_1], newPoints[0]); |
| } |
| |
| cPoints.insert(cPoints.end(), addedPoints.begin(), addedPoints.end()); |
| } |
| |
| |
| |
| void RecursiveReferenceLinearizator::subdivide( |
| std::vector<CenterlinePoint> &cPoints, CenterlinePoint &cp0, CenterlinePoint &cp1) |
| { |
| if (!(cp0.m_hasNextD && cp1.m_hasPrevD)) |
| return; |
| |
| const TStroke &stroke = *this->m_stroke; |
| const TThickQuadratic &ttq = *stroke.getChunk(cp0.m_chunkIdx); |
| |
| const TStroke &path = *this->m_path; |
| |
| |
| |
| TPointD envDirL0, envDirR0, envDirL1, envDirR1; |
| buildEnvelopeDirections(cp0.m_p, cp0.m_nextD, envDirL0, envDirR0); |
| buildEnvelopeDirections(cp1.m_p, cp1.m_prevD, envDirL1, envDirR1); |
| |
| TPointD diff(convert(cp1.m_p) - convert(cp0.m_p)); |
| double d = tmax( |
| fabs(envDirL0 * (diff + cp1.m_p.thick * envDirL1 - cp0.m_p.thick * envDirL0)), |
| fabs(envDirR0 * (diff + cp1.m_p.thick * envDirR1 - cp0.m_p.thick * envDirR0))); |
| |
| if (d > m_data.m_pixSize && |
| cp1.m_t - cp0.m_t > 1e-4) { |
| CenterlinePoint strokeMidPoint(cp0.m_chunkIdx, 0.5 * (cp0.m_t + cp1.m_t)); |
| CenterlinePoint newPoints[2]; |
| |
| int count = m_data.buildPoints(*this->m_stroke, *this->m_path, strokeMidPoint, newPoints); |
| if (count == 1) |
| { |
| subdivide(cPoints, cp0, newPoints[0]); |
| subdivide(cPoints, newPoints[0], cp1); |
| |
| cPoints.push_back(newPoints[0]); |
| } |
| } |
| } |
| |
| |
| |
| void RecursiveReferenceLinearizator::subdivideCenterline( |
| std::vector<CenterlinePoint> &cPoints, |
| CenterlinePoint &cp0, CenterlinePoint &cp1) |
| { |
| if (cp0.m_covered || !cp0.m_hasNextD) |
| return; |
| |
| |
| TPointD dir((1.0 / norm(cp0.m_nextD)) * cp0.m_nextD); |
| TPointD diff(convert(cp1.m_p) - convert(cp0.m_p)); |
| |
| double d = fabs(dir.x * diff.y - dir.y * diff.x); |
| |
| if (d > m_data.m_pixSize && |
| cp1.m_t - cp0.m_t > 1e-4) { |
| CenterlinePoint strokeMidPoint(cp0.m_chunkIdx, 0.5 * (cp0.m_t + cp1.m_t)); |
| CenterlinePoint newPoints[2]; |
| |
| int count = m_data.buildPoints(*this->m_stroke, *this->m_path, strokeMidPoint, newPoints); |
| if (count == 1) |
| { |
| subdivide(cPoints, cp0, newPoints[0]); |
| subdivide(cPoints, newPoints[0], cp1); |
| |
| cPoints.push_back(newPoints[0]); |
| } |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| struct LinearizatorsSet { |
| static const int nLinearizators = 2; |
| |
| ReferenceChunksLinearizator m_refChunksLinearizator; |
| RecursiveReferenceLinearizator m_recursiveRefLinearizator; |
| |
| ReferenceLinearizator *m_linearizatorPtrs[nLinearizators]; |
| |
| public: |
| LinearizatorsSet(const TStroke &stroke, const TStroke &path, |
| const StrokeOutlinizationData &data) |
| : m_refChunksLinearizator(&stroke, &path, data), m_recursiveRefLinearizator(&stroke, &path, data) |
| { |
| m_linearizatorPtrs[0] = &m_refChunksLinearizator; |
| m_linearizatorPtrs[1] = &m_recursiveRefLinearizator; |
| } |
| |
| ReferenceLinearizator *operator[](int i) { return m_linearizatorPtrs[i]; } |
| const int size() const { return nLinearizators; } |
| }; |
| |
| } |
| |
| |
| |
| void TOutlineUtil::makeOutline(const TStroke &path, const TStroke &stroke, |
| const TRectD &strokeBox, TStrokeOutline &outline, |
| const TOutlineUtil::OutlineParameter &options) |
| { |
| |
| StrokeOutlinizationData data(stroke, strokeBox, options); |
| |
| |
| LinearizatorsSet linearizators(stroke, path, data); |
| CenterlinePoint newPoints[2]; |
| |
| std::vector<CenterlinePoint> cPoints, chunkPoints; |
| int i, chunksCount = stroke.getChunkCount(); |
| for (i = 0; i < chunksCount; ++i) { |
| chunkPoints.clear(); |
| |
| CenterlinePoint cp(i, 0.0); |
| int j, count = data.buildPoints(stroke, path, cp, newPoints); |
| for (j = 0; j < count; ++j) |
| chunkPoints.push_back(newPoints[j]); |
| |
| int linearsCount = linearizators.size(); |
| for (j = 0; j < linearsCount; ++j) { |
| StrokeLinearizator *linearizator = linearizators[j]; |
| linearizator->linearize(chunkPoints, i); |
| } |
| |
| |
| |
| std::stable_sort(chunkPoints.begin(), chunkPoints.end()); |
| |
| cPoints.insert(cPoints.end(), chunkPoints.begin(), chunkPoints.end()); |
| } |
| |
| |
| CenterlinePoint cPoint(chunksCount - 1, 1.0); |
| |
| int count = data.buildPoints(stroke, path, cPoint, newPoints); |
| for (i = 0; i < count; ++i) |
| cPoints.push_back(newPoints[i]); |
| |
| |
| |
| if (cPoints.empty()) |
| return; |
| |
| |
| if (stroke.isSelfLoop()) { |
| CenterlinePoint &lastCp = cPoints[cPoints.size() - 1]; |
| |
| cPoints[0].m_prevD = cPoint.m_prevD; |
| cPoints[0].m_hasPrevD = true; |
| lastCp.m_nextD = cPoint.m_prevD; |
| lastCp.m_hasNextD = true; |
| } |
| |
| #ifdef DEBUG_DRAW_TANGENTS |
| { |
| |
| glBegin(GL_LINES); |
| glColor3d(1.0, 0.0, 0.0); |
| |
| unsigned int i, size = cPoints.size(); |
| for (i = 0; i < size; ++i) { |
| glVertex2d(cPoints[i].m_p.x, cPoints[i].m_p.y); |
| glVertex2d( |
| cPoints[i].m_p.x + cPoints[i].m_nextD.x * cPoints[i].m_p.thick, |
| cPoints[i].m_p.y + cPoints[i].m_nextD.y * cPoints[i].m_p.thick); |
| } |
| |
| glEnd(); |
| } |
| #endif |
| |
| |
| buildOutline(stroke, cPoints, outline, data); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| namespace |
| { |
| |
| void makeCenterline(const TStroke &path, const TEdge &edge, const TRectD ®ionBox, |
| std::vector<T3DPointD> &outline) |
| { |
| int initialOutlineSize = outline.size(); |
| |
| static const TOutlineUtil::OutlineParameter options; |
| const TStroke &stroke = *edge.m_s; |
| |
| double w0 = edge.m_w0, w1 = edge.m_w1; |
| bool reversed = w1 < w0; |
| if (reversed) |
| w0 = edge.m_w1, w1 = edge.m_w0; |
| |
| |
| StrokeOutlinizationData data(stroke, regionBox, options); |
| |
| |
| LinearizatorsSet linearizators(stroke, path, data); |
| CenterlinePoint newPoints[2]; |
| |
| linearizators.m_recursiveRefLinearizator.m_subdivisor = |
| &RecursiveReferenceLinearizator::subdivideCenterline; |
| |
| std::vector<CenterlinePoint> chunkPoints; |
| |
| int i, chunk0, chunk1; |
| double t0, t1; |
| |
| bool ok0 = !edge.m_s->getChunkAndT(w0, chunk0, t0); |
| bool ok1 = !edge.m_s->getChunkAndT(w1, chunk1, t1); |
| |
| assert(ok0 && ok1); |
| |
| double tStart = t0; |
| for (i = chunk0; i < chunk1; ++i, tStart = 0.0) { |
| chunkPoints.clear(); |
| |
| CenterlinePoint cp(i, tStart); |
| int j, count = data.buildPoints(stroke, path, cp, newPoints); |
| for (j = 0; j < count; ++j) |
| chunkPoints.push_back(newPoints[j]); |
| |
| int linearsCount = linearizators.size(); |
| for (j = 0; j < linearsCount; ++j) { |
| ReferenceLinearizator *linearizator = linearizators[j]; |
| linearizator->linearize(chunkPoints, i, 1.0); |
| } |
| |
| |
| |
| std::stable_sort(chunkPoints.begin(), chunkPoints.end()); |
| |
| int size = chunkPoints.size(); |
| outline.reserve(outline.size() + size); |
| |
| for (j = 0; j < size; ++j) { |
| const TPointD &point = chunkPoints[j].m_p; |
| outline.push_back(T3DPointD(point.x, point.y, 0.0)); |
| } |
| } |
| |
| |
| { |
| chunkPoints.clear(); |
| |
| CenterlinePoint cp(chunk1, tStart); |
| int j, count = data.buildPoints(stroke, path, cp, newPoints); |
| for (j = 0; j < count; ++j) |
| chunkPoints.push_back(newPoints[j]); |
| |
| int linearsCount = linearizators.size(); |
| for (j = 0; j < linearsCount; ++j) { |
| ReferenceLinearizator *linearizator = linearizators[j]; |
| linearizator->linearize(chunkPoints, chunk1, t1); |
| } |
| |
| std::stable_sort(chunkPoints.begin(), chunkPoints.end()); |
| |
| int size = chunkPoints.size(); |
| outline.reserve(outline.size() + size); |
| |
| for (j = 0; j < size; ++j) { |
| const TPointD &point = chunkPoints[j].m_p; |
| outline.push_back(T3DPointD(point.x, point.y, 0.0)); |
| } |
| } |
| |
| |
| CenterlinePoint cp(chunk1, t1); |
| int j, count = data.buildPoints(stroke, path, cp, newPoints); |
| for (j = 0; j < count; ++j) { |
| const TPointD &point = newPoints[j].m_p; |
| outline.push_back(T3DPointD(point.x, point.y, 0.0)); |
| } |
| |
| |
| if (reversed) |
| std::reverse(outline.begin() + initialOutlineSize, outline.end()); |
| } |
| |
| |
| |
| void makeOutlineRaw(const TStroke &path, const TRegion ®ion, const TRectD ®ionBox, |
| std::vector<T3DPointD> &outline) |
| { |
| |
| int e, edgesCount = region.getEdgeCount(); |
| for (e = 0; e < edgesCount; ++e) |
| makeCenterline(path, *region.getEdge(e), regionBox, outline); |
| } |
| |
| } |
| |
| |
| |
| void TOutlineUtil::makeOutline(const TStroke &path, const TRegion ®ion, const TRectD ®ionBox, |
| TRegionOutline &outline) |
| { |
| outline.m_doAntialiasing = true; |
| |
| |
| { |
| outline.m_exterior.resize(1); |
| outline.m_exterior[0].clear(); |
| |
| makeOutlineRaw(path, region, regionBox, outline.m_exterior[0]); |
| } |
| |
| |
| { |
| outline.m_interior.clear(); |
| |
| int i, subRegionNumber = region.getSubregionCount(); |
| outline.m_interior.resize(subRegionNumber); |
| |
| for (i = 0; i < subRegionNumber; i++) |
| makeOutlineRaw(path, *region.getSubregion(i), regionBox, outline.m_interior[i]); |
| } |
| |
| outline.m_bbox = region.getBBox(); |
| } |
| |