Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
#include "tregion.h"
Toshihiro Shimizu 890ddd
#include "tregionoutline.h"
Toshihiro Shimizu 890ddd
#include "tellipticbrushP.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
#include "tstrokeoutline.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
using namespace tellipticbrush;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
#define USE_LENGTH
Toshihiro Shimizu 890ddd
//#define DEBUG_DRAW_TANGENTS
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//********************************************************************************
Toshihiro Shimizu 890ddd
//    EXPLANATION
Toshihiro Shimizu 890ddd
//********************************************************************************
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
/*! \file tellipticbrush.cpp
Shinya Kitaoka 120a6e
\brief This code performs the outlinization of a \a brush stroke with respect to
Shinya Kitaoka 120a6e
a
Toshihiro Shimizu 890ddd
       secondary \a path stroke. This is used to draw Adobe Illustrator-like
Shinya Kitaoka 120a6e
       vectors whose brush is itself a custom vector (and by extension, a
Shinya Kitaoka 120a6e
complex
Toshihiro Shimizu 890ddd
       vector image).
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
       Generalization: Introduce a repeat % and a superposition % in the
Shinya Kitaoka 120a6e
algorithm
Toshihiro Shimizu 890ddd
*/
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
/* TECHNICAL:
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
  We have two strokes: one is the 'guideline', the other is the 'brush', and the
Shinya Kitaoka 120a6e
  purpose of this algorithm is that of bending the brush to follow the
Shinya Kitaoka 120a6e
  guideline.
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  The brush is supposed to be lying horizontally, inside a rectangle
Shinya Kitaoka 120a6e
  corresponding
Toshihiro Shimizu 890ddd
  to the bounding box of the image containing the brush.
Shinya Kitaoka 120a6e
  The line segment connecting the midpoints of the vertical image bbox edges
Shinya Kitaoka 120a6e
  maps
Toshihiro Shimizu 890ddd
  to the guideline's centerline.
Toshihiro Shimizu 890ddd
  Such mapping makes [bbox.x0, bbox.x1] -> [0, 1] in a linear fashion, where the
Toshihiro Shimizu 890ddd
  image interval represents the parametric space of the guideline.
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  Each vertical scanline of the bbox is further mapped to the segment extruding
Shinya Kitaoka 120a6e
  from
Toshihiro Shimizu 890ddd
  the guideline's centerline along its normal (multiplied in length by the
Toshihiro Shimizu 890ddd
  guideline's thickness).
Toshihiro Shimizu 890ddd
*/
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//********************************************************************************
Toshihiro Shimizu 890ddd
//    Geometric helpers
Toshihiro Shimizu 890ddd
//********************************************************************************
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
namespace {
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
double getX(const TThickPoint &P0, const TThickPoint &P1, const TThickPoint &P2,
Shinya Kitaoka 120a6e
            double t) {
Shinya Kitaoka 120a6e
  double one_t = 1.0 - t;
Shinya Kitaoka 120a6e
  return P0.x * sq(one_t) + 2.0 * P1.x * t * one_t + P2.x * sq(t);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//--------------------------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
TPointD normal(const TPointD &p, bool left) {
Shinya Kitaoka 120a6e
  TPointD n(-p.y, p.x);
Shinya Kitaoka 120a6e
  return (1.0 / norm(n)) * (left ? n : -n);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//--------------------------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
TPointD normal(const TPointD &p) {
Shinya Kitaoka 120a6e
  return (1.0 / norm(p)) * TPointD(-p.y, p.x);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//--------------------------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void getHRange(const TThickQuadratic &ttq, double &x0, double &x1) {
Shinya Kitaoka 120a6e
  const TPointD &P0 = ttq.getP0();
Shinya Kitaoka 120a6e
  const TPointD &P1 = ttq.getP1();
Shinya Kitaoka 120a6e
  const TPointD &P2 = ttq.getP2();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Get the horizontal range of the chunk
Shinya Kitaoka 120a6e
  x0 = std::min({x0, P0.x, P2.x}), x1 = std::max({x1, P0.x, P2.x});
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  double t = (P0.x - P1.x) / (P0.x + P2.x - 2.0 * P1.x);
Shinya Kitaoka 120a6e
  if (t > 0.0 && t < 1.0) {
Shinya Kitaoka 120a6e
    double x = getX(P0, P1, P2, t);
Shinya Kitaoka 120a6e
    x0 = std::min(x0, x), x1 = std::max(x1, x);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void getHRange(const TThickQuadratic &ttq, double t0, double t1, double &x0,
Shinya Kitaoka 120a6e
               double &x1) {
Shinya Kitaoka 120a6e
  const TPointD &P0 = ttq.getP0();
Shinya Kitaoka 120a6e
  const TPointD &P1 = ttq.getP1();
Shinya Kitaoka 120a6e
  const TPointD &P2 = ttq.getP2();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  double x0_ = getX(P0, P1, P2, t0);
Shinya Kitaoka 120a6e
  double x1_ = getX(P0, P1, P2, t1);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Get the horizontal range of the chunk
Shinya Kitaoka 120a6e
  x0 = std::min({x0, x0_, x1_}), x1 = std::max({x1, x0_, x1_});
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  double t = (P0.x - P1.x) / (P0.x + P2.x - 2.0 * P1.x);
Shinya Kitaoka 120a6e
  if (t > t0 && t < t1) {
Shinya Kitaoka 120a6e
    double x = getX(P0, P1, P2, t);
Shinya Kitaoka 120a6e
    x0 = std::min(x0, x), x1 = std::max(x1, x);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void getHRange(const TStroke &stroke, double &x0, double &x1) {
Shinya Kitaoka 120a6e
  int i, nChunks = stroke.getChunkCount();
Shinya Kitaoka 120a6e
  for (i = 0; i < nChunks; ++i) getHRange(*stroke.getChunk(i), x0, x1);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//********************************************************************************
Toshihiro Shimizu 890ddd
//    Outlinization Data
Toshihiro Shimizu 890ddd
//********************************************************************************
Toshihiro Shimizu 890ddd
Shinya Kitaoka d1f6c4
struct StrokeOutlinizationData final
Shinya Kitaoka d1f6c4
    : public tellipticbrush::OutlinizationData {
Shinya Kitaoka 120a6e
  double m_x0, m_x1, m_xRange;
Shinya Kitaoka 120a6e
  double m_y0, m_yScale;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  StrokeOutlinizationData() : OutlinizationData() {}
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  StrokeOutlinizationData(const TStroke &stroke, const TRectD &strokeBox,
Shinya Kitaoka 120a6e
                          const TOutlineUtil::OutlineParameter &options)
Shinya Kitaoka 120a6e
      : OutlinizationData(options)
Shinya Kitaoka 120a6e
      , m_x0(strokeBox.x0)
Shinya Kitaoka 120a6e
      , m_x1(strokeBox.x1)
Shinya Kitaoka 120a6e
      , m_xRange(m_x1 - m_x0)
Shinya Kitaoka 120a6e
      , m_y0(0.5 * (strokeBox.y0 + strokeBox.y1))
Shinya Kitaoka 120a6e
      , m_yScale(1.0 / (strokeBox.y1 - strokeBox.y0)) {}
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  void buildPoint(const CenterlinePoint &p, bool isNextD, CenterlinePoint &ref,
Shinya Kitaoka 120a6e
                  bool isRefNextD, CenterlinePoint &out);
Shinya Kitaoka 120a6e
  int buildPoints(const CenterlinePoint &p, CenterlinePoint &ref,
Shinya Kitaoka 120a6e
                  CenterlinePoint *out);
Shinya Kitaoka 120a6e
  int buildPoints(const TStroke &stroke, const TStroke &path,
Shinya Kitaoka 120a6e
                  CenterlinePoint &cp, CenterlinePoint *out);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  bool getChunkAndT_param(const TStroke &path, double x, int &chunk, double &t);
Shinya Kitaoka 120a6e
  bool getChunkAndT_length(const TStroke &path, double x, int &chunk,
Shinya Kitaoka 120a6e
                           double &t);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  double toW(double x);
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//--------------------------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
double StrokeOutlinizationData::toW(double x) {
Shinya Kitaoka 120a6e
  return tcrop((x - m_x0) / m_xRange, 0.0, 1.0);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//--------------------------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
bool StrokeOutlinizationData::getChunkAndT_param(const TStroke &path, double x,
Shinya Kitaoka 120a6e
                                                 int &chunk, double &t) {
Shinya Kitaoka 120a6e
  double w = toW(x);
Shinya Kitaoka 120a6e
  return !path.getChunkAndT(w, chunk, t);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//--------------------------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
bool StrokeOutlinizationData::getChunkAndT_length(const TStroke &path, double x,
Shinya Kitaoka 120a6e
                                                  int &chunk, double &t) {
Shinya Kitaoka 120a6e
  double s = toW(x) * path.getLength();
Shinya Kitaoka 120a6e
  return !path.getChunkAndTAtLength(s, chunk, t);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//--------------------------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void StrokeOutlinizationData::buildPoint(const CenterlinePoint &p, bool pNextD,
Shinya Kitaoka 120a6e
                                         CenterlinePoint &ref, bool refNextD,
Shinya Kitaoka 120a6e
                                         CenterlinePoint &out) {
Shinya Kitaoka 120a6e
  TThickPoint &refD = refNextD ? ref.m_nextD : ref.m_prevD;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  const TThickPoint *pD;
Shinya Kitaoka 120a6e
  TThickPoint *outD;
Shinya Kitaoka 120a6e
  bool *outHasD;
Shinya Kitaoka 120a6e
  if (pNextD) {
Shinya Kitaoka 120a6e
    pD      = &p.m_nextD;
Shinya Kitaoka 120a6e
    outD    = &out.m_nextD;
Shinya Kitaoka 120a6e
    outHasD = &out.m_hasNextD;
Shinya Kitaoka 120a6e
  } else {
Shinya Kitaoka 120a6e
    pD      = &p.m_prevD;
Shinya Kitaoka 120a6e
    outD    = &out.m_prevD;
Shinya Kitaoka 120a6e
    outHasD = &out.m_hasPrevD;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Build position
Shinya Kitaoka 120a6e
  refD = (1.0 / norm(refD)) * refD;
Shinya Kitaoka 120a6e
  TPointD normalDirection(-refD.y, refD.x);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  double yPercentage = (p.m_p.y - m_y0) * m_yScale;
Shinya Kitaoka 120a6e
  double yRelative   = yPercentage * ref.m_p.thick;
Shinya Kitaoka 120a6e
  double yFactor     = ref.m_p.thick * m_yScale;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  out.m_p = TThickPoint(ref.m_p.x + yRelative * normalDirection.x,
Shinya Kitaoka 120a6e
                        ref.m_p.y + yRelative * normalDirection.y,
Shinya Kitaoka 120a6e
                        p.m_p.thick * yFactor);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Build direction
Shinya Kitaoka 120a6e
  double stretchedDY = pD->x * yPercentage * refD.thick + pD->y * yFactor;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  *outD = TThickPoint(refD.x * pD->x - refD.y * stretchedDY,
Shinya Kitaoka 120a6e
                      refD.y * pD->x + refD.x * stretchedDY,
Shinya Kitaoka 120a6e
                      pD->thick * (1.0 + refD.thick));
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  bool covered  = (sq(outD->x) + sq(outD->y) < sq(outD->thick) + tolPar);
Shinya Kitaoka 120a6e
  out.m_covered = out.m_covered && covered;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  *outHasD = *outHasD && !covered;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//--------------------------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
/*    EXPLANATION:
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
        \ _   \         The path stroke with centerline point C has 2 outward
Toshihiro Shimizu 890ddd
     \      \  \        directions (prevD and nextD), which may be different.
Shinya Kitaoka 120a6e
      \      |* \       Therefore, there may also be 2 different envelope
Shinya Kitaoka 120a6e
   directions
Toshihiro Shimizu 890ddd
       \   * .   \      from C (the *s in the drawing).
Toshihiro Shimizu 890ddd
        C    .    |     When the brush stroke 'hits' one envelope direction, it
Toshihiro Shimizu 890ddd
       /   * .   /      transfers on the other e.d., and continues on that side.
Toshihiro Shimizu 890ddd
      /   _ / * /
Toshihiro Shimizu 890ddd
     /  /      /
Toshihiro Shimizu 890ddd
       |      /
Toshihiro Shimizu 890ddd
*/
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
//! Build points resulting from the association between p (must have pos and
Shinya Kitaoka 120a6e
//! dirs
Shinya Kitaoka 120a6e
//! already built) and ref, returning the number of output points stored in
Shinya Kitaoka 120a6e
//! out[] (at max 2).
Shinya Kitaoka 120a6e
int StrokeOutlinizationData::buildPoints(const CenterlinePoint &p,
Shinya Kitaoka 120a6e
                                         CenterlinePoint &ref,
Shinya Kitaoka 120a6e
                                         CenterlinePoint *out) {
Shinya Kitaoka 38fd86
  out[0] = out[1]  = p;
Shinya Kitaoka 120a6e
  out[0].m_covered = out[1].m_covered = true;  // Coverage is rebuilt
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  bool refSymmetric =
Shinya Kitaoka 120a6e
      ref.m_hasPrevD && ref.m_hasNextD && ref.m_nextD == ref.m_prevD;
Shinya Kitaoka 120a6e
  bool pSymmetric = p.m_hasPrevD && p.m_hasNextD && p.m_nextD == p.m_prevD;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Build prev
Shinya Kitaoka 120a6e
  bool prevSideIsNext =
Shinya Kitaoka 120a6e
      (p.m_prevD.x < 0) ? true : (p.m_prevD.x > 0) ? false : ref.m_hasNextD;
Shinya Kitaoka 120a6e
  bool hasPrev =
Shinya Kitaoka 120a6e
      p.m_hasPrevD && (prevSideIsNext ? ref.m_hasNextD : ref.m_hasPrevD);
Shinya Kitaoka 120a6e
  int prevIdx = hasPrev ? 0 : -1;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (hasPrev) {
Shinya Kitaoka 120a6e
    CenterlinePoint &outPoint = out[prevIdx];
Shinya Kitaoka 120a6e
    buildPoint(p, false, ref, prevSideIsNext, outPoint);
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (refSymmetric && pSymmetric) {
Shinya Kitaoka 120a6e
    // Copy prev to next
Shinya Kitaoka 120a6e
    if (hasPrev) {
Shinya Kitaoka 120a6e
      CenterlinePoint &outPoint = out[prevIdx];
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      outPoint.m_hasNextD = outPoint.m_hasPrevD;
Shinya Kitaoka 120a6e
      outPoint.m_nextD    = outPoint.m_prevD;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      return 1;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    return 0;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Build next
Shinya Kitaoka 120a6e
  bool nextSideIsNext =
Shinya Kitaoka 120a6e
      (p.m_nextD.x > 0) ? true : (p.m_nextD.x < 0) ? false : ref.m_hasNextD;
Shinya Kitaoka 120a6e
  bool hasNext =
Shinya Kitaoka 120a6e
      p.m_hasNextD && (nextSideIsNext ? ref.m_hasNextD : ref.m_hasPrevD);
Shinya Kitaoka 120a6e
  int nextIdx =
Shinya Kitaoka 120a6e
      hasNext ? hasPrev ? ((int)prevSideIsNext != nextSideIsNext) : 0 : -1;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (hasNext) {
Shinya Kitaoka 120a6e
    CenterlinePoint &outPoint = out[nextIdx];
Shinya Kitaoka 120a6e
    buildPoint(p, true, ref, nextSideIsNext, outPoint);
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Fill in unbuilt directions if necessary
Shinya Kitaoka 120a6e
  if (hasPrev && hasNext && prevIdx != nextIdx) {
Shinya Kitaoka 120a6e
    CenterlinePoint &outPrev = out[prevIdx];
Shinya Kitaoka 120a6e
    CenterlinePoint &outNext = out[nextIdx];
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if (dist(outPrev.m_p, outNext.m_p) > 1e-4) {
Shinya Kitaoka 120a6e
      // If there are 2 full output points, make their unbuilt directions match
Shinya Kitaoka 120a6e
      outPrev.m_nextD = outNext.m_prevD = 0.5 * (outNext.m_p - outPrev.m_p);
Shinya Kitaoka 120a6e
      bool covered = (sq(outPrev.m_nextD.x) + sq(outPrev.m_nextD.y) <
Shinya Kitaoka 120a6e
                      sq(outPrev.m_nextD.thick) + tolPar);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      outPrev.m_hasNextD = outNext.m_hasPrevD = !covered;
Shinya Kitaoka 120a6e
      outPrev.m_covered                       = outPrev.m_covered && covered;
Shinya Kitaoka 120a6e
      outNext.m_covered                       = outNext.m_covered && covered;
Shinya Kitaoka 120a6e
    } else {
Shinya Kitaoka 120a6e
      // Merge the 2 existing ones
Shinya Kitaoka 120a6e
      nextIdx = prevIdx;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      outPrev.m_nextD    = outNext.m_nextD;
Shinya Kitaoka 120a6e
      outPrev.m_hasNextD = outNext.m_hasPrevD;
Shinya Kitaoka 120a6e
      outPrev.m_covered  = outPrev.m_covered && outNext.m_covered;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  return std::max(prevIdx, nextIdx) + 1;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//--------------------------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
int StrokeOutlinizationData::buildPoints(const TStroke &stroke,
Shinya Kitaoka 120a6e
                                         const TStroke &path,
Shinya Kitaoka 120a6e
                                         CenterlinePoint &cp,
Shinya Kitaoka 120a6e
                                         CenterlinePoint *out) {
Shinya Kitaoka 120a6e
  const TThickQuadratic &ttq = *stroke.getChunk(cp.m_chunkIdx);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  const TThickPoint &P0 = ttq.getP0();
Shinya Kitaoka 120a6e
  const TThickPoint &P1 = ttq.getP1();
Shinya Kitaoka 120a6e
  const TThickPoint &P2 = ttq.getP2();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  double x = getX(P0, P1, P2, cp.m_t);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  double pathT;
Shinya Kitaoka 120a6e
  int pathChunk;
Toshihiro Shimizu 890ddd
#ifdef USE_LENGTH
Shinya Kitaoka 120a6e
  bool ok = getChunkAndT_length(path, x, pathChunk, pathT);
Toshihiro Shimizu 890ddd
#else
Shinya Kitaoka 120a6e
  bool ok = getChunkAndT_param(path, x, pathChunk, pathT);
Toshihiro Shimizu 890ddd
#endif
Shinya Kitaoka 120a6e
  assert(ok);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  CenterlinePoint pathCp(pathChunk, pathT);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  cp.buildPos(stroke);
Shinya Kitaoka 120a6e
  cp.buildDirs(stroke);
Shinya Kitaoka 120a6e
  pathCp.buildPos(path);
Shinya Kitaoka 120a6e
  pathCp.buildDirs(path);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  return buildPoints(cp, pathCp, out);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//********************************************************************************
Toshihiro Shimizu 890ddd
//    Path-Altered Brush Linearizator (base class)
Toshihiro Shimizu 890ddd
//********************************************************************************
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
class ReferenceLinearizator : public tellipticbrush::StrokeLinearizator {
Toshihiro Shimizu 890ddd
protected:
Shinya Kitaoka 120a6e
  const TStroke *m_path;
Shinya Kitaoka 120a6e
  StrokeOutlinizationData m_data;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  ReferenceLinearizator(const TStroke *stroke, const TStroke *path,
Shinya Kitaoka 120a6e
                        const StrokeOutlinizationData &data);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  virtual void linearize(std::vector<centerlinepoint> &cPoints, int chunk,</centerlinepoint>
Shinya Kitaoka 120a6e
                         double t1) = 0;
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//--------------------------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
ReferenceLinearizator::ReferenceLinearizator(
Shinya Kitaoka 120a6e
    const TStroke *stroke, const TStroke *path,
Shinya Kitaoka 120a6e
    const StrokeOutlinizationData &data)
Shinya Kitaoka 120a6e
    : StrokeLinearizator(stroke), m_path(path), m_data(data) {}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//********************************************************************************
Toshihiro Shimizu 890ddd
//    Brush Linearizator on Path inter-chunk points
Toshihiro Shimizu 890ddd
//********************************************************************************
Toshihiro Shimizu 890ddd
Shinya Kitaoka d1f6c4
class ReferenceChunksLinearizator final : public ReferenceLinearizator {
Shinya Kitaoka 120a6e
  double m_w0, m_w1;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  ReferenceChunksLinearizator(const TStroke *stroke, const TStroke *path,
Shinya Kitaoka 120a6e
                              const StrokeOutlinizationData &data)
Shinya Kitaoka 120a6e
      : ReferenceLinearizator(stroke, path, data) {}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 473e70
  void linearize(std::vector<centerlinepoint> &cPoints, int chunk) override;</centerlinepoint>
Shinya Kitaoka 38fd86
  void linearize(std::vector<centerlinepoint> &cPoints, int chunk,</centerlinepoint>
Shinya Kitaoka 38fd86
                 double t1) override;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  void addCenterlinePoints(std::vector<centerlinepoint> &cPoints,</centerlinepoint>
Shinya Kitaoka 120a6e
                           int brushChunk, double x0, double x1);
Shinya Kitaoka 120a6e
  void addCenterlinePoints(std::vector<centerlinepoint> &cPoints,</centerlinepoint>
Shinya Kitaoka 120a6e
                           int strokeChunk, double strokeT, int refChunk);
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//--------------------------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void ReferenceChunksLinearizator::linearize(
Shinya Kitaoka 120a6e
    std::vector<centerlinepoint> &cPoints, int chunk) {</centerlinepoint>
Shinya Kitaoka 120a6e
  // Get the stroke chunk
Shinya Kitaoka 120a6e
  const TThickQuadratic &ttq = *this->m_stroke->getChunk(chunk);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Get the chunk's horizontal range
Shinya Kitaoka 120a6e
  double x0 = (std::numeric_limits<double>::max)(), x1 = -x0;</double>
Shinya Kitaoka 120a6e
  getHRange(ttq, x0, x1);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Now, we have to add all points corresponding to the intersections between
Shinya Kitaoka 120a6e
  // the relative
Shinya Kitaoka 120a6e
  // vertical projection of path's chunk endpoints, and the stroke
Shinya Kitaoka 120a6e
  addCenterlinePoints(cPoints, chunk, x0, x1);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//--------------------------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void ReferenceChunksLinearizator::linearize(
Shinya Kitaoka 120a6e
    std::vector<centerlinepoint> &cPoints, int chunk, double t1) {</centerlinepoint>
Shinya Kitaoka 120a6e
  if (cPoints.empty()) return;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Get the stroke chunk
Shinya Kitaoka 120a6e
  const TThickQuadratic &ttq = *this->m_stroke->getChunk(chunk);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Get the chunk's horizontal range
Shinya Kitaoka 120a6e
  double x0 = (std::numeric_limits<double>::max)(), x1 = -x0;</double>
Shinya Kitaoka 120a6e
  getHRange(ttq, cPoints[0].m_t, t1, x0, x1);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Now, we have to add all points corresponding to the intersections between
Shinya Kitaoka 120a6e
  // the relative
Shinya Kitaoka 120a6e
  // vertical projection of path's chunk endpoints, and the stroke
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  addCenterlinePoints(cPoints, chunk, x0, x1);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//--------------------------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void ReferenceChunksLinearizator::addCenterlinePoints(
Shinya Kitaoka 120a6e
    std::vector<centerlinepoint> &cPoints, int chunk, double x0, double x1) {</centerlinepoint>
Shinya Kitaoka 120a6e
  const TThickQuadratic &ttq = *this->m_stroke->getChunk(chunk);
Shinya Kitaoka 120a6e
  const TStroke &path        = *this->m_path;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  int chunk0, chunk1;
Shinya Kitaoka 120a6e
  double t0, t1;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
#ifdef USE_LENGTH
Shinya Kitaoka 120a6e
  bool ok0 = m_data.getChunkAndT_length(path, x0, chunk0, t0);
Shinya Kitaoka 120a6e
  bool ok1 = m_data.getChunkAndT_length(path, x1, chunk1, t1);
Toshihiro Shimizu 890ddd
#else
Shinya Kitaoka 120a6e
  bool ok0   = m_data.getChunkAndT_param(path, x0, chunk0, t0);
Shinya Kitaoka 120a6e
  bool ok1   = m_data.getChunkAndT_param(path, x1, chunk1, t1);
Toshihiro Shimizu 890ddd
#endif
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  assert(ok0 && ok1);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  const TPointD &P0 = ttq.getP0();
Shinya Kitaoka 120a6e
  const TPointD &P1 = ttq.getP1();
Shinya Kitaoka 120a6e
  const TPointD &P2 = ttq.getP2();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  double A      = P0.x + P2.x - 2.0 * P1.x;
Shinya Kitaoka 120a6e
  double B      = P1.x - P0.x;
Shinya Kitaoka 120a6e
  double delta_ = sq(B) - P0.x * A;  // actual delta = delta_ + x * A;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  int i, initialSize = cPoints.size();
Shinya Kitaoka 120a6e
  for (i = chunk0; i < chunk1; ++i) {
Toshihiro Shimizu 890ddd
#ifdef USE_LENGTH
Shinya Kitaoka 120a6e
    double s = std::min(path.getLength(i, 1.0) / path.getLength(), 1.0);
Shinya Kitaoka 120a6e
    double x = m_data.m_x0 + m_data.m_xRange * s;
Toshihiro Shimizu 890ddd
#else
Shinya Kitaoka 120a6e
    double w = path.getW(i, 1.0);
Shinya Kitaoka 120a6e
    double x = m_data.m_x0 + m_data.m_xRange * w;
Toshihiro Shimizu 890ddd
#endif
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    double delta = delta_ + x * A;
Shinya Kitaoka 120a6e
    if (delta < 0) continue;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // Add first solution
Shinya Kitaoka 120a6e
    double t = (sqrt(delta) - B) / A;
Shinya Kitaoka 120a6e
    if (t > 0.0 && t < 1.0)  // 0 and 1 are dealt outside
Shinya Kitaoka 120a6e
      addCenterlinePoints(cPoints, chunk, t, i);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    if (delta < tolPar) continue;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // Add second solution
Shinya Kitaoka 120a6e
    t = -(sqrt(delta) + B) / A;
Shinya Kitaoka 120a6e
    if (t > 0.0 && t < 1.0) addCenterlinePoints(cPoints, chunk, t, i);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // As points may be mixed (by parameter), sort them.
Shinya Kitaoka 120a6e
  std::sort(cPoints.begin() + initialSize, cPoints.end());
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//--------------------------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void ReferenceChunksLinearizator::addCenterlinePoints(
Shinya Kitaoka 120a6e
    std::vector<centerlinepoint> &cPoints, int strokeChunk, double strokeT,</centerlinepoint>
Shinya Kitaoka 120a6e
    int refChunk) {
Shinya Kitaoka 120a6e
  CenterlinePoint p(strokeChunk, strokeT);
Shinya Kitaoka 120a6e
  CenterlinePoint ref(refChunk, 1.0);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  CenterlinePoint newPoints[2];
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  p.buildPos(*m_stroke);
Shinya Kitaoka 120a6e
  p.buildDirs(*m_stroke);
Shinya Kitaoka 120a6e
  ref.buildPos(*m_path);
Shinya Kitaoka 120a6e
  ref.buildDirs(*m_path);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  int i, count = m_data.buildPoints(p, ref, newPoints);
Shinya Kitaoka 120a6e
  for (i = 0; i < count; ++i) cPoints.push_back(newPoints[i]);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//********************************************************************************
Toshihiro Shimizu 890ddd
//    Recursive (regular) Reference Stroke Linearizator
Toshihiro Shimizu 890ddd
//********************************************************************************
Toshihiro Shimizu 890ddd
Shinya Kitaoka d1f6c4
class RecursiveReferenceLinearizator final : public ReferenceLinearizator {
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  typedef void (RecursiveReferenceLinearizator::*SubdivisorFuncPtr)(
Shinya Kitaoka 120a6e
      std::vector<centerlinepoint> &cPoints, CenterlinePoint &cp0,</centerlinepoint>
Shinya Kitaoka 120a6e
      CenterlinePoint &cp1);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  SubdivisorFuncPtr m_subdivisor;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 473e70
  void linearize(std::vector<centerlinepoint> &cPoints, int chunk) override;</centerlinepoint>
Shinya Kitaoka 38fd86
  void linearize(std::vector<centerlinepoint> &cPoints, int chunk,</centerlinepoint>
Shinya Kitaoka 38fd86
                 double t1) override;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  void subdivide(std::vector<centerlinepoint> &cPoints, CenterlinePoint &cp0,</centerlinepoint>
Shinya Kitaoka 120a6e
                 CenterlinePoint &cp1);
Shinya Kitaoka 120a6e
  void subdivideCenterline(std::vector<centerlinepoint> &cPoints,</centerlinepoint>
Shinya Kitaoka 120a6e
                           CenterlinePoint &cp0, CenterlinePoint &cp1);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  RecursiveReferenceLinearizator(const TStroke *stroke, const TStroke *path,
Shinya Kitaoka 120a6e
                                 const StrokeOutlinizationData &data)
Shinya Kitaoka 120a6e
      : ReferenceLinearizator(stroke, path, data)
Shinya Kitaoka 120a6e
      , m_subdivisor(&RecursiveReferenceLinearizator::subdivide) {}
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//--------------------------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void RecursiveReferenceLinearizator::linearize(
Shinya Kitaoka 120a6e
    std::vector<centerlinepoint> &cPoints, int chunk) {</centerlinepoint>
Shinya Kitaoka 120a6e
  linearize(cPoints, chunk, 1.0);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//--------------------------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void RecursiveReferenceLinearizator::linearize(
Shinya Kitaoka 120a6e
    std::vector<centerlinepoint> &cPoints, int chunk, double t1) {</centerlinepoint>
Shinya Kitaoka 120a6e
  if (cPoints.empty()) return;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  const TStroke &stroke      = *this->m_stroke;
Shinya Kitaoka 120a6e
  const TThickQuadratic &ttq = *stroke.getChunk(chunk);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  const TStroke &path = *this->m_path;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Sort the interval (SHOULD BE DONE OUTSIDE?)
Shinya Kitaoka 120a6e
  std::stable_sort(cPoints.begin(), cPoints.end());
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  std::vector<centerlinepoint> addedPoints;</centerlinepoint>
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  unsigned int i, size_1 = cPoints.size() - 1;
Shinya Kitaoka 120a6e
  for (i = 0; i < size_1; ++i) {
Shinya Kitaoka 120a6e
    CenterlinePoint &cp1 = cPoints[i], cp2 = cPoints[i + 1];
Shinya Kitaoka 120a6e
    if (cp2.m_t - cp1.m_t > 1e-4)
Shinya Kitaoka 120a6e
      (this->*m_subdivisor)(addedPoints, cPoints[i], cPoints[i + 1]);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  if (cPoints[size_1].m_t < t1) {
Shinya Kitaoka 120a6e
    double t, x = (t1 == 1.0) ? ttq.getP2().x
Shinya Kitaoka 120a6e
                              : getX(ttq.getP0(), ttq.getP1(), ttq.getP2(), t1);
Shinya Kitaoka 120a6e
    int refChunk;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
#ifdef USE_LENGTH
Shinya Kitaoka 120a6e
    bool ok = m_data.getChunkAndT_length(path, x, refChunk, t);
Toshihiro Shimizu 890ddd
#else
Shinya Kitaoka 120a6e
    bool ok  = m_data.getChunkAndT_param(path, x, refChunk, t);
Toshihiro Shimizu 890ddd
#endif
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    CenterlinePoint strokeCpEnd(chunk, t1);
Shinya Kitaoka 120a6e
    CenterlinePoint refCp(refChunk, t);
Shinya Kitaoka 120a6e
    CenterlinePoint newPoints[2];
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    strokeCpEnd.buildPos(*m_stroke);
Shinya Kitaoka 120a6e
    strokeCpEnd.buildDirs(*m_stroke);
Shinya Kitaoka 120a6e
    refCp.buildPos(*m_path);
Shinya Kitaoka 120a6e
    refCp.buildDirs(*m_path);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    int count = m_data.buildPoints(strokeCpEnd, refCp, newPoints);
Shinya Kitaoka 120a6e
    if (count == 1)  // Otherwise, this is either impossible, or already covered
Shinya Kitaoka 120a6e
      (this->*m_subdivisor)(addedPoints, cPoints[size_1], newPoints[0]);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  cPoints.insert(cPoints.end(), addedPoints.begin(), addedPoints.end());
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//--------------------------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void RecursiveReferenceLinearizator::subdivide(
Shinya Kitaoka 120a6e
    std::vector<centerlinepoint> &cPoints, CenterlinePoint &cp0,</centerlinepoint>
Shinya Kitaoka 120a6e
    CenterlinePoint &cp1) {
Shinya Kitaoka 120a6e
  if (!(cp0.m_hasNextD && cp1.m_hasPrevD)) return;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  const TStroke &stroke      = *this->m_stroke;
Shinya Kitaoka 120a6e
  const TThickQuadratic &ttq = *stroke.getChunk(cp0.m_chunkIdx);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  const TStroke &path = *this->m_path;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Build the distance of next from the outline of cp's 'envelope extension'
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TPointD envDirL0, envDirR0, envDirL1, envDirR1;
Shinya Kitaoka 120a6e
  buildEnvelopeDirections(cp0.m_p, cp0.m_nextD, envDirL0, envDirR0);
Shinya Kitaoka 120a6e
  buildEnvelopeDirections(cp1.m_p, cp1.m_prevD, envDirL1, envDirR1);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TPointD diff(convert(cp1.m_p) - convert(cp0.m_p));
Shinya Kitaoka 120a6e
  double d = std::max(fabs(envDirL0 * (diff + cp1.m_p.thick * envDirL1 -
Shinya Kitaoka 120a6e
                                       cp0.m_p.thick * envDirL0)),
Shinya Kitaoka 120a6e
                      fabs(envDirR0 * (diff + cp1.m_p.thick * envDirR1 -
Shinya Kitaoka 120a6e
                                       cp0.m_p.thick * envDirR0)));
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  if (d > m_data.m_pixSize && cp1.m_t - cp0.m_t > 1e-4) {
Shinya Kitaoka 120a6e
    CenterlinePoint strokeMidPoint(cp0.m_chunkIdx, 0.5 * (cp0.m_t + cp1.m_t));
Shinya Kitaoka 120a6e
    CenterlinePoint newPoints[2];
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    int count = m_data.buildPoints(*this->m_stroke, *this->m_path,
Shinya Kitaoka 120a6e
                                   strokeMidPoint, newPoints);
Shinya Kitaoka 120a6e
    if (count == 1)  // Otherwise, this is either impossible, or already covered
Shinya Kitaoka 120a6e
    {
Shinya Kitaoka 120a6e
      subdivide(cPoints, cp0,
Shinya Kitaoka 120a6e
                newPoints[0]);  // should I use strokeMidPoint(Prev) here ?
Shinya Kitaoka 120a6e
      subdivide(cPoints, newPoints[0], cp1);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
      cPoints.push_back(newPoints[0]);
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//--------------------------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void RecursiveReferenceLinearizator::subdivideCenterline(
Shinya Kitaoka 120a6e
    std::vector<centerlinepoint> &cPoints, CenterlinePoint &cp0,</centerlinepoint>
Shinya Kitaoka 120a6e
    CenterlinePoint &cp1) {
Shinya Kitaoka 120a6e
  if (cp0.m_covered || !cp0.m_hasNextD) return;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Build the distance of next from cp's 'direction extension'
Shinya Kitaoka 120a6e
  TPointD dir((1.0 / norm(cp0.m_nextD)) * cp0.m_nextD);
Shinya Kitaoka 120a6e
  TPointD diff(convert(cp1.m_p) - convert(cp0.m_p));
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  double d = fabs(dir.x * diff.y - dir.y * diff.x);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (d > m_data.m_pixSize && cp1.m_t - cp0.m_t > 1e-4) {
Shinya Kitaoka 120a6e
    CenterlinePoint strokeMidPoint(cp0.m_chunkIdx, 0.5 * (cp0.m_t + cp1.m_t));
Shinya Kitaoka 120a6e
    CenterlinePoint newPoints[2];
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    int count = m_data.buildPoints(*this->m_stroke, *this->m_path,
Shinya Kitaoka 120a6e
                                   strokeMidPoint, newPoints);
Shinya Kitaoka 120a6e
    if (count == 1)  // Otherwise, this is either impossible, or already covered
Shinya Kitaoka 120a6e
    {
Shinya Kitaoka 120a6e
      subdivide(cPoints, cp0,
Shinya Kitaoka 120a6e
                newPoints[0]);  // should I use strokeMidPoint(Prev) here ?
Shinya Kitaoka 120a6e
      subdivide(cPoints, newPoints[0], cp1);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      cPoints.push_back(newPoints[0]);
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//********************************************************************************
Toshihiro Shimizu 890ddd
//    Make Outline Implementation (stroke version)
Toshihiro Shimizu 890ddd
//********************************************************************************
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//********************************************************************************
Toshihiro Shimizu 890ddd
//    Make Outline Implementation (stroke version)
Toshihiro Shimizu 890ddd
//********************************************************************************
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
/*
Toshihiro Shimizu 890ddd
  Quick container to store all the linearization features to be supported.
luz paz 6454c4
  \note The set should be appropriately ordered so that linearizator dependence
Toshihiro Shimizu 890ddd
  can be supported (linearizators may work depending on knowledge of the other
Toshihiro Shimizu 890ddd
  linearized points)
Toshihiro Shimizu 890ddd
*/
Toshihiro Shimizu 890ddd
struct LinearizatorsSet {
Shinya Kitaoka 120a6e
  static const int nLinearizators = 2;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  ReferenceChunksLinearizator m_refChunksLinearizator;
Shinya Kitaoka 120a6e
  RecursiveReferenceLinearizator m_recursiveRefLinearizator;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  ReferenceLinearizator *m_linearizatorPtrs[nLinearizators];
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  LinearizatorsSet(const TStroke &stroke, const TStroke &path,
Shinya Kitaoka 120a6e
                   const StrokeOutlinizationData &data)
Shinya Kitaoka 120a6e
      : m_refChunksLinearizator(&stroke, &path, data)
Shinya Kitaoka 120a6e
      , m_recursiveRefLinearizator(&stroke, &path, data) {
Shinya Kitaoka 120a6e
    m_linearizatorPtrs[0] = &m_refChunksLinearizator;
Shinya Kitaoka 120a6e
    m_linearizatorPtrs[1] = &m_recursiveRefLinearizator;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  ReferenceLinearizator *operator[](int i) { return m_linearizatorPtrs[i]; }
Shinya Kitaoka 120a6e
  const int size() const { return nLinearizators; }
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
}  // namespace
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//============================================================================================
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void TOutlineUtil::makeOutline(const TStroke &path, const TStroke &stroke,
Shinya Kitaoka 120a6e
                               const TRectD &strokeBox, TStrokeOutline &outline,
Shinya Kitaoka 120a6e
                               const TOutlineUtil::OutlineParameter &options) {
Shinya Kitaoka 120a6e
  // Build outlinization data
Shinya Kitaoka 120a6e
  StrokeOutlinizationData data(stroke, strokeBox, options);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Build a set of linearizators for the specified stroke
Shinya Kitaoka 120a6e
  LinearizatorsSet linearizators(stroke, path, data);
Shinya Kitaoka 120a6e
  CenterlinePoint newPoints[2];
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  std::vector<centerlinepoint> cPoints, chunkPoints;</centerlinepoint>
Shinya Kitaoka 120a6e
  int i, chunksCount = stroke.getChunkCount();
Shinya Kitaoka 120a6e
  for (i = 0; i < chunksCount; ++i) {
Shinya Kitaoka 120a6e
    chunkPoints.clear();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    CenterlinePoint cp(i, 0.0);
Shinya Kitaoka 120a6e
    int j, count = data.buildPoints(stroke, path, cp, newPoints);
Shinya Kitaoka 120a6e
    for (j = 0; j < count; ++j) chunkPoints.push_back(newPoints[j]);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    int linearsCount = linearizators.size();
Shinya Kitaoka 120a6e
    for (j = 0; j < linearsCount; ++j) {
Shinya Kitaoka 120a6e
      StrokeLinearizator *linearizator = linearizators[j];
Shinya Kitaoka 120a6e
      linearizator->linearize(chunkPoints, i);
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // These points are just PUSH_BACK'd to the vector. A sorting must be
Shinya Kitaoka 120a6e
    // performed
Shinya Kitaoka 120a6e
    // before storing them in the overall centerline points vector
Shinya Kitaoka 120a6e
    std::stable_sort(chunkPoints.begin(), chunkPoints.end());
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    cPoints.insert(cPoints.end(), chunkPoints.begin(), chunkPoints.end());
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Build the final point.
Shinya Kitaoka 120a6e
  CenterlinePoint cPoint(chunksCount - 1, 1.0);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  int count = data.buildPoints(stroke, path, cPoint, newPoints);
Shinya Kitaoka 120a6e
  for (i = 0; i < count; ++i) cPoints.push_back(newPoints[i]);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // If no centerline point was built, no outline point can, too.
Shinya Kitaoka 120a6e
  // This specifically happens when the supplied path is a point.
Shinya Kitaoka 120a6e
  if (cPoints.empty()) return;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // In the selfLoop case, use its info to modify the initial point.
Shinya Kitaoka 120a6e
  if (stroke.isSelfLoop()) {
Shinya Kitaoka 120a6e
    CenterlinePoint &lastCp = cPoints[cPoints.size() - 1];
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    cPoints[0].m_prevD    = cPoint.m_prevD;
Shinya Kitaoka 120a6e
    cPoints[0].m_hasPrevD = true;
Shinya Kitaoka 120a6e
    lastCp.m_nextD        = cPoint.m_prevD;
Shinya Kitaoka 120a6e
    lastCp.m_hasNextD     = true;
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
#ifdef DEBUG_DRAW_TANGENTS
Shinya Kitaoka 120a6e
  {
Shinya Kitaoka 120a6e
    // Debug - draw centerline directions (derivatives)
Shinya Kitaoka 120a6e
    glBegin(GL_LINES);
Shinya Kitaoka 120a6e
    glColor3d(1.0, 0.0, 0.0);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    unsigned int i, size = cPoints.size();
Shinya Kitaoka 120a6e
    for (i = 0; i < size; ++i) {
Shinya Kitaoka 120a6e
      glVertex2d(cPoints[i].m_p.x, cPoints[i].m_p.y);
Shinya Kitaoka 120a6e
      glVertex2d(
Shinya Kitaoka 120a6e
          cPoints[i].m_p.x + cPoints[i].m_nextD.x * cPoints[i].m_p.thick,
Shinya Kitaoka 120a6e
          cPoints[i].m_p.y + cPoints[i].m_nextD.y * cPoints[i].m_p.thick);
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    glEnd();
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
#endif
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Now, build the outline associated to the linearized centerline
Shinya Kitaoka 120a6e
  buildOutline(stroke, cPoints, outline, data);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//============================================================================================
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
/*
Toshihiro Shimizu 890ddd
TRectD TOutlineUtil::computeBBox(const TStroke &stroke, const TStroke& path)
Toshihiro Shimizu 890ddd
{
Toshihiro Shimizu 890ddd
  typedef TStroke::OutlineOptions OOpts;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
  //First, calculate the usual stroke bbox
Toshihiro Shimizu 890ddd
  TRectD roundBBox(::computeBBox(stroke));
Toshihiro Shimizu 890ddd
  const OOpts& oOptions(stroke.outlineOptions());
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  if(oOptions.m_capStyle != OOpts::PROJECTING_CAP && oOptions.m_joinStyle !=
Shinya Kitaoka 120a6e
OOpts::MITER_JOIN)
Toshihiro Shimizu 890ddd
    return roundBBox;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
  //Build interesting centerline points (in this case, junction points)
Toshihiro Shimizu 890ddd
  std::vector<centerlinepoint> cPoints;</centerlinepoint>
Toshihiro Shimizu 890ddd
  int i, chunksCount = stroke.getChunkCount();
Toshihiro Shimizu 890ddd
  for(i=0; i
Toshihiro Shimizu 890ddd
  {
Toshihiro Shimizu 890ddd
    CenterlinePoint cPoint(i, 0.0);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
    cPoint.buildPos(stroke);
Toshihiro Shimizu 890ddd
    cPoint.buildDirs(stroke);
Toshihiro Shimizu 890ddd
    cPoints.push_back(cPoint);
Toshihiro Shimizu 890ddd
  }
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
  //Build the final point.
Toshihiro Shimizu 890ddd
  CenterlinePoint cPoint(chunksCount-1, 1.0);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
  cPoint.buildPos(stroke);
Toshihiro Shimizu 890ddd
  cPoint.buildDirs(stroke);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
  //In the selfLoop case, use its info to modify the initial point.
Toshihiro Shimizu 890ddd
  if(stroke.isSelfLoop())
Toshihiro Shimizu 890ddd
  {
Toshihiro Shimizu 890ddd
    cPoints[0].m_prevD = cPoint.m_prevD;
Toshihiro Shimizu 890ddd
    cPoints[0].m_hasPrevD = true;
Toshihiro Shimizu 890ddd
    cPoint.m_nextD = cPoint.m_prevD;
Toshihiro Shimizu 890ddd
    cPoint.m_hasNextD = true;
Toshihiro Shimizu 890ddd
  }
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
  cPoints.push_back(cPoint);
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
  //Now, add the associated 'extending' outline points
Toshihiro Shimizu 890ddd
  OutlineBuilder outBuilder(OutlinizationData(), stroke);
Toshihiro Shimizu 890ddd
  TRectD extensionBBox(
Toshihiro Shimizu 890ddd
    (std::numeric_limits<double>::max)(),</double>
Toshihiro Shimizu 890ddd
    (std::numeric_limits<double>::max)(),</double>
Toshihiro Shimizu 890ddd
    -(std::numeric_limits<double>::max)(),</double>
Toshihiro Shimizu 890ddd
    -(std::numeric_limits<double>::max)()</double>
Toshihiro Shimizu 890ddd
  );
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
  unsigned int j, cPointsCount = cPoints.size();
Toshihiro Shimizu 890ddd
  for(j=0; ; ++j)
Toshihiro Shimizu 890ddd
  {
Toshihiro Shimizu 890ddd
    //Search the next uncovered point
Toshihiro Shimizu 890ddd
    for(; j < cPointsCount && cPoints[j].m_covered; ++j)
Toshihiro Shimizu 890ddd
      ;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
    if(j >= cPointsCount)
Toshihiro Shimizu 890ddd
      break;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
    //Build the associated outline points
Toshihiro Shimizu 890ddd
    outBuilder.buildOutlineExtensions(extensionBBox, cPoints[j]);
Toshihiro Shimizu 890ddd
  }
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
  //Finally, merge the 2 bboxes
Toshihiro Shimizu 890ddd
  return roundBBox + extensionBBox;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
*/
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//============================================================================================
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
namespace {
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void makeCenterline(const TStroke &path, const TEdge &edge,
Shinya Kitaoka 120a6e
                    const TRectD ®ionBox, std::vector<t3dpointd> &outline) {</t3dpointd>
Shinya Kitaoka 120a6e
  int initialOutlineSize = outline.size();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  static const TOutlineUtil::OutlineParameter options;
Shinya Kitaoka 120a6e
  const TStroke &stroke = *edge.m_s;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  double w0 = edge.m_w0, w1 = edge.m_w1;
Shinya Kitaoka 120a6e
  bool reversed    = w1 < w0;
Shinya Kitaoka 120a6e
  if (reversed) w0 = edge.m_w1, w1 = edge.m_w0;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Build outlinization data
Shinya Kitaoka 120a6e
  StrokeOutlinizationData data(stroke, regionBox, options);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Build a set of linearizators for the specified stroke
Shinya Kitaoka 120a6e
  LinearizatorsSet linearizators(stroke, path, data);
Shinya Kitaoka 120a6e
  CenterlinePoint newPoints[2];
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  linearizators.m_recursiveRefLinearizator.m_subdivisor =
Shinya Kitaoka 120a6e
      &RecursiveReferenceLinearizator::subdivideCenterline;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  std::vector<centerlinepoint> chunkPoints;</centerlinepoint>
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  int i, chunk0, chunk1;
Shinya Kitaoka 120a6e
  double t0, t1;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  bool ok0 = !edge.m_s->getChunkAndT(w0, chunk0, t0);
Shinya Kitaoka 120a6e
  bool ok1 = !edge.m_s->getChunkAndT(w1, chunk1, t1);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  assert(ok0 && ok1);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  double tStart = t0;
Shinya Kitaoka 120a6e
  for (i = chunk0; i < chunk1; ++i, tStart = 0.0) {
Shinya Kitaoka 120a6e
    chunkPoints.clear();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    CenterlinePoint cp(i, tStart);
Shinya Kitaoka 120a6e
    int j, count = data.buildPoints(stroke, path, cp, newPoints);
Shinya Kitaoka 120a6e
    for (j = 0; j < count; ++j) chunkPoints.push_back(newPoints[j]);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    int linearsCount = linearizators.size();
Shinya Kitaoka 120a6e
    for (j = 0; j < linearsCount; ++j) {
Shinya Kitaoka 120a6e
      ReferenceLinearizator *linearizator = linearizators[j];
Shinya Kitaoka 120a6e
      linearizator->linearize(chunkPoints, i, 1.0);
Shinya Kitaoka 120a6e
    }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // These points are just PUSH_BACK'd to the vector. A sorting must be
Shinya Kitaoka 120a6e
    // performed
Shinya Kitaoka 120a6e
    // before storing them in the overall centerline points vector
Shinya Kitaoka 120a6e
    std::stable_sort(chunkPoints.begin(), chunkPoints.end());
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    int size = chunkPoints.size();
Shinya Kitaoka 120a6e
    outline.reserve(outline.size() + size);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    for (j = 0; j < size; ++j) {
Shinya Kitaoka 120a6e
      const TPointD &point = chunkPoints[j].m_p;
Shinya Kitaoka 120a6e
      outline.push_back(T3DPointD(point.x, point.y, 0.0));
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // The last one with t1 as endpoint
Shinya Kitaoka 120a6e
  {
Shinya Kitaoka 120a6e
    chunkPoints.clear();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    CenterlinePoint cp(chunk1, tStart);
Shinya Kitaoka 120a6e
    int j, count = data.buildPoints(stroke, path, cp, newPoints);
Shinya Kitaoka 120a6e
    for (j = 0; j < count; ++j) chunkPoints.push_back(newPoints[j]);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    int linearsCount = linearizators.size();
Shinya Kitaoka 120a6e
    for (j = 0; j < linearsCount; ++j) {
Shinya Kitaoka 120a6e
      ReferenceLinearizator *linearizator = linearizators[j];
Shinya Kitaoka 120a6e
      linearizator->linearize(chunkPoints, chunk1, t1);
Shinya Kitaoka 120a6e
    }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    std::stable_sort(chunkPoints.begin(), chunkPoints.end());
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    int size = chunkPoints.size();
Shinya Kitaoka 120a6e
    outline.reserve(outline.size() + size);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    for (j = 0; j < size; ++j) {
Shinya Kitaoka 120a6e
      const TPointD &point = chunkPoints[j].m_p;
Shinya Kitaoka 120a6e
      outline.push_back(T3DPointD(point.x, point.y, 0.0));
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Finally, add the endpoint
Shinya Kitaoka 120a6e
  CenterlinePoint cp(chunk1, t1);
Shinya Kitaoka 120a6e
  int j, count = data.buildPoints(stroke, path, cp, newPoints);
Shinya Kitaoka 120a6e
  for (j = 0; j < count; ++j) {
Shinya Kitaoka 120a6e
    const TPointD &point = newPoints[j].m_p;
Shinya Kitaoka 120a6e
    outline.push_back(T3DPointD(point.x, point.y, 0.0));
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Eventually, reverse the output
Shinya Kitaoka 120a6e
  if (reversed)
Shinya Kitaoka 120a6e
    std::reverse(outline.begin() + initialOutlineSize, outline.end());
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//--------------------------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void makeOutlineRaw(const TStroke &path, const TRegion ®ion,
Shinya Kitaoka 120a6e
                    const TRectD ®ionBox, std::vector<t3dpointd> &outline) {</t3dpointd>
Shinya Kitaoka 120a6e
  // Deal with each edge independently
Shinya Kitaoka 120a6e
  int e, edgesCount = region.getEdgeCount();
Shinya Kitaoka 120a6e
  for (e = 0; e < edgesCount; ++e)
Shinya Kitaoka 120a6e
    makeCenterline(path, *region.getEdge(e), regionBox, outline);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
}  // namespace
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//--------------------------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void TOutlineUtil::makeOutline(const TStroke &path, const TRegion ®ion,
Shinya Kitaoka 120a6e
                               const TRectD ®ionBox,
Shinya Kitaoka 120a6e
                               TRegionOutline &outline) {
Shinya Kitaoka 120a6e
  outline.m_doAntialiasing = true;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Build the external boundary
Shinya Kitaoka 120a6e
  {
Shinya Kitaoka 120a6e
    outline.m_exterior.resize(1);
Shinya Kitaoka 120a6e
    outline.m_exterior[0].clear();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    makeOutlineRaw(path, region, regionBox, outline.m_exterior[0]);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Build internal boundaries
Shinya Kitaoka 120a6e
  {
Shinya Kitaoka 120a6e
    outline.m_interior.clear();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    int i, subRegionNumber = region.getSubregionCount();
Shinya Kitaoka 120a6e
    outline.m_interior.resize(subRegionNumber);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    for (i = 0; i < subRegionNumber; i++)
Shinya Kitaoka 120a6e
      makeOutlineRaw(path, *region.getSubregion(i), regionBox,
Shinya Kitaoka 120a6e
                     outline.m_interior[i]);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  outline.m_bbox = region.getBBox();
Toshihiro Shimizu 890ddd
}