Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
// Toonz components includes
Toshihiro Shimizu 890ddd
#include "tcurveutil.h"
Toshihiro Shimizu 890ddd
#include "tinterval.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
#include "tellipticbrushP.h"
Toshihiro Shimizu 890ddd
#include "tstrokeoutline.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
using namespace tellipticbrush;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//********************************************************************************
Toshihiro Shimizu 890ddd
//    EXPLANATION
Toshihiro Shimizu 890ddd
//********************************************************************************
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
/*! \file tellipticbrush.cpp
Toshihiro Shimizu 890ddd
This code deals with the "outlinization" process of a TStroke instance.
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
The process of extracing the outline of a thick stroke can be resumed in 2 main
Shinya Kitaoka 120a6e
steps:
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
  1. Discretize the stroke centerline in the most appropriate centerline points,
Toshihiro Shimizu 890ddd
     extracting infos about position and left/right derivatives at each.
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
  2. Build the outline points associated to each individual centerline point;
Toshihiro Shimizu 890ddd
     eventually including additional junction points and caps.
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
The first major step has some sub-routines worth noting:
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
  1.1 Isolate regions of the stroke where the thickness speed is greater
Shinya Kitaoka 120a6e
      than the gemoetrical speed of the centerline. These points are
Shinya Kitaoka 120a6e
'self-covered'
Shinya Kitaoka 120a6e
      by their immediate neighbourhood, and thus cannot be seen - or build
Shinya Kitaoka 120a6e
outline directions.
Shinya Kitaoka 120a6e
  1.2 Some procedural style need to sample the centerline at a given length
Shinya Kitaoka 120a6e
step.
Toshihiro Shimizu 890ddd
  1.3 The centerline should be sampled so that the resulting polygonal outline
Shinya Kitaoka 120a6e
      approximation is tightly close to the theoretical outline, up to an error
Shinya Kitaoka 120a6e
bound.
Toshihiro Shimizu 890ddd
      The recursive approach is the simplest to deal with this issue.
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
The second step implements different outline styles to extrude the centerline
Shinya Kitaoka 120a6e
points.
Toshihiro Shimizu 890ddd
*/
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//********************************************************************************
Toshihiro Shimizu 890ddd
//    Geometric Helper Functions
Toshihiro Shimizu 890ddd
//********************************************************************************
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//! Returns the distance between two points
Shinya Kitaoka 120a6e
double tellipticbrush::dist(const TPointD &P1, const TPointD &P2) {
Shinya Kitaoka 120a6e
  return norm(P2 - P1);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//! Returns the distance between two points
Shinya Kitaoka 120a6e
double tellipticbrush::dist(const TThickPoint &P1, const TThickPoint &P2) {
Shinya Kitaoka 120a6e
  return norm(P2 - P1);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
//! Returns the angle between (unnormalized) vectors v1 and v2
Shinya Kitaoka 120a6e
double tellipticbrush::angle(const TPointD &v1, const TPointD &v2) {
Shinya Kitaoka 120a6e
  TPointD d1(v1 * (1.0 / norm(v1))), d2(v2 * (1.0 / norm(v2)));
Shinya Kitaoka 120a6e
  return atan2(cross(v1, v2), v1 * v2);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
/*!
Toshihiro Shimizu 890ddd
  Returns the intersection between two lines in the form of \b coordinates
Toshihiro Shimizu 890ddd
  from a pair of the lines' starting points. Passed directions must have norm 1.
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  If the system's determinant modulus is under the specified tolerance
Shinya Kitaoka 120a6e
  parameter,
Toshihiro Shimizu 890ddd
  TConsts::napd is returned.
Toshihiro Shimizu 890ddd
*/
Shinya Kitaoka 120a6e
TPointD tellipticbrush::intersectionCoords(const TPointD &P0, const TPointD &d0,
Shinya Kitaoka 120a6e
                                           const TPointD &P1, const TPointD &d1,
Shinya Kitaoka 120a6e
                                           double detTol) {
Shinya Kitaoka 120a6e
  // Solve P0 + x * d0 == P1 + y * d1
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  double det = d0.y * d1.x - d0.x * d1.y;
Shinya Kitaoka 120a6e
  if (fabs(det) < detTol) return TConsts::napd;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  TPointD P1_P0(P1 - P0);
Shinya Kitaoka 120a6e
  return TPointD((d1.x * P1_P0.y - d1.y * P1_P0.x) / det,
Shinya Kitaoka 120a6e
                 (d0.x * P1_P0.y - d0.y * P1_P0.x) / det);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
/*!
Shinya Kitaoka 120a6e
  Returns the left or right envelope direction of centerline point p against
Shinya Kitaoka 120a6e
  thick direction d.
Toshihiro Shimizu 890ddd
*/
Shinya Kitaoka 120a6e
void tellipticbrush::buildEnvelopeDirection(const TThickPoint &p,
Shinya Kitaoka 120a6e
                                            const TThickPoint &d, bool left,
Shinya Kitaoka 120a6e
                                            TPointD &res) {
Shinya Kitaoka 120a6e
  double dNorm2 = sq(d.x) + sq(d.y);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  double a = -d.thick / dNorm2;
Shinya Kitaoka 120a6e
  double b = sqrt(dNorm2 - sq(d.thick)) / dNorm2;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TPointD n(left ? TPointD(-d.y, d.x) : TPointD(d.y, -d.x));
Shinya Kitaoka 120a6e
  res = a * TPointD(d.x, d.y) + b * n;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void tellipticbrush::buildEnvelopeDirections(const TThickPoint &p,
Shinya Kitaoka 120a6e
                                             const TThickPoint &d,
Shinya Kitaoka 120a6e
                                             TPointD &resL, TPointD &resR) {
Shinya Kitaoka 120a6e
  double dNorm2 = sq(d.x) + sq(d.y);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  double a = -d.thick / dNorm2;
Shinya Kitaoka 120a6e
  double b = sqrt(dNorm2 - sq(d.thick)) / dNorm2;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TPointD n(-d.y, d.x);
Shinya Kitaoka 120a6e
  resL = a * TPointD(d.x, d.y) + b * n;
Shinya Kitaoka 120a6e
  resR = a * TPointD(d.x, d.y) - b * n;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
/*!
Shinya Kitaoka 120a6e
  Extrudes centerline point p against thick direction d, returning its left or
Shinya Kitaoka 120a6e
  right
Toshihiro Shimizu 890ddd
  envelope displacement vector.
Toshihiro Shimizu 890ddd
*/
Shinya Kitaoka 120a6e
void tellipticbrush::buildEnvelopeVector(const TThickPoint &p,
Shinya Kitaoka 120a6e
                                         const TThickPoint &d, bool left,
Shinya Kitaoka 120a6e
                                         TPointD &res) {
Shinya Kitaoka 120a6e
  buildEnvelopeDirection(p, d, left, res);
Shinya Kitaoka 120a6e
  res.x = p.thick * res.x;
Shinya Kitaoka 120a6e
  res.y = p.thick * res.y;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void tellipticbrush::buildEnvelopeVectors(const TThickPoint &p,
Shinya Kitaoka 120a6e
                                          const TThickPoint &d, TPointD &resL,
Shinya Kitaoka 120a6e
                                          TPointD &resR) {
Shinya Kitaoka 120a6e
  buildEnvelopeDirections(p, d, resL, resR);
Shinya Kitaoka 120a6e
  resL.x = p.thick * resL.x;
Shinya Kitaoka 120a6e
  resL.y = p.thick * resL.y;
Shinya Kitaoka 120a6e
  resR.x = p.thick * resR.x;
Shinya Kitaoka 120a6e
  resR.y = p.thick * resR.y;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
/*!
Toshihiro Shimizu 890ddd
  Builds the angle that supports a *quality* discretization of the circle
Toshihiro Shimizu 890ddd
  with maximal error < m_pixSize.
Toshihiro Shimizu 890ddd
*/
Shinya Kitaoka 120a6e
void tellipticbrush::buildAngularSubdivision(double radius, double angle,
Shinya Kitaoka 120a6e
                                             double err, int &nAngles) {
Shinya Kitaoka 120a6e
  /*
Shinya Kitaoka 120a6e
See "Graphic Gems", page 600.
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
NOTE: maxAngle is not multiplied by 2.0 as the naive pythagorical
Shinya Kitaoka 120a6e
argument would pretend. The 2.0 holds if we want to find the angle
Shinya Kitaoka 120a6e
at which the distance of the circle from its approximation is always < error.
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
But we want MORE. We want that to happen against the distance from EVERY
Shinya Kitaoka 120a6e
TANGENT LINE of the arc - not the arc itself.
Shinya Kitaoka 120a6e
This is coherent with the assumption that pixels orientation is not known.
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
It's easy to see that maxAngle just has to be not multiplied by 2.
Shinya Kitaoka 120a6e
*/
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  double maxAngle = acos(1.0 - err / radius);  //* 2.0;
Shinya Kitaoka 120a6e
  nAngles         = tceil(fabs(angle) / maxAngle);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
TRectD tellipticbrush::computeBBox(const TStroke &stroke) {
Shinya Kitaoka 120a6e
  TRectD bbox;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  int i, n = stroke.getChunkCount();
Shinya Kitaoka 120a6e
  for (i = 0; i < n; i++) bbox += stroke.getChunk(i)->getBBox();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  return bbox;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//********************************************************************************
Toshihiro Shimizu 890ddd
//    CenterlinePoint implementation
Toshihiro Shimizu 890ddd
//********************************************************************************
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void tellipticbrush::CenterlinePoint::buildPos(const TStroke &stroke) {
Shinya Kitaoka 120a6e
  if (m_posBuilt) return;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  m_p        = stroke.getChunk(m_chunkIdx)->getThickPoint(m_t);
Shinya Kitaoka 120a6e
  m_posBuilt = true;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void tellipticbrush::CenterlinePoint::buildDirs(const TStroke &stroke) {
Shinya Kitaoka 120a6e
  if (m_dirsBuilt) return;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  int chunkPrev, chunkNext;
Shinya Kitaoka 120a6e
  double tPrev, tNext;
Shinya Kitaoka 120a6e
  bool coveredPrev, coveredNext;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Discriminate the boundary cases
Shinya Kitaoka 120a6e
  bool quadBoundary;
Shinya Kitaoka 120a6e
  if (m_t == 0.0) {
Shinya Kitaoka 120a6e
    quadBoundary = true;
Shinya Kitaoka 120a6e
    chunkPrev = m_chunkIdx - 1, chunkNext = m_chunkIdx;
Shinya Kitaoka 120a6e
    tPrev = 1.0, tNext = 0.0;
Shinya Kitaoka 120a6e
  } else if (m_t == 1.0) {
Shinya Kitaoka 120a6e
    quadBoundary = true;
Shinya Kitaoka 120a6e
    chunkPrev = m_chunkIdx, chunkNext = m_chunkIdx + 1;
Shinya Kitaoka 120a6e
    tPrev = 1.0, tNext = 0.0;
Shinya Kitaoka 120a6e
  } else {
Shinya Kitaoka 120a6e
    quadBoundary = false;
Shinya Kitaoka 120a6e
    chunkPrev = chunkNext = m_chunkIdx;
Shinya Kitaoka 120a6e
    tPrev = tNext = m_t;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Build the backward direction
Shinya Kitaoka 120a6e
  if (chunkPrev >= 0) {
Shinya Kitaoka 120a6e
    const TThickQuadratic *ttqPrev = stroke.getChunk(chunkPrev);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    const TThickPoint &P0 = ttqPrev->getThickP0();
Shinya Kitaoka 120a6e
    const TThickPoint &P1 = ttqPrev->getThickP1();
Shinya Kitaoka 120a6e
    const TThickPoint &P2 = ttqPrev->getThickP2();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if (quadBoundary && (P1 == P2))
Shinya Kitaoka 120a6e
      m_prevD = P2 - P0;  // Toonz 'Linear' CPs. Eliminating a perilous
Shinya Kitaoka 120a6e
                          // singularity this way.
Shinya Kitaoka 120a6e
    else {
Shinya Kitaoka 120a6e
      m_prevD.x = 2.0 * ((P1.x - P0.x) + tPrev * (P0.x - 2.0 * P1.x + P2.x));
Shinya Kitaoka 120a6e
      m_prevD.y = 2.0 * ((P1.y - P0.y) + tPrev * (P0.y - 2.0 * P1.y + P2.y));
Shinya Kitaoka 120a6e
      m_prevD.thick = 2.0 * ((P1.thick - P0.thick) +
Shinya Kitaoka 120a6e
                             tPrev * (P0.thick - 2.0 * P1.thick + P2.thick));
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // Points whose thickness derivative does exceeds the point speed
Shinya Kitaoka 120a6e
    // cannot project envelope directions for that direction. This needs to be
Shinya Kitaoka 120a6e
    // known.
Shinya Kitaoka 120a6e
    coveredPrev = (sq(m_prevD.x) + sq(m_prevD.y) < sq(m_prevD.thick) + tolPar);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // Accept only uncovered derivatives
Shinya Kitaoka 120a6e
    m_hasPrevD = !coveredPrev;
Shinya Kitaoka 120a6e
  } else {
Shinya Kitaoka 120a6e
    m_hasPrevD  = false;
Shinya Kitaoka 120a6e
    coveredPrev = true;  // ie prev coverage must not affect next coverage
Shinya Kitaoka 120a6e
    m_prevD     = TConsts::natp;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Build the forward direction
Shinya Kitaoka 120a6e
  if (chunkPrev == chunkNext) {
Shinya Kitaoka 120a6e
    // If the quadratic is the same, no need to derive it twice
Shinya Kitaoka 120a6e
    m_hasNextD  = m_hasPrevD;
Shinya Kitaoka 120a6e
    m_nextD     = m_prevD;
Shinya Kitaoka 120a6e
    coveredNext = coveredPrev;
Shinya Kitaoka 120a6e
  } else if (chunkNext < stroke.getChunkCount()) {
Shinya Kitaoka 120a6e
    const TThickQuadratic *ttqNext = stroke.getChunk(chunkNext);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    const TThickPoint &P0 = ttqNext->getThickP0();
Shinya Kitaoka 120a6e
    const TThickPoint &P1 = ttqNext->getThickP1();
Shinya Kitaoka 120a6e
    const TThickPoint &P2 = ttqNext->getThickP2();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if (quadBoundary && (P0 == P1))
Shinya Kitaoka 120a6e
      m_nextD = P2 - P0;
Shinya Kitaoka 120a6e
    else {
Shinya Kitaoka 120a6e
      m_nextD.x = 2.0 * ((P1.x - P0.x) + tNext * (P0.x - 2.0 * P1.x + P2.x));
Shinya Kitaoka 120a6e
      m_nextD.y = 2.0 * ((P1.y - P0.y) + tNext * (P0.y - 2.0 * P1.y + P2.y));
Shinya Kitaoka 120a6e
      m_nextD.thick = 2.0 * ((P1.thick - P0.thick) +
Shinya Kitaoka 120a6e
                             tNext * (P0.thick - 2.0 * P1.thick + P2.thick));
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    coveredNext = (sq(m_nextD.x) + sq(m_nextD.y) < sq(m_nextD.thick) + tolPar);
Shinya Kitaoka 120a6e
    m_hasNextD  = !coveredNext;
Shinya Kitaoka 120a6e
  } else {
Shinya Kitaoka 120a6e
    m_hasNextD  = false;
Shinya Kitaoka 120a6e
    coveredNext = true;  // ie prev coverage must not affect next coverage
Shinya Kitaoka 120a6e
    m_nextD     = TConsts::natp;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  m_covered = (coveredPrev && coveredNext);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  m_dirsBuilt = true;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//********************************************************************************
Toshihiro Shimizu 890ddd
//    Specialized Linearizator for common stroke drawing
Toshihiro Shimizu 890ddd
//********************************************************************************
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
namespace {
Toshihiro Shimizu 890ddd
Shinya Kitaoka d1f6c4
class LengthLinearizator final : public tellipticbrush::StrokeLinearizator {
Shinya Kitaoka 120a6e
  double m_lengthStep;
Shinya Kitaoka 120a6e
  int m_countIdx;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  LengthLinearizator(const TStroke *stroke, double lengthStep)
Shinya Kitaoka 120a6e
      : StrokeLinearizator(stroke), m_lengthStep(lengthStep), m_countIdx(0) {}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 473e70
  void linearize(std::vector<centerlinepoint> &cPoints, int chunk) override;</centerlinepoint>
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//--------------------------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void LengthLinearizator::linearize(std::vector<centerlinepoint> &cPoints,</centerlinepoint>
Shinya Kitaoka 120a6e
                                   int chunk) {
Shinya Kitaoka 120a6e
  if (m_lengthStep == 0.0) return;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Retrieve the stroke length at stroke start
Shinya Kitaoka 120a6e
  double startW      = this->m_stroke->getW(chunk, 0.0);
Shinya Kitaoka 120a6e
  double startLength = this->m_stroke->getLength(startW);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Retrieve the quadratic's end length
Shinya Kitaoka 120a6e
  const TThickQuadratic *ttq = this->m_stroke->getChunk(chunk);
Shinya Kitaoka 120a6e
  double endLength           = startLength + ttq->getLength();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Build the step-length inside the chunk
Shinya Kitaoka 120a6e
  int n = tceil(startLength / m_lengthStep);
Shinya Kitaoka 120a6e
  double length;
Shinya Kitaoka 120a6e
  double t, w;
Shinya Kitaoka 120a6e
  int chk;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  for (length = n * m_lengthStep; length < endLength; length += m_lengthStep) {
Shinya Kitaoka 120a6e
    // Retrieve the new params at length. Need to use the sloppy TStroke
Shinya Kitaoka 120a6e
    // interface,
Shinya Kitaoka 120a6e
    // unfortunately...
Shinya Kitaoka 120a6e
    w = this->m_stroke->getParameterAtLength(length);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // WARNING: TStroke's interface is COMPLETELY WRONG about what gets returned
Shinya Kitaoka 120a6e
    // by the following function. This is just *CRAZY* - however, let's take it
Shinya Kitaoka 120a6e
    // all right...
Shinya Kitaoka 120a6e
    bool ok = !this->m_stroke->getChunkAndT(w, chk, t);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // In case something goes wrong, skip
Shinya Kitaoka 120a6e
    if (!ok || chk != chunk) continue;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // Store the param, that NEEDS TO BE INCREMENTALLY COUNTED - as length
Shinya Kitaoka 120a6e
    // linearization
Shinya Kitaoka 120a6e
    // is typically used for special procedural vector styles that need this
Shinya Kitaoka 120a6e
    // info.
Shinya Kitaoka 120a6e
    CenterlinePoint cPoint(chk, t);
Shinya Kitaoka 120a6e
    cPoint.m_countIdx = m_countIdx += 2;  //++m_countIdx;
Shinya Kitaoka 120a6e
    cPoints.push_back(cPoint);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//============================================================================================
Toshihiro Shimizu 890ddd
Shinya Kitaoka d1f6c4
class RecursiveLinearizator final : public tellipticbrush::StrokeLinearizator {
Shinya Kitaoka 120a6e
  double m_pixSize;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  RecursiveLinearizator(const TStroke *stroke, double pixSize)
Shinya Kitaoka 120a6e
      : StrokeLinearizator(stroke), m_pixSize(pixSize) {}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 473e70
  void linearize(std::vector<centerlinepoint> &cPoints, int chunk) override;</centerlinepoint>
Shinya Kitaoka 120a6e
  void subdivide(std::vector<centerlinepoint> &cPoints, CenterlinePoint &cp0,</centerlinepoint>
Shinya Kitaoka 120a6e
                 CenterlinePoint &cp1);
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//--------------------------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void RecursiveLinearizator::linearize(std::vector<centerlinepoint> &cPoints,</centerlinepoint>
Shinya Kitaoka 120a6e
                                      int chunk) {
Shinya Kitaoka 120a6e
  /*
Shinya Kitaoka 120a6e
Recursively linearizes the centerline, in the following way:
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
Take one point, together with the next. Add a point in the middle interval,
Shinya Kitaoka 120a6e
until
Shinya Kitaoka 120a6e
the next thick point is included (up to pixSize) in the 'forward-cast' envelope
Shinya Kitaoka 120a6e
of
Shinya Kitaoka 120a6e
current one. If the midpoint was added, repeat on the 2 sub-intervals.
Shinya Kitaoka 120a6e
*/
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
  // Sort the interval (SHOULD BE DONE OUTSIDE?)
Shinya Kitaoka 120a6e
  std::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
    cPoints[i].buildPos(stroke);
Shinya Kitaoka 120a6e
    cPoints[i].buildDirs(stroke);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    cPoints[i + 1].buildPos(stroke);
Shinya Kitaoka 120a6e
    cPoints[i + 1].buildDirs(stroke);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    subdivide(addedPoints, cPoints[i], cPoints[i + 1]);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  cPoints[size_1].buildPos(stroke);
Shinya Kitaoka 120a6e
  cPoints[size_1].buildDirs(stroke);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  CenterlinePoint cpEnd(chunk, 1.0);
Shinya Kitaoka 120a6e
  {
Shinya Kitaoka 120a6e
    const TThickPoint &P1(ttq.getThickP1());
Shinya Kitaoka 120a6e
    cpEnd.m_p = ttq.getThickP2();
Shinya Kitaoka 120a6e
    cpEnd.m_prevD =
Shinya Kitaoka 120a6e
        TThickPoint(2.0 * (cpEnd.m_p.x - P1.x), 2.0 * (cpEnd.m_p.y - P1.y),
Shinya Kitaoka 120a6e
                    2.0 * (cpEnd.m_p.thick - P1.thick));
Shinya Kitaoka 120a6e
    cpEnd.m_hasPrevD =
Shinya Kitaoka 120a6e
        true;  // The effective false case should already be dealt by sqrt...
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  subdivide(addedPoints, cPoints[size_1], cpEnd);
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 RecursiveLinearizator::subdivide(std::vector<centerlinepoint> &cPoints,</centerlinepoint>
Shinya Kitaoka 120a6e
                                      CenterlinePoint &cp0,
Shinya Kitaoka 120a6e
                                      CenterlinePoint &cp1) {
Shinya Kitaoka 120a6e
  if (!(cp0.m_hasNextD && cp1.m_hasPrevD)) return;
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_pixSize && cp1.m_t - cp0.m_t > 1e-4) {
Shinya Kitaoka 120a6e
    double midT = 0.5 * (cp0.m_t + cp1.m_t);
Shinya Kitaoka 120a6e
    CenterlinePoint midPoint(cp0.m_chunkIdx, midT);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    midPoint.buildPos(*this->m_stroke);
Shinya Kitaoka 120a6e
    midPoint.buildDirs(*this->m_stroke);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    subdivide(cPoints, cp0, midPoint);
Shinya Kitaoka 120a6e
    subdivide(cPoints, midPoint, cp1);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    cPoints.push_back(midPoint);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//============================================================================================
Toshihiro Shimizu 890ddd
Shinya Kitaoka d1f6c4
class CoverageLinearizator final : public tellipticbrush::StrokeLinearizator {
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  CoverageLinearizator(const TStroke *stroke) : StrokeLinearizator(stroke) {}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 473e70
  void linearize(std::vector<centerlinepoint> &cPoints, int chunk) override;</centerlinepoint>
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//--------------------------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void CoverageLinearizator::linearize(std::vector<centerlinepoint> &cPoints,</centerlinepoint>
Shinya Kitaoka 120a6e
                                     int chunk) {
Shinya Kitaoka 120a6e
  // Retrieve the at max 2 parameters for which:
Shinya Kitaoka 120a6e
  //    sq(d.x) + sq(d.y) == sq(d.thick) + tolPar(*)     (ie, "self-coverage"
Shinya Kitaoka 120a6e
  //    critical points)
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // It can be rewritten in the canonical form:    at^2 + bt + c == 0
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  const TThickQuadratic &ttq(*this->m_stroke->getChunk(chunk));
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TThickPoint P0(ttq.getThickP0()), P1(ttq.getThickP1()), P2(ttq.getThickP2());
Shinya Kitaoka 120a6e
  if ((P0 == P1) || (P1 == P2))
Shinya Kitaoka 120a6e
    return;  // Linear speed out/in case. Straighted up in the buildDirs()
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Remember that d = 2 [P1 - P0 + t (P0 + P2 - 2 P1)]
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  T3DPointD u(P1.x - P0.x, P1.y - P0.y, P1.thick - P0.thick);
Shinya Kitaoka 120a6e
  T3DPointD v(P0.x + P2.x - 2.0 * P1.x, P0.y + P2.y - 2.0 * P1.y,
Shinya Kitaoka 120a6e
              P0.thick + P2.thick - 2.0 * P1.thick);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  double a = sq(v.x) + sq(v.y) - sq(v.z);
Shinya Kitaoka 120a6e
  if (fabs(a) < 1e-4) return;  // Little (acceleration) quadratics case
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  //(*) Build tolerance - 2.0 since tolPar is already used to discriminate
Shinya Kitaoka 120a6e
  //'good' dirs. Ours must be.
Shinya Kitaoka 120a6e
  const double twiceTolPar = 2.0 * tolPar;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  double b = 2.0 * (u.x * v.x + u.y * v.y - u.z * v.z);
Shinya Kitaoka 120a6e
  double c = sq(u.x) + sq(u.y) - sq(u.z) - twiceTolPar;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  double delta = sq(b) - 4.0 * a * c;
Shinya Kitaoka 120a6e
  if (delta < 0) return;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  double sqrtDelta = sqrt(delta);
Shinya Kitaoka 120a6e
  double t0        = (-b - sqrtDelta) / (2.0 * a);
Shinya Kitaoka 120a6e
  double t1        = (-b + sqrtDelta) / (2.0 * a);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  if (t0 > 0 && t0 < 1) {
Shinya Kitaoka 120a6e
    CenterlinePoint cp(chunk, t0);
Shinya Kitaoka 120a6e
    cp.buildPos(*this->m_stroke);
Shinya Kitaoka 120a6e
    cp.buildDirs(*this->m_stroke);
Shinya Kitaoka 120a6e
    cp.m_hasNextD = false;
Shinya Kitaoka 120a6e
    cPoints.push_back(cp);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  if (t1 > 0 && t1 < 1) {
Shinya Kitaoka 120a6e
    CenterlinePoint cp(chunk, t1);
Shinya Kitaoka 120a6e
    cp.buildPos(*this->m_stroke);
Shinya Kitaoka 120a6e
    cp.buildDirs(*this->m_stroke);
Shinya Kitaoka 120a6e
    cp.m_hasPrevD = false;
Shinya Kitaoka 120a6e
    cPoints.push_back(cp);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
}  // namespace
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//********************************************************************************
Toshihiro Shimizu 890ddd
//    Outline Builder implementation
Toshihiro Shimizu 890ddd
//********************************************************************************
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
tellipticbrush::OutlineBuilder::OutlineBuilder(const OutlinizationData &data,
Shinya Kitaoka 120a6e
                                               const TStroke &stroke)
Shinya Kitaoka 120a6e
    : m_pixSize(data.m_pixSize)
Shinya Kitaoka 120a6e
    , m_oOptions(stroke.outlineOptions())
Shinya Kitaoka 120a6e
    , m_lastChunk(stroke.getChunkCount() - 1) {
Shinya Kitaoka 120a6e
  typedef TStroke::OutlineOptions OutlineOptions;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  switch (m_oOptions.m_capStyle) {
Shinya Kitaoka 120a6e
  case OutlineOptions::PROJECTING_CAP: {
Shinya Kitaoka 120a6e
    m_addBeginCap =
Shinya Kitaoka 120a6e
        &OutlineBuilder::addProjectingBeginCap<std::vector<toutlinepoint>>;</std::vector<toutlinepoint>
Shinya Kitaoka 120a6e
    m_addEndCap =
Shinya Kitaoka 120a6e
        &OutlineBuilder::addProjectingEndCap<std::vector<toutlinepoint>>;</std::vector<toutlinepoint>
Shinya Kitaoka 120a6e
    m_addBeginCap_ext = &OutlineBuilder::addProjectingBeginCap<trectd>;</trectd>
Shinya Kitaoka 120a6e
    m_addEndCap_ext   = &OutlineBuilder::addProjectingEndCap<trectd>;</trectd>
Shinya Kitaoka 120a6e
    break;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  case OutlineOptions::BUTT_CAP: {
Shinya Kitaoka 120a6e
    m_addBeginCap     = &OutlineBuilder::addButtBeginCap;
Shinya Kitaoka 120a6e
    m_addEndCap       = &OutlineBuilder::addButtEndCap;
Shinya Kitaoka 120a6e
    m_addBeginCap_ext = 0;
Shinya Kitaoka 120a6e
    m_addEndCap_ext   = 0;
Shinya Kitaoka 120a6e
    break;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  case OutlineOptions::ROUND_CAP:
Shinya Kitaoka 120a6e
  default:
Shinya Kitaoka 120a6e
    m_addBeginCap     = &OutlineBuilder::addRoundBeginCap;
Shinya Kitaoka 120a6e
    m_addEndCap       = &OutlineBuilder::addRoundEndCap;
Shinya Kitaoka 120a6e
    m_addBeginCap_ext = 0;
Shinya Kitaoka 120a6e
    m_addEndCap_ext   = 0;
Shinya Kitaoka 120a6e
  };
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  switch (m_oOptions.m_joinStyle) {
Shinya Kitaoka 120a6e
  case OutlineOptions::MITER_JOIN: {
Shinya Kitaoka 120a6e
    m_addSideCaps =
Shinya Kitaoka 120a6e
        &OutlineBuilder::addMiterSideCaps<std::vector<toutlinepoint>>;</std::vector<toutlinepoint>
Shinya Kitaoka 120a6e
    m_addSideCaps_ext = &OutlineBuilder::addMiterSideCaps<trectd>;</trectd>
Shinya Kitaoka 120a6e
    break;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  case OutlineOptions::BEVEL_JOIN: {
Shinya Kitaoka 120a6e
    m_addSideCaps     = &OutlineBuilder::addBevelSideCaps;
Shinya Kitaoka 120a6e
    m_addSideCaps_ext = 0;
Shinya Kitaoka 120a6e
    break;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  case OutlineOptions::ROUND_JOIN:
Shinya Kitaoka 120a6e
  default:
Shinya Kitaoka 120a6e
    m_addSideCaps     = &OutlineBuilder::addRoundSideCaps;
Shinya Kitaoka 120a6e
    m_addSideCaps_ext = 0;
Shinya Kitaoka 120a6e
  };
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
/*!
Toshihiro Shimizu 890ddd
  Translates a CenterlinePoint instance into OutlinePoints, and
Toshihiro Shimizu 890ddd
  adds them to the supplied vector container.
Toshihiro Shimizu 890ddd
*/
Toshihiro Shimizu 890ddd
void tellipticbrush::OutlineBuilder::buildOutlinePoints(
Shinya Kitaoka 120a6e
    std::vector<toutlinepoint> &oPoints, const CenterlinePoint &cPoint) {</toutlinepoint>
Shinya Kitaoka 120a6e
  // If the centerline directions exist and match, just add their envelope
Shinya Kitaoka 120a6e
  // displacement directly
Shinya Kitaoka 120a6e
  if (cPoint.m_hasPrevD && cPoint.m_hasNextD &&
Shinya Kitaoka 120a6e
      cPoint.m_prevD == cPoint.m_nextD) {
Shinya Kitaoka 120a6e
    TPointD leftD, rightD;
Shinya Kitaoka 120a6e
    buildEnvelopeVector(cPoint.m_p, cPoint.m_prevD, true, leftD);
Shinya Kitaoka 120a6e
    buildEnvelopeVector(cPoint.m_p, cPoint.m_prevD, false, rightD);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    oPoints.push_back(
Shinya Kitaoka 120a6e
        TOutlinePoint(convert(cPoint.m_p) + rightD, cPoint.m_countIdx));
Shinya Kitaoka 120a6e
    oPoints.push_back(
Shinya Kitaoka 120a6e
        TOutlinePoint(convert(cPoint.m_p) + leftD, cPoint.m_countIdx));
Shinya Kitaoka 120a6e
  } else {
Shinya Kitaoka 120a6e
    // We have to add caps/joins together with the envelope displacements
Shinya Kitaoka 120a6e
    // Caps which are not at stroke ends are always imposed to be round.
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if (cPoint.m_hasPrevD) {
Shinya Kitaoka 120a6e
      if (cPoint.m_hasNextD)
Shinya Kitaoka 120a6e
        (this->*m_addSideCaps)(oPoints, cPoint);
Shinya Kitaoka 120a6e
      else if (cPoint.m_chunkIdx == m_lastChunk && cPoint.m_t == 1.0)
Shinya Kitaoka 120a6e
        (this->*m_addEndCap)(oPoints, cPoint);
Shinya Kitaoka 120a6e
      else
Shinya Kitaoka 120a6e
        addRoundEndCap(oPoints, cPoint);
Shinya Kitaoka 120a6e
    } else {
Shinya Kitaoka 120a6e
      if (cPoint.m_hasNextD)
Shinya Kitaoka 120a6e
        if (cPoint.m_chunkIdx == 0 && cPoint.m_t == 0.0)
Shinya Kitaoka 120a6e
          (this->*m_addBeginCap)(oPoints, cPoint);
Shinya Kitaoka 120a6e
        else
Shinya Kitaoka 120a6e
          addRoundBeginCap(oPoints, cPoint);
Shinya Kitaoka 120a6e
      else
Shinya Kitaoka 120a6e
        addCircle(oPoints, cPoint);
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
/*!
Toshihiro Shimizu 890ddd
  Translates a CenterlinePoint instance into bounding box points,
Toshihiro Shimizu 890ddd
  and adds them to the supplied (bbox) rect.
Toshihiro Shimizu 890ddd
*/
Toshihiro Shimizu 890ddd
void tellipticbrush::OutlineBuilder::buildOutlineExtensions(
Shinya Kitaoka 120a6e
    TRectD &bbox, const CenterlinePoint &cPoint) {
Shinya Kitaoka 120a6e
  if (!(cPoint.m_hasPrevD && cPoint.m_hasNextD &&
Shinya Kitaoka 120a6e
        cPoint.m_prevD == cPoint.m_nextD)) {
Shinya Kitaoka 120a6e
    // Only non-envelope points are interesting to the bbox builder procedure
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if (cPoint.m_hasPrevD) {
Shinya Kitaoka 120a6e
      if (cPoint.m_hasNextD && m_addSideCaps_ext)
Shinya Kitaoka 120a6e
        (this->*m_addSideCaps_ext)(bbox, cPoint);
Shinya Kitaoka 120a6e
      else if (cPoint.m_chunkIdx == m_lastChunk && cPoint.m_t == 1.0 &&
Shinya Kitaoka 120a6e
               m_addEndCap_ext)
Shinya Kitaoka 120a6e
        (this->*m_addEndCap_ext)(bbox, cPoint);
Shinya Kitaoka 120a6e
    } else {
Shinya Kitaoka 120a6e
      if (cPoint.m_hasNextD)
Shinya Kitaoka 120a6e
        if (cPoint.m_chunkIdx == 0 && cPoint.m_t == 0.0 && m_addBeginCap_ext)
Shinya Kitaoka 120a6e
          (this->*m_addBeginCap_ext)(bbox, cPoint);
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void tellipticbrush::OutlineBuilder::addCircularArcPoints(
Shinya Kitaoka 120a6e
    int idx, std::vector<toutlinepoint> &outPoints, const TPointD ¢er,</toutlinepoint>
Shinya Kitaoka 120a6e
    const TPointD &ray, double angle, int nAngles, int countIdx) {
Shinya Kitaoka 120a6e
  TPointD rotRay(ray);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Push the initial point without rotation
Shinya Kitaoka 120a6e
  outPoints[idx] = TOutlinePoint(center + ray, countIdx);
Shinya Kitaoka 120a6e
  idx += 2;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Build the rotation
Shinya Kitaoka 120a6e
  double sin_a = sin(angle);  // NOTE: The 'angle' input parameter CANNOT be
Shinya Kitaoka 120a6e
                              // substituted with just cos,
Shinya Kitaoka 120a6e
  double cos_a = cos(angle);  // while sin = sqrt(1.0 - sq(cos)), BECAUSE this
Shinya Kitaoka 120a6e
                              // way sin is ALWAYS > 0
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  int i;
Shinya Kitaoka 120a6e
  for (i = 1; i <= nAngles; ++i, idx += 2) {
Shinya Kitaoka 120a6e
    rotRay = TPointD(rotRay.x * cos_a - rotRay.y * sin_a,
Shinya Kitaoka 120a6e
                     rotRay.x * sin_a + rotRay.y * cos_a);
Shinya Kitaoka 120a6e
    outPoints[idx] = center + rotRay;
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void tellipticbrush::OutlineBuilder::addCircle(
Shinya Kitaoka 120a6e
    std::vector<toutlinepoint> &oPoints, const CenterlinePoint &cPoint) {</toutlinepoint>
Shinya Kitaoka 120a6e
  // Build the angle step for (0, pi)
Shinya Kitaoka 120a6e
  int nAngles;
Shinya Kitaoka 120a6e
  double stepAngle, totAngle = angle(TPointD(1.0, 0.0), TPointD(-1.0, 0.0));
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  buildAngularSubdivision(cPoint.m_p.thick, totAngle, m_pixSize, nAngles);
Shinya Kitaoka 120a6e
  stepAngle = totAngle / (double)nAngles;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Resize the vector to store the required points
Shinya Kitaoka 120a6e
  int idx = oPoints.size();
Shinya Kitaoka 120a6e
  oPoints.resize(oPoints.size() + 2 * (nAngles + 1), TOutlinePoint(TPointD()));
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Add the circle points from each semi-circle
Shinya Kitaoka 120a6e
  addCircularArcPoints(idx, oPoints, convert(cPoint.m_p),
Shinya Kitaoka 120a6e
                       TPointD(cPoint.m_p.thick, 0.0), -stepAngle, nAngles,
Shinya Kitaoka 120a6e
                       cPoint.m_countIdx);
Shinya Kitaoka 120a6e
  addCircularArcPoints(idx + 1, oPoints, convert(cPoint.m_p),
Shinya Kitaoka 120a6e
                       TPointD(cPoint.m_p.thick, 0.0), stepAngle, nAngles,
Shinya Kitaoka 120a6e
                       cPoint.m_countIdx);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void tellipticbrush::OutlineBuilder::addRoundBeginCap(
Shinya Kitaoka 120a6e
    std::vector<toutlinepoint> &oPoints, const CenterlinePoint &cPoint) {</toutlinepoint>
Shinya Kitaoka 120a6e
  TPointD rightD;
Shinya Kitaoka 120a6e
  buildEnvelopeVector(cPoint.m_p, cPoint.m_nextD, false, rightD);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TPointD beginD(-convert(cPoint.m_nextD));
Shinya Kitaoka 120a6e
  beginD = (cPoint.m_p.thick / norm(beginD)) * beginD;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  int nAngles;
Shinya Kitaoka 120a6e
  double stepAngle, totAngle = angle(beginD, rightD);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  buildAngularSubdivision(cPoint.m_p.thick, totAngle, m_pixSize, nAngles);
Shinya Kitaoka 120a6e
  stepAngle = totAngle / (double)nAngles;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  int idx = oPoints.size();
Shinya Kitaoka 120a6e
  oPoints.resize(oPoints.size() + 2 * (nAngles + 1), TOutlinePoint(TPointD()));
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  addCircularArcPoints(idx, oPoints, convert(cPoint.m_p), beginD, stepAngle,
Shinya Kitaoka 120a6e
                       nAngles, cPoint.m_countIdx);
Shinya Kitaoka 120a6e
  addCircularArcPoints(idx + 1, oPoints, convert(cPoint.m_p), beginD,
Shinya Kitaoka 120a6e
                       -stepAngle, nAngles,
Shinya Kitaoka 120a6e
                       cPoint.m_countIdx);  // we just need to take the opposite
Shinya Kitaoka 120a6e
                                            // angle to deal with left side
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void tellipticbrush::OutlineBuilder::addRoundEndCap(
Shinya Kitaoka 120a6e
    std::vector<toutlinepoint> &oPoints, const CenterlinePoint &cPoint) {</toutlinepoint>
Shinya Kitaoka 120a6e
  // Build the backward envelope directions
Shinya Kitaoka 120a6e
  // Note that the situation is specular on the left and right side...
Shinya Kitaoka 120a6e
  TPointD leftD, rightD;
Shinya Kitaoka 120a6e
  buildEnvelopeVector(cPoint.m_p, cPoint.m_prevD, true, leftD);
Shinya Kitaoka 120a6e
  buildEnvelopeVector(cPoint.m_p, cPoint.m_prevD, false, rightD);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  int nAngles;
Shinya Kitaoka 120a6e
  double stepAngle, totAngle = angle(rightD, convert(cPoint.m_prevD));
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  buildAngularSubdivision(cPoint.m_p.thick, totAngle, m_pixSize, nAngles);
Shinya Kitaoka 120a6e
  stepAngle = totAngle / (double)nAngles;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  int idx = oPoints.size();
Shinya Kitaoka 120a6e
  oPoints.resize(oPoints.size() + 2 * (nAngles + 1), TOutlinePoint(TPointD()));
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  addCircularArcPoints(idx, oPoints, convert(cPoint.m_p), rightD, stepAngle,
Shinya Kitaoka 120a6e
                       nAngles, cPoint.m_countIdx);
Shinya Kitaoka 120a6e
  addCircularArcPoints(idx + 1, oPoints, convert(cPoint.m_p), leftD, -stepAngle,
Shinya Kitaoka 120a6e
                       nAngles, cPoint.m_countIdx);  // we just need to take the
Shinya Kitaoka 120a6e
                                                     // opposite angle to deal
Shinya Kitaoka 120a6e
                                                     // with left side
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void tellipticbrush::OutlineBuilder::addButtBeginCap(
Shinya Kitaoka 120a6e
    std::vector<toutlinepoint> &oPoints, const CenterlinePoint &cPoint) {</toutlinepoint>
Shinya Kitaoka 120a6e
  // Just add the 2 basic envelope points
Shinya Kitaoka 120a6e
  TPointD leftDNext, rightDNext;
Shinya Kitaoka 120a6e
  buildEnvelopeVectors(cPoint.m_p, cPoint.m_nextD, leftDNext, rightDNext);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // PLUS, add their midpoint, since it generates this part of stroke
Shinya Kitaoka 120a6e
  // antialias...
Shinya Kitaoka 120a6e
  TPointD leftP(convert(cPoint.m_p) + leftDNext),
Shinya Kitaoka 120a6e
      rightP(convert(cPoint.m_p) + rightDNext);
Shinya Kitaoka 120a6e
  TPointD midP(0.5 * (leftP + rightP));
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  oPoints.push_back(midP);
Shinya Kitaoka 120a6e
  oPoints.push_back(midP);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  oPoints.push_back(TOutlinePoint(rightP, cPoint.m_countIdx));
Shinya Kitaoka 120a6e
  oPoints.push_back(TOutlinePoint(leftP, cPoint.m_countIdx));
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void tellipticbrush::OutlineBuilder::addButtEndCap(
Shinya Kitaoka 120a6e
    std::vector<toutlinepoint> &oPoints, const CenterlinePoint &cPoint) {</toutlinepoint>
Shinya Kitaoka 120a6e
  TPointD leftDPrev, rightDPrev;
Shinya Kitaoka 120a6e
  buildEnvelopeVectors(cPoint.m_p, cPoint.m_prevD, leftDPrev, rightDPrev);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TPointD leftP(convert(cPoint.m_p) + leftDPrev),
Shinya Kitaoka 120a6e
      rightP(convert(cPoint.m_p) + rightDPrev);
Shinya Kitaoka 120a6e
  TPointD midP(0.5 * (leftP + rightP));
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  oPoints.push_back(
Shinya Kitaoka 120a6e
      TOutlinePoint(convert(cPoint.m_p) + rightDPrev, cPoint.m_countIdx));
Shinya Kitaoka 120a6e
  oPoints.push_back(
Shinya Kitaoka 120a6e
      TOutlinePoint(convert(cPoint.m_p) + leftDPrev, cPoint.m_countIdx));
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  oPoints.push_back(midP);
Shinya Kitaoka 120a6e
  oPoints.push_back(midP);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
template <typename t=""></typename>
Toshihiro Shimizu 890ddd
void tellipticbrush::OutlineBuilder::addProjectingBeginCap(
Shinya Kitaoka 120a6e
    T &oPoints, const CenterlinePoint &cPoint) {
Shinya Kitaoka 120a6e
  double thick = cPoint.m_p.thick;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Find the base points
Shinya Kitaoka 120a6e
  TPointD leftDNext, rightDNext;
Shinya Kitaoka 120a6e
  buildEnvelopeDirections(cPoint.m_p, cPoint.m_nextD, leftDNext, rightDNext);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TPointD leftP(convert(cPoint.m_p) + thick * leftDNext);
Shinya Kitaoka 120a6e
  TPointD rightP(convert(cPoint.m_p) + thick * rightDNext);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Add the intersections between the envelope directions' orthogonals and the
Shinya Kitaoka 120a6e
  // direction orthogonals
Shinya Kitaoka 120a6e
  TPointD dir(normalize(-cPoint.m_nextD));
Shinya Kitaoka 120a6e
  TPointD dirP(convert(cPoint.m_p) + thick * dir);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TPointD cornerLCoords = intersectionCoords(
Shinya Kitaoka 120a6e
      dirP, TPointD(dir.y, -dir.x), leftP, TPointD(-leftDNext.y, leftDNext.x));
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TPointD cornerRCoords =
Shinya Kitaoka 120a6e
      intersectionCoords(dirP, TPointD(-dir.y, dir.x), rightP,
Shinya Kitaoka 120a6e
                         TPointD(rightDNext.y, -rightDNext.x));
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  if (cornerLCoords.x < 0 || cornerRCoords.y < 0) return;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // As before, midPoints must be added due to antialias
Shinya Kitaoka 120a6e
  TPointD cornerL(dirP + cornerLCoords.x * TPointD(dir.y, -dir.x));
Shinya Kitaoka 120a6e
  TPointD cornerR(dirP + cornerRCoords.x * TPointD(-dir.y, dir.x));
Shinya Kitaoka 120a6e
  TPointD midP(0.5 * (cornerL + cornerR));
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  addEnvelopePoint(oPoints, midP);
Shinya Kitaoka 120a6e
  addEnvelopePoint(oPoints, midP);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  addExtensionPoint(oPoints, cornerR);
Shinya Kitaoka 120a6e
  addExtensionPoint(oPoints, cornerL);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Initial points must be added later, in the begin case
Shinya Kitaoka 120a6e
  addEnvelopePoint(oPoints, rightP, cPoint.m_countIdx);
Shinya Kitaoka 120a6e
  addEnvelopePoint(oPoints, leftP, cPoint.m_countIdx);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
template <typename t=""></typename>
Toshihiro Shimizu 890ddd
void tellipticbrush::OutlineBuilder::addProjectingEndCap(
Shinya Kitaoka 120a6e
    T &oPoints, const CenterlinePoint &cPoint) {
Shinya Kitaoka 120a6e
  double thick = cPoint.m_p.thick;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Add the base points
Shinya Kitaoka 120a6e
  TPointD leftDPrev, rightDPrev;
Shinya Kitaoka 120a6e
  buildEnvelopeDirections(cPoint.m_p, cPoint.m_prevD, leftDPrev, rightDPrev);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TPointD leftP(convert(cPoint.m_p) + thick * leftDPrev);
Shinya Kitaoka 120a6e
  TPointD rightP(convert(cPoint.m_p) + thick * rightDPrev);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  addEnvelopePoint(oPoints, rightP, cPoint.m_countIdx);
Shinya Kitaoka 120a6e
  addEnvelopePoint(oPoints, leftP, cPoint.m_countIdx);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Add the intersections between the envelope directions' orthogonals and the
Shinya Kitaoka 120a6e
  // direction orthogonals
Shinya Kitaoka 120a6e
  TPointD dir(normalize(cPoint.m_prevD));
Shinya Kitaoka 120a6e
  TPointD dirP(convert(cPoint.m_p) + thick * dir);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TPointD cornerLCoords = intersectionCoords(
Shinya Kitaoka 120a6e
      dirP, TPointD(-dir.y, dir.x), leftP, TPointD(leftDPrev.y, -leftDPrev.x));
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TPointD cornerRCoords =
Shinya Kitaoka 120a6e
      intersectionCoords(dirP, TPointD(dir.y, -dir.x), rightP,
Shinya Kitaoka 120a6e
                         TPointD(-rightDPrev.y, rightDPrev.x));
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  if (cornerLCoords.x < 0 || cornerRCoords.y < 0) return;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TPointD cornerL(dirP + cornerLCoords.x * TPointD(-dir.y, dir.x));
Shinya Kitaoka 120a6e
  TPointD cornerR(dirP + cornerRCoords.x * TPointD(dir.y, -dir.x));
Shinya Kitaoka 120a6e
  TPointD midP(0.5 * (cornerL + cornerR));
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  addExtensionPoint(oPoints, cornerR);
Shinya Kitaoka 120a6e
  addExtensionPoint(oPoints, cornerL);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  addEnvelopePoint(oPoints, midP);
Shinya Kitaoka 120a6e
  addEnvelopePoint(oPoints, midP);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void tellipticbrush::OutlineBuilder::addRoundSideCaps(
Shinya Kitaoka 120a6e
    std::vector<toutlinepoint> &oPoints, const CenterlinePoint &cPoint) {</toutlinepoint>
Shinya Kitaoka 120a6e
  // Side caps - this has only sense when the backward and forward
Shinya Kitaoka 120a6e
  // direction-derivatives
Shinya Kitaoka 120a6e
  // are different. This means that thay build different envelope directions.
Shinya Kitaoka 120a6e
  // So, we add
Shinya Kitaoka 120a6e
  // side caps to cover the 'elbow fractures'
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  TPointD leftDPrev, leftDNext, rightDPrev, rightDNext;
Shinya Kitaoka 120a6e
  buildEnvelopeVector(cPoint.m_p, cPoint.m_prevD, true, leftDPrev);
Shinya Kitaoka 120a6e
  buildEnvelopeVector(cPoint.m_p, cPoint.m_nextD, true, leftDNext);
Shinya Kitaoka 120a6e
  buildEnvelopeVector(cPoint.m_p, cPoint.m_prevD, false, rightDPrev);
Shinya Kitaoka 120a6e
  buildEnvelopeVector(cPoint.m_p, cPoint.m_nextD, false, rightDNext);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // This time, angle step is NOT specular
Shinya Kitaoka 120a6e
  int nAnglesL, nAnglesR;
Shinya Kitaoka 120a6e
  double totAngleL = angle(leftDPrev, leftDNext);
Shinya Kitaoka 120a6e
  double totAngleR = angle(rightDPrev, rightDNext);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // The common case is that these angles have the same sign - thus building
Shinya Kitaoka 120a6e
  // opposites arcs of a circle
Shinya Kitaoka 120a6e
  if (tsign(totAngleL) != tsign(totAngleR)) {
Shinya Kitaoka 120a6e
    // However, there may be exceptions. We must still impose
Shinya Kitaoka 120a6e
    // the constraint about 'covering opposite arcs of a circle' -
Shinya Kitaoka 120a6e
    // it is necessary to make the outline look consistently filled.
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    TPointD prevD(convert(cPoint.m_prevD)), nextD(convert(cPoint.m_nextD));
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // The only dangerous case is when the directions are near-opposed
Shinya Kitaoka 120a6e
    if (prevD * nextD < 0) {
Shinya Kitaoka 120a6e
      // Here, we must make one angle its (sign-opposite) 2*pi complement.
Shinya Kitaoka 120a6e
      // Keep the angle with the least fabs (smallest 'butterfly intersection')
Shinya Kitaoka 120a6e
      if (fabs(totAngleL) < fabs(totAngleR))
Shinya Kitaoka 120a6e
        totAngleR = (totAngleR > 0) ? totAngleR - M_2PI : totAngleR + M_2PI;
Shinya Kitaoka 120a6e
      else
Shinya Kitaoka 120a6e
        totAngleL = (totAngleL > 0) ? totAngleL - M_2PI : totAngleL + M_2PI;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  buildAngularSubdivision(cPoint.m_p.thick, totAngleL, m_pixSize, nAnglesL);
Shinya Kitaoka 120a6e
  buildAngularSubdivision(cPoint.m_p.thick, totAngleR, m_pixSize, nAnglesR);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  int nAngles       = std::max(nAnglesL, nAnglesR);
Shinya Kitaoka 120a6e
  double stepAngleL = totAngleL / (double)nAngles;
Shinya Kitaoka 120a6e
  double stepAngleR = totAngleR / (double)nAngles;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (nAnglesL == 1 && nAnglesR == 1 && fabs(totAngleL) < 0.525 &&
Shinya Kitaoka 120a6e
      fabs(totAngleR) < 0.525)  // angle < 30 degrees
Shinya Kitaoka 120a6e
  {
Shinya Kitaoka 120a6e
    // Simple case
Shinya Kitaoka 120a6e
    oPoints.push_back(
Shinya Kitaoka 120a6e
        TOutlinePoint(convert(cPoint.m_p) + rightDPrev, cPoint.m_countIdx));
Shinya Kitaoka 120a6e
    oPoints.push_back(
Shinya Kitaoka 120a6e
        TOutlinePoint(convert(cPoint.m_p) + leftDPrev, cPoint.m_countIdx));
Shinya Kitaoka 120a6e
  } else {
Shinya Kitaoka 120a6e
    int idx = oPoints.size();
Shinya Kitaoka 120a6e
    oPoints.resize(oPoints.size() + 2 * (nAngles + 1),
Shinya Kitaoka 120a6e
                   TOutlinePoint(TPointD()));
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    addCircularArcPoints(idx, oPoints, convert(cPoint.m_p), rightDPrev,
Shinya Kitaoka 120a6e
                         stepAngleR, nAngles, cPoint.m_countIdx);
Shinya Kitaoka 120a6e
    addCircularArcPoints(
Shinya Kitaoka 120a6e
        idx + 1, oPoints, convert(cPoint.m_p), leftDPrev, stepAngleL, nAngles,
Shinya Kitaoka 120a6e
        cPoint.m_countIdx);  // same angle here, as this is just a stroke
Shinya Kitaoka 120a6e
                             // direction rotation
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void tellipticbrush::OutlineBuilder::addBevelSideCaps(
Shinya Kitaoka 120a6e
    std::vector<toutlinepoint> &oPoints, const CenterlinePoint &cPoint) {</toutlinepoint>
Shinya Kitaoka 120a6e
  // Build the envelope directions
Shinya Kitaoka 120a6e
  TPointD leftDPrev, leftDNext, rightDPrev, rightDNext;
Shinya Kitaoka 120a6e
  buildEnvelopeDirections(cPoint.m_p, cPoint.m_prevD, leftDPrev, rightDPrev);
Shinya Kitaoka 120a6e
  buildEnvelopeDirections(cPoint.m_p, cPoint.m_nextD, leftDNext, rightDNext);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Add at least 2 outline points (the prevs)
Shinya Kitaoka 120a6e
  oPoints.push_back(TOutlinePoint(
Shinya Kitaoka 120a6e
      convert(cPoint.m_p) + cPoint.m_p.thick * rightDPrev, cPoint.m_countIdx));
Shinya Kitaoka 120a6e
  oPoints.push_back(TOutlinePoint(
Shinya Kitaoka 120a6e
      convert(cPoint.m_p) + cPoint.m_p.thick * leftDPrev, cPoint.m_countIdx));
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Only add the additional points when at least one of the envelope
Shinya Kitaoka 120a6e
  // differences
Shinya Kitaoka 120a6e
  // passing from prev to next is above the pixel size
Shinya Kitaoka 120a6e
  if (2.0 * cPoint.m_p.thick < m_pixSize) return;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  double threshold = sq(m_pixSize / cPoint.m_p.thick);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  double bevelSizeL = norm2(leftDNext - leftDPrev);
Shinya Kitaoka 120a6e
  double bevelSizeR = norm2(rightDNext - rightDPrev);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (bevelSizeL > threshold || bevelSizeR > threshold) {
Shinya Kitaoka 120a6e
    oPoints.push_back(convert(cPoint.m_p) + cPoint.m_p.thick * rightDNext);
Shinya Kitaoka 120a6e
    oPoints.push_back(convert(cPoint.m_p) + cPoint.m_p.thick * leftDNext);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
template <typename t=""></typename>
Toshihiro Shimizu 890ddd
void tellipticbrush::OutlineBuilder::addMiterSideCaps(
Shinya Kitaoka 120a6e
    T &oPoints, const CenterlinePoint &cPoint) {
Shinya Kitaoka 120a6e
  // Build the elbow side
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  TPointD prevD(cPoint.m_prevD);
Shinya Kitaoka 120a6e
  prevD = (1.0 / norm(prevD)) * prevD;
Shinya Kitaoka 120a6e
  TPointD nextD(cPoint.m_nextD);
Shinya Kitaoka 120a6e
  nextD = (1.0 / norm(nextD)) * nextD;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  double cross  = prevD.x * nextD.y - prevD.y * nextD.x;
Shinya Kitaoka 120a6e
  bool leftSide = (cross < 0);  // ie elbow on the left side when turning right
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Add the intersection point of the outline's tangential extensions
Shinya Kitaoka 120a6e
  //'Tangential extensions' are just the orthogonals to envelope directions
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Build envelope directions
Shinya Kitaoka 120a6e
  TPointD envPrevSide, envNextSide;
Shinya Kitaoka 120a6e
  buildEnvelopeDirection(cPoint.m_p, cPoint.m_prevD, leftSide, envPrevSide);
Shinya Kitaoka 120a6e
  buildEnvelopeDirection(cPoint.m_p, cPoint.m_nextD, leftSide, envNextSide);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Build tangential directions
Shinya Kitaoka 120a6e
  TPointD prevTangentialD, nextTangentialD;
Shinya Kitaoka 120a6e
  if (leftSide) {
Shinya Kitaoka 120a6e
    prevTangentialD = TPointD(envPrevSide.y, -envPrevSide.x);
Shinya Kitaoka 120a6e
    nextTangentialD = TPointD(-envNextSide.y, envNextSide.x);
Shinya Kitaoka 120a6e
  } else {
Shinya Kitaoka 120a6e
    prevTangentialD = TPointD(-envPrevSide.y, envPrevSide.x);
Shinya Kitaoka 120a6e
    nextTangentialD = TPointD(envNextSide.y, -envNextSide.x);
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Build the outline points in the envelope directions
Shinya Kitaoka 120a6e
  envPrevSide = cPoint.m_p.thick * envPrevSide;
Shinya Kitaoka 120a6e
  envNextSide = cPoint.m_p.thick * envNextSide;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  TPointD p0(convert(cPoint.m_p) + envPrevSide);
Shinya Kitaoka 120a6e
  TPointD p1(convert(cPoint.m_p) + envNextSide);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Set coordinates bounds
Shinya Kitaoka 120a6e
  double lowerBound =
Shinya Kitaoka 120a6e
      std::max(cPoint.m_p.thick * m_oOptions.m_miterLower, m_pixSize);
Shinya Kitaoka 120a6e
  double upperBound = cPoint.m_p.thick * m_oOptions.m_miterUpper;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Build the intersection between the 2 lines
Shinya Kitaoka 120a6e
  TPointD cornerCoords(
Shinya Kitaoka 120a6e
      intersectionCoords(p0, prevTangentialD, p1, nextTangentialD));
Shinya Kitaoka 120a6e
  if (cornerCoords == TConsts::napd || cornerCoords.x < lowerBound ||
Shinya Kitaoka 120a6e
      cornerCoords.y > upperBound || cornerCoords.y < lowerBound ||
Shinya Kitaoka 120a6e
      cornerCoords.y > upperBound) {
Shinya Kitaoka 120a6e
    // Bevel caps
Shinya Kitaoka 120a6e
    addOutlineBuilderFunc(&OutlineBuilder::addBevelSideCaps, oPoints, cPoint);
Shinya Kitaoka 120a6e
    return;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  TPointD corner(p0 + cornerCoords.x * prevTangentialD);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  TPointD envPrevNotSide, envNextNotSide;
Shinya Kitaoka 120a6e
  buildEnvelopeVector(cPoint.m_p, cPoint.m_prevD, !leftSide, envPrevNotSide);
Shinya Kitaoka 120a6e
  buildEnvelopeVector(cPoint.m_p, cPoint.m_nextD, !leftSide, envNextNotSide);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  TPointD notSidePrev(convert(cPoint.m_p) + envPrevNotSide);
Shinya Kitaoka 120a6e
  TPointD notSideNext(convert(cPoint.m_p) + envNextNotSide);
Shinya Kitaoka 120a6e
  if (leftSide) {
Shinya Kitaoka 120a6e
    addEnvelopePoint(oPoints, notSidePrev, cPoint.m_countIdx);
Shinya Kitaoka 120a6e
    addEnvelopePoint(oPoints, convert(cPoint.m_p) + envPrevSide,
Shinya Kitaoka 120a6e
                     cPoint.m_countIdx);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    addExtensionPoint(oPoints, 0.5 * (notSidePrev + notSideNext));
Shinya Kitaoka 120a6e
    addExtensionPoint(oPoints, corner);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    addEnvelopePoint(oPoints, notSideNext);
Shinya Kitaoka 120a6e
    addEnvelopePoint(oPoints, convert(cPoint.m_p) + envNextSide);
Shinya Kitaoka 120a6e
  } else {
Shinya Kitaoka 120a6e
    addEnvelopePoint(oPoints, convert(cPoint.m_p) + envPrevSide,
Shinya Kitaoka 120a6e
                     cPoint.m_countIdx);
Shinya Kitaoka 120a6e
    addEnvelopePoint(oPoints, notSidePrev, cPoint.m_countIdx);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    addExtensionPoint(oPoints, corner);
Shinya Kitaoka 120a6e
    addExtensionPoint(oPoints, 0.5 * (notSidePrev + notSideNext));
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    addEnvelopePoint(oPoints, convert(cPoint.m_p) + envNextSide);
Shinya Kitaoka 120a6e
    addEnvelopePoint(oPoints, notSideNext);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//********************************************************************************
Toshihiro Shimizu 890ddd
//    Outline Builder Function
Toshihiro Shimizu 890ddd
//********************************************************************************
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
namespace {
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
int buildCPointsData(const TStroke &stroke,
Shinya Kitaoka 120a6e
                     std::vector<centerlinepoint> &cPoints) {</centerlinepoint>
Shinya Kitaoka 120a6e
  // Build point positions
Shinya Kitaoka 120a6e
  unsigned int i, pointsCount = cPoints.size();
Shinya Kitaoka 120a6e
  int validPointsCount = 0;
Shinya Kitaoka 120a6e
  for (i = 0; i < pointsCount; ++i) {
Shinya Kitaoka 120a6e
    CenterlinePoint &cPoint = cPoints[i];
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    cPoint.buildPos(stroke);
Shinya Kitaoka 120a6e
    cPoint.buildDirs(stroke);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if (!cPoint.m_covered)
Shinya Kitaoka 120a6e
      // Covered points simply cannot build envelope directions (forward nor
Shinya Kitaoka 120a6e
      // backward).
Shinya Kitaoka 120a6e
      // So, don't consider them
Shinya Kitaoka 120a6e
      ++validPointsCount;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (!validPointsCount) {
Shinya Kitaoka 120a6e
    // Only single points may end up here. We just solve the problem
Shinya Kitaoka 120a6e
    // uncovering the first point.
Shinya Kitaoka 120a6e
    cPoints[0].m_covered = false;
Shinya Kitaoka 120a6e
    validPointsCount     = 1;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  return validPointsCount;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
}  // namespace
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void tellipticbrush::buildOutline(const TStroke &stroke,
Shinya Kitaoka 120a6e
                                  std::vector<centerlinepoint> &cPoints,</centerlinepoint>
Shinya Kitaoka 120a6e
                                  TStrokeOutline &outline,
Shinya Kitaoka 120a6e
                                  const OutlinizationData &data) {
Shinya Kitaoka 120a6e
  // Build the centerline points associated with passed stroke parameters
Shinya Kitaoka 120a6e
  int outlineSize = buildCPointsData(stroke, cPoints);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Reserve the lower bound known to the outline points
Shinya Kitaoka 120a6e
  std::vector<toutlinepoint> &oPoints = outline.getArray();</toutlinepoint>
Shinya Kitaoka 120a6e
  oPoints.reserve(2 * outlineSize);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  OutlineBuilder outBuilder(data, stroke);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Now, build the outline
Shinya Kitaoka 120a6e
  unsigned int i, cPointsCount = cPoints.size();
Shinya Kitaoka 120a6e
  for (i = 0;; ++i) {
Shinya Kitaoka 120a6e
    // Search the next uncovered point
Shinya Kitaoka 120a6e
    for (; i < cPointsCount && cPoints[i].m_covered; ++i)
Shinya Kitaoka 120a6e
      ;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    if (i >= cPointsCount) break;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // Build the associated outline points
Shinya Kitaoka 120a6e
    outBuilder.buildOutlinePoints(oPoints, cPoints[i]);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//********************************************************************************
Toshihiro Shimizu 890ddd
//    Make Outline Implementation
Toshihiro Shimizu 890ddd
//********************************************************************************
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
namespace {
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
/*
Toshihiro Shimizu 890ddd
  Quick container to store all the linearization features to be supported.
Toshihiro Shimizu 890ddd
  \note The set should be appropriately ordered so that linearizator dependance
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 = 3;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  LengthLinearizator m_lengthLinearizator;
Shinya Kitaoka 120a6e
  CoverageLinearizator m_coverageLinearizator;
Shinya Kitaoka 120a6e
  RecursiveLinearizator m_recursiveLinearizator;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  StrokeLinearizator *m_linearizatorPtrs[nLinearizators];
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  LinearizatorsSet(const TStroke &stroke, const OutlinizationData &data)
Shinya Kitaoka 120a6e
      : m_lengthLinearizator(&stroke, data.m_options.m_lengthStep)
Shinya Kitaoka 120a6e
      , m_coverageLinearizator(&stroke)
Shinya Kitaoka 120a6e
      , m_recursiveLinearizator(&stroke, data.m_pixSize) {
Shinya Kitaoka 120a6e
    m_linearizatorPtrs[0] = &m_lengthLinearizator;
Shinya Kitaoka 120a6e
    m_linearizatorPtrs[1] = &m_coverageLinearizator;
Shinya Kitaoka 120a6e
    m_linearizatorPtrs[2] = &m_recursiveLinearizator;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  StrokeLinearizator *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
Shinya Kitaoka 120a6e
void TOutlineUtil::makeOutline(const TStroke &stroke, TStrokeOutline &outline,
Shinya Kitaoka 120a6e
                               const TOutlineUtil::OutlineParameter &options) {
Shinya Kitaoka 120a6e
  // Build outlinization data
Shinya Kitaoka 120a6e
  OutlinizationData data(options);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Build a set of linearizators for the specified stroke
Shinya Kitaoka 120a6e
  LinearizatorsSet linearizators(stroke, data);
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
    chunkPoints.push_back(CenterlinePoint(i, 0.0));
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    int j, 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::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 last(chunksCount - 1, 1.0);
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 &first = cPoints[0];
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    first.buildPos(stroke);
Shinya Kitaoka 120a6e
    first.buildDirs(stroke);
Shinya Kitaoka 120a6e
    last.buildPos(stroke);
Shinya Kitaoka 120a6e
    last.buildDirs(stroke);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    first.m_prevD    = last.m_prevD;
Shinya Kitaoka 120a6e
    first.m_hasPrevD = last.m_hasPrevD;
Shinya Kitaoka 120a6e
    last.m_nextD     = first.m_nextD;
Shinya Kitaoka 120a6e
    last.m_hasNextD  = first.m_hasNextD;
Shinya Kitaoka 120a6e
    first.m_covered = last.m_covered = (first.m_covered && last.m_covered);
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  cPoints.push_back(last);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Now, build the outline associated to the linearized centerline
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // NOTE: It's NOT NECESSARY TO BUILD ALL THE CENTERLINE POINTS BEFORE THIS!
Shinya Kitaoka 120a6e
  // It's sufficient to build the outline TOGETHER with the centeraline, for
Shinya Kitaoka 120a6e
  // each quadratic!
Shinya Kitaoka 120a6e
  buildOutline(stroke, cPoints, outline, data);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//============================================================================================
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
TRectD TOutlineUtil::computeBBox(const TStroke &stroke) {
Shinya Kitaoka 120a6e
  typedef TStroke::OutlineOptions OOpts;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // First, calculate the usual stroke bbox
Shinya Kitaoka 120a6e
  TRectD roundBBox(::computeBBox(stroke));
Shinya Kitaoka 120a6e
  const OOpts &oOptions(stroke.outlineOptions());
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (oOptions.m_capStyle != OOpts::PROJECTING_CAP &&
Shinya Kitaoka 120a6e
      oOptions.m_joinStyle != OOpts::MITER_JOIN)
Shinya Kitaoka 120a6e
    return roundBBox;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Build interesting centerline points (in this case, junction points)
Shinya Kitaoka 120a6e
  std::vector<centerlinepoint> cPoints;</centerlinepoint>
Shinya Kitaoka 120a6e
  int i, chunksCount = stroke.getChunkCount();
Shinya Kitaoka 120a6e
  for (i = 0; i < chunksCount; ++i) {
Shinya Kitaoka 120a6e
    CenterlinePoint cPoint(i, 0.0);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    cPoint.buildPos(stroke);
Shinya Kitaoka 120a6e
    cPoint.buildDirs(stroke);
Shinya Kitaoka 120a6e
    cPoints.push_back(cPoint);
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Build the final point.
Shinya Kitaoka 120a6e
  CenterlinePoint last(chunksCount - 1, 1.0);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  last.buildPos(stroke);
Shinya Kitaoka 120a6e
  last.buildDirs(stroke);
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 &first = cPoints[0];
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    first.m_prevD    = last.m_prevD;
Shinya Kitaoka 120a6e
    first.m_hasPrevD = last.m_hasPrevD;
Shinya Kitaoka 120a6e
    last.m_nextD     = first.m_nextD;
Shinya Kitaoka 120a6e
    last.m_hasNextD  = first.m_hasNextD;
Shinya Kitaoka 120a6e
    first.m_covered = last.m_covered = (first.m_covered && last.m_covered);
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  cPoints.push_back(last);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Now, add the associated 'extending' outline points
Shinya Kitaoka 120a6e
  OutlineBuilder outBuilder(OutlinizationData(), stroke);
Shinya Kitaoka 120a6e
  TRectD extensionBBox((std::numeric_limits<double>::max)(),</double>
Shinya Kitaoka 120a6e
                       (std::numeric_limits<double>::max)(),</double>
Shinya Kitaoka 120a6e
                       -(std::numeric_limits<double>::max)(),</double>
Shinya Kitaoka 120a6e
                       -(std::numeric_limits<double>::max)());</double>
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  unsigned int j, cPointsCount = cPoints.size();
Shinya Kitaoka 120a6e
  for (j = 0;; ++j) {
Shinya Kitaoka 120a6e
    // Search the next uncovered point
Shinya Kitaoka 120a6e
    for (; j < cPointsCount && cPoints[j].m_covered; ++j)
Shinya Kitaoka 120a6e
      ;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if (j >= cPointsCount) break;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // Build the associated outline points
Shinya Kitaoka 120a6e
    outBuilder.buildOutlineExtensions(extensionBBox, cPoints[j]);
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Finally, merge the 2 bboxes
Shinya Kitaoka 120a6e
  return roundBBox + extensionBBox;
Toshihiro Shimizu 890ddd
}