Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
// TnzCore includes
Toshihiro Shimizu 890ddd
#include "tmathutil.h"
Toshihiro Shimizu 890ddd
#include "tcurves.h"
Toshihiro Shimizu 890ddd
#include "tbezier.h"
Toshihiro Shimizu 890ddd
#include "tstrokedeformations.h"
Toshihiro Shimizu 890ddd
#include "tstroke.h"
Toshihiro Shimizu 890ddd
#include "tcurveutil.h"
Toshihiro Shimizu 890ddd
#include "tcg_wrap.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
// tcg includes
Toshihiro Shimizu 890ddd
#include "tcg/tcg_poly_ops.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
#define INCLUDE_HPP
Toshihiro Shimizu 890ddd
#include "tcg/tcg_polylineops.h"
Toshihiro Shimizu 890ddd
#include "tcg/tcg_cyclic.h"
Toshihiro Shimizu 890ddd
#undef INCLUDE_HPP
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
#include "tstrokeutil.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//*********************************************************************************
Toshihiro Shimizu 890ddd
//    Local namespace  stuff
Toshihiro Shimizu 890ddd
//*********************************************************************************
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
namespace {
Toshihiro Shimizu 890ddd
Shinya Kitaoka 3bfa54
typedef std::vector<tthickcubic *=""> TThickCubicArray;</tthickcubic>
Shinya Kitaoka 3bfa54
typedef std::vector<tthickquadratic *=""> QuadStrokeChunkArray;</tthickquadratic>
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
int getControlPointIndex(const TStroke &stroke, double w) {
Shinya Kitaoka 120a6e
  TThickPoint p = stroke.getControlPointAtParameter(w);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  int i                 = 0;
Shinya Kitaoka 120a6e
  int controlPointCount = stroke.getControlPointCount();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  for (; i < controlPointCount; ++i)
Shinya Kitaoka 120a6e
    if (stroke.getControlPoint(i) == p) return i;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  return controlPointCount - 1;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
double findMinimum(const TStrokeDeformation &def, const TStroke &stroke,
Shinya Kitaoka 120a6e
                   double x1, double x2, double xacc, double length = 0,
Shinya Kitaoka 120a6e
                   int max_iter = 100)
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
{
Shinya Kitaoka 120a6e
  int j;
Shinya Kitaoka 120a6e
  double dx, f, fmid, xmid, rtb;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  f    = def.getDelta(stroke, x1) - length;
Shinya Kitaoka 120a6e
  fmid = def.getDelta(stroke, x2) - length;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (f == 0) return x1;
Shinya Kitaoka 120a6e
  if (fmid == 0) return x2;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (f * fmid > 0.0) return -1;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  rtb = f < 0.0 ? (dx = x2 - x1, x1) : (dx = x1 - x2, x2);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  for (j = 1; j <= max_iter; j++) {
Shinya Kitaoka 120a6e
    fmid = def.getDelta(stroke, xmid = rtb + (dx *= 0.5)) - length;
Shinya Kitaoka 120a6e
    if (fmid <= 0.0) rtb = xmid;
Shinya Kitaoka 120a6e
    if (fabs(dx) < xacc || fmid == 0.0) return rtb;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  return -2;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
/**
shun-iwasawa 443318
 * Rationale:
shun-iwasawa 443318
 *  Suppose we want to model a segment (represented by a stroke)  so that it
shun-iwasawa 443318
 * takes the shape of a parabola (the usual case offered by the modifier). We
shun-iwasawa 443318
 * assume that: (o) stroke points lie along the y=-100 axis; (o) the x's that
shun-iwasawa 443318
 * will correspond are x1=-10 and x2=+10 (obvious from the equation).
shun-iwasawa 443318
 *
shun-iwasawa 443318
 *  The parabola may be represented on the left side by a quadratic with control
shun-iwasawa 443318
 * points: P0=(-10,-100), P1=(-5,    0), P2=( 0,    0). If we know the number of
shun-iwasawa 443318
 * linear strokes representing this parabola, we also know how many "samples"
shun-iwasawa 443318
 * are required for its linearization. This parameter can be used to
shun-iwasawa 443318
 * qualitatively determine the value with which to sample the stroke to be
shun-iwasawa 443318
 * tested; there will need to be as many points to move as there are samples in
shun-iwasawa 443318
 * the reference.
shun-iwasawa 443318
 */
Shinya Kitaoka 120a6e
double computeIncrement(double strokeLength, double pixelSize) {
Shinya Kitaoka 120a6e
  assert(pixelSize > 0 && "Pixel size is negative!!!");
Shinya Kitaoka 120a6e
  assert(strokeLength > 0 && "Stroke Length size is negative!!!");
Toshihiro Shimizu 890ddd
shun-iwasawa 443318
  // height of the parabola (goes downward)
Shinya Kitaoka 120a6e
  double height = 100;
Toshihiro Shimizu 890ddd
shun-iwasawa 443318
  // I suppose I'm doing at least a 100-pixel drag
Shinya Kitaoka 120a6e
  assert(height >= 100.0);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  double x = sqrt(height);
Toshihiro Shimizu 890ddd
shun-iwasawa 443318
  // the point p1 will have to be at the intersection of the tangents to the two
shun-iwasawa 443318
  // extremes. The tangent of the point p2 and the x-axis, the other will have
shun-iwasawa 443318
  // versor given by the gradient at p0,
shun-iwasawa 443318
  //  ie: grad(x,-2 x)
shun-iwasawa 443318
  //  and if y = m x + q
Shinya Kitaoka 120a6e
  //  m =
Shinya Kitaoka 120a6e
  double m = 2.0 * x;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  double q = m * x - height;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  double p1x = q / m;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  double scale = strokeLength / (2.0 * x);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TScale scaleAffine(scale, scale);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TPointD p0 = scaleAffine * TPointD(-x, -height),
Shinya Kitaoka 120a6e
          p1 = scaleAffine * TPointD(-p1x, 0.0),
Shinya Kitaoka 120a6e
          p2 = scaleAffine * TPointD(0.0, 0.0);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TQuadratic quadratic(p0, p1, p2);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  double step = computeStep(quadratic, pixelSize);
Toshihiro Shimizu 890ddd
shun-iwasawa 443318
  //  just to add points even in the worst case.
Shinya Kitaoka 120a6e
  if (step >= 1.0) step = 0.1;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  return step;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void detectEdges(const std::vector<tpointd> &pointArray,</tpointd>
Shinya Kitaoka 120a6e
                 std::vector<uint> &edgeIndexArray) {</uint>
shun-iwasawa 443318
  // ASSUMPTION: sharpPointArray does not contain adjacent coincident points
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  int size = pointArray.size();
shun-iwasawa 443318
  // I check that there are more than three elements
Shinya Kitaoka 120a6e
  if (size < 3) return;
shun-iwasawa 443318
  //  runs pointArray and for each of its points tries to inscribe triangles
shun-iwasawa 443318
  //  (using left and right points) considering potential corners those with
shun-iwasawa 443318
  //  sides l such that dMin <= l <= dMax (actually at the first time that l >
shun-iwasawa 443318
  //  dMax: breack) and with angular aperture alpha <= alphaMax.
shun-iwasawa 443318
  // Then it looks for local maxes among the potential corners in a window of
shun-iwasawa 443318
  // semiamplitude dMax(actually at the first time dMax : breack is exceeded)
shun-iwasawa 443318
  //  default values: dMin = 7; dMax = dMin + 2; alphaMax = 2.6 (150 degrees)
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  const double dMin     = 4;
Shinya Kitaoka 120a6e
  const double dMax     = dMin + 3;
shun-iwasawa 443318
  const double alphaMax = 2.4;  // ( 137.5 degrees)
Shinya Kitaoka 120a6e
  const double dMin2    = dMin * dMin;
Shinya Kitaoka 120a6e
  const double dMax2    = dMax * dMax;
Shinya Kitaoka 120a6e
  std::vector<double> sharpnessArray;</double>
shun-iwasawa 443318
  sharpnessArray.push_back(M_PI);  //  the first point is a corner
Shinya Kitaoka 120a6e
  int nodeCount;
Shinya Kitaoka 120a6e
  for (nodeCount = 1; nodeCount < size - 1;
shun-iwasawa 443318
       ++nodeCount) {  //  scrolls the sharpPointArray excluding the extremes
Shinya Kitaoka 120a6e
    sharpnessArray.push_back(0);
Shinya Kitaoka 120a6e
    TPointD point(pointArray[nodeCount]);
Shinya Kitaoka 120a6e
    int leftCount;
Shinya Kitaoka 120a6e
    for (leftCount = nodeCount - 1; leftCount >= 0;
shun-iwasawa 443318
         --leftCount) {  //  Calculates the "left" sides of the inscribed
shun-iwasawa 443318
                         //  triangles...
Shinya Kitaoka 120a6e
      TPointD left  = pointArray[leftCount];
Shinya Kitaoka 120a6e
      double dLeft2 = norm2(left - point);
Shinya Kitaoka 120a6e
      if (dLeft2 < dMin2)
Shinya Kitaoka 120a6e
        continue;
Shinya Kitaoka 120a6e
      else if (dLeft2 > dMax2)
Shinya Kitaoka 120a6e
        break;
Shinya Kitaoka 120a6e
      int rightCount;
Shinya Kitaoka 120a6e
      for (rightCount = nodeCount + 1; rightCount < size;
shun-iwasawa 443318
           ++rightCount) {  // Calculates the "right" sides of the inscribed
shun-iwasawa 443318
                            // triangles...
Shinya Kitaoka 120a6e
        TPointD right  = pointArray[rightCount];
Shinya Kitaoka 120a6e
        double dRight2 = norm2(right - point);
Shinya Kitaoka 120a6e
        if (dRight2 < dMin2)
Shinya Kitaoka 120a6e
          continue;
Shinya Kitaoka 120a6e
        else if (dMax2 < dRight2)
Shinya Kitaoka 120a6e
          break;
Shinya Kitaoka 120a6e
shun-iwasawa 443318
        //  Calculates the "center" sides of the inscribed triangles
Shinya Kitaoka 120a6e
        double dCenter2 = norm2(left - right);
Shinya Kitaoka 120a6e
        assert(dLeft2 != 0.0 && dRight2 != 0.0);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
        double cs =
Shinya Kitaoka 120a6e
            (dLeft2 + dRight2 - dCenter2) / (2 * sqrt(dLeft2 * dRight2));
Shinya Kitaoka 120a6e
        double alpha = acos(cs);
Shinya Kitaoka 120a6e
        if (alpha > alphaMax) continue;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
        double sharpness = M_PI - alpha;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
        if (sharpnessArray[nodeCount] < sharpness)
Shinya Kitaoka 120a6e
          sharpnessArray[nodeCount] = sharpness;
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
shun-iwasawa 443318
  edgeIndexArray.push_back(0);  // the first point is a corner
Shinya Kitaoka 120a6e
shun-iwasawa 443318
  // I find local maxima by excluding extremes
Shinya Kitaoka 120a6e
  for (nodeCount = 1; nodeCount < size - 1;
shun-iwasawa 443318
       ++nodeCount) {  // scroll through the list excluding the extremes
Shinya Kitaoka 120a6e
    bool isCorner = true;
Shinya Kitaoka 120a6e
    TPointD point(pointArray[nodeCount]);
Shinya Kitaoka 120a6e
    int leftCount;
Shinya Kitaoka 120a6e
    for (leftCount = nodeCount - 1; leftCount >= 0;
shun-iwasawa 443318
         --leftCount) {  //  scrolls down the list of sharpPoints to the left of
shun-iwasawa 443318
                         //  node...
Shinya Kitaoka 120a6e
      TPointD left  = pointArray[leftCount];
Shinya Kitaoka 120a6e
      double dLeft2 = norm2(left - point);
Shinya Kitaoka 120a6e
      if (dLeft2 > dMax2) break;
Shinya Kitaoka 120a6e
      if (sharpnessArray[leftCount] > sharpnessArray[nodeCount]) {
Shinya Kitaoka 120a6e
        isCorner = false;
Shinya Kitaoka 120a6e
        break;
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
    if (isCorner) continue;
Shinya Kitaoka 120a6e
    int rightCount;
Shinya Kitaoka 120a6e
    for (rightCount = nodeCount + 1; rightCount < size;
shun-iwasawa 443318
         ++rightCount) {  // scrolls the list of sharpPoints to the right of
shun-iwasawa 443318
                          // node..
Shinya Kitaoka 120a6e
      TPointD right  = pointArray[rightCount];
Shinya Kitaoka 120a6e
      double dRight2 = norm2(right - point);
Shinya Kitaoka 120a6e
      if (dRight2 > dMax2) break;
Shinya Kitaoka 120a6e
      if (sharpnessArray[rightCount] > sharpnessArray[nodeCount]) {
Shinya Kitaoka 120a6e
        isCorner = false;
Shinya Kitaoka 120a6e
        break;
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
    if (isCorner) edgeIndexArray.push_back(nodeCount);
Shinya Kitaoka 120a6e
  }
shun-iwasawa 443318
  edgeIndexArray.push_back(size - 1);  //  the last point is a corner
Toshihiro Shimizu 890ddd
}
Shinya Kitaoka 120a6e
}  // namespace
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//*******************************************************************************
Toshihiro Shimizu 890ddd
//    API  functions
Toshihiro Shimizu 890ddd
//*******************************************************************************
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
bool increaseControlPoints(TStroke &stroke, const TStrokeDeformation &deformer,
Shinya Kitaoka 120a6e
                           double pixelSize) {
Shinya Kitaoka 120a6e
  if (isAlmostZero(stroke.getLength())) {
Shinya Kitaoka 120a6e
    return norm2(deformer.getDisplacement(stroke, 0.0)) > 0;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // step 1:
Shinya Kitaoka 120a6e
  // It's possible to have control point at not null potential
Shinya Kitaoka 120a6e
  //  but with delta equal 0 (equipotential control point)
Shinya Kitaoka 120a6e
  bool notVoidPotential = false;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  for (int i = 0; i < stroke.getControlPointCount(); ++i) {
Shinya Kitaoka 120a6e
    double par = stroke.getParameterAtControlPoint(i);
Shinya Kitaoka 120a6e
    if (deformer.getDisplacement(stroke, par) != TThickPoint()) {
Shinya Kitaoka 120a6e
      notVoidPotential = true;
Shinya Kitaoka 120a6e
      break;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // step 2:
Shinya Kitaoka 120a6e
  //  increase control point checking delta of deformer
Shinya Kitaoka 120a6e
  double maxDifference =
shun-iwasawa 443318
      deformer.getMaxDiff();  // above this delta value, points are added
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  int strokeControlPoint = stroke.getControlPointCount();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // pixelSize = sq( pixelSize );
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (pixelSize < TConsts::epsilon) pixelSize = TConsts::epsilon;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  double length = stroke.getLength(),
Shinya Kitaoka 120a6e
         // set the step function of length
Shinya Kitaoka 120a6e
      //    step = length > 1.0 ?  pixelSize * 15.0/ length : length,
Shinya Kitaoka 120a6e
      // step = 0.01,
Shinya Kitaoka 120a6e
      w = 0.0;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  double step = computeIncrement(length, pixelSize);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  double x1, x2, d1, d2, diff, offset, minimum, incr;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  incr = step;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  while (w + incr < 1.0) {
Shinya Kitaoka 120a6e
    d1 = deformer.getDelta(stroke, w);
Shinya Kitaoka 120a6e
    d2 = deformer.getDelta(stroke, w + incr);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    diff = d2 - d1;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if (fabs(diff) >= maxDifference)  // if there is a step of potential
Shinya Kitaoka 120a6e
    {
Shinya Kitaoka 120a6e
      if (tsign(diff) > 0) {
Shinya Kitaoka 120a6e
        x1 = w;
Shinya Kitaoka 120a6e
        x2 = w + incr;
Shinya Kitaoka 120a6e
      } else {
Shinya Kitaoka 120a6e
        x1 = w + incr;
Shinya Kitaoka 120a6e
        x2 = w;
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      offset = (d1 + d2) * 0.5;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      // find the position of step
Shinya Kitaoka 120a6e
      minimum = findMinimum(
Shinya Kitaoka 120a6e
          deformer, stroke, x1, x2, TConsts::epsilon, offset,
shun-iwasawa 443318
          20);  // A new control point should be put between x1 and x2. where?
shun-iwasawa 443318
      // this function finds the point at which the maxdifference value is
shun-iwasawa 443318
      // exceeded
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      // if minimum is not found or is equal to previous value
luz paz 6454c4
      //  use an heuristic...
Shinya Kitaoka 120a6e
      if (minimum < 0 || w == minimum) {
Shinya Kitaoka 120a6e
        minimum = w + incr * 0.5;
Shinya Kitaoka 120a6e
        w += step;
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      //... else insert a control point in minimum
shun-iwasawa 443318
      w = minimum;  // scanning resumes from the new point, in this way it
shun-iwasawa 443318
                    // thickens ...
shun-iwasawa 443318
Shinya Kitaoka 120a6e
      stroke.insertControlPoints(minimum);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      // update of step
Shinya Kitaoka 120a6e
      incr = step;
Shinya Kitaoka 120a6e
    } else
Shinya Kitaoka 120a6e
      incr += step;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // return true if control point are increased
Shinya Kitaoka 120a6e
  return (stroke.getControlPointCount() > strokeControlPoint) ||
Shinya Kitaoka 120a6e
         notVoidPotential;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void modifyControlPoints(TStroke &stroke, const TStrokeDeformation &deformer) {
Shinya Kitaoka 120a6e
  int cpCount = stroke.getControlPointCount();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TThickPoint newP;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  for (int i = 0; i < cpCount; ++i) {
Shinya Kitaoka 120a6e
    newP = stroke.getControlPoint(i) +
Shinya Kitaoka 120a6e
           deformer.getDisplacementForControlPoint(stroke, i);
Shinya Kitaoka 120a6e
    if (isAlmostZero(newP.thick, 0.005)) newP.thick = 0;
Shinya Kitaoka 120a6e
    stroke.setControlPoint(i, newP);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void modifyControlPoints(TStroke &stroke, const TStrokeDeformation &deformer,
Shinya Kitaoka 120a6e
                         std::vector<double> &controlPointLen) {</double>
Shinya Kitaoka 120a6e
  UINT cpCount = stroke.getControlPointCount();
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TThickPoint newP;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
#ifdef _DEBUG
Shinya Kitaoka 120a6e
  UINT debugVariable = controlPointLen.size();
Toshihiro Shimizu 890ddd
#endif
Shinya Kitaoka 120a6e
  assert(controlPointLen.size() == cpCount);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  for (UINT i = 0; i < cpCount; ++i) {
Shinya Kitaoka 120a6e
    newP =
Shinya Kitaoka 120a6e
        stroke.getControlPoint(i) +
Shinya Kitaoka 120a6e
        deformer.getDisplacementForControlPointLen(stroke, controlPointLen[i]);
Shinya Kitaoka 120a6e
    if (isAlmostZero(newP.thick, 0.005)) newP.thick = 0;
Shinya Kitaoka 120a6e
    stroke.setControlPoint(i, newP);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void modifyThickness(TStroke &stroke, const TStrokeDeformation &deformer,
Shinya Kitaoka 120a6e
                     std::vector<double> &controlPointLen, bool exponentially) {</double>
Shinya Kitaoka 120a6e
  UINT cpCount = stroke.getControlPointCount();
Shinya Kitaoka 120a6e
  assert(controlPointLen.size() == cpCount);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  double disp;
Shinya Kitaoka 120a6e
  double thick;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  for (UINT i = 0; i < cpCount; ++i) {
Shinya Kitaoka 120a6e
    disp =
Shinya Kitaoka 120a6e
        (deformer.getDisplacementForControlPointLen(stroke, controlPointLen[i]))
Shinya Kitaoka 120a6e
            .thick;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    thick = stroke.getControlPoint(i).thick;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    // The additive version is straightforward.
Shinya Kitaoka 120a6e
    // The exponential version is devised to keep derivative 1 at disp == 0;
Shinya Kitaoka 120a6e
    // it is typically used when the thickness decreases.
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    thick = (exponentially && thick >= 0.005) ? thick * exp(disp / thick)
Shinya Kitaoka 120a6e
                                              : thick + disp;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    if (thick < 0.005) thick = 0.0;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    stroke.setControlPoint(i, TThickPoint(stroke.getControlPoint(i), thick));
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void transform_thickness(TStroke &stroke, const double poly[], int deg) {
Shinya Kitaoka 120a6e
  int cp, cpCount = stroke.getControlPointCount();
Shinya Kitaoka 120a6e
  for (cp = 0; cp != cpCount; ++cp) {
Shinya Kitaoka 120a6e
    TThickPoint cpPoint = stroke.getControlPoint(cp);
Shinya Kitaoka 120a6e
    cpPoint.thick =
Shinya Kitaoka 120a6e
        std::max(tcg::poly_ops::evaluate(poly, deg, cpPoint.thick), 0.0);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    stroke.setControlPoint(cp, cpPoint);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
TStroke *Toonz::merge(const std::vector<tstroke *=""> &strokes) {</tstroke>
Shinya Kitaoka 120a6e
  if (strokes.empty()) return 0;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  std::vector<tthickpoint> new_stroke_cp;</tthickpoint>
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  int size_stroke_array = strokes.size();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  int size_cp;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  const TStroke *ref;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  TThickPoint last = TConsts::natp;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (!strokes[0]) return 0;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  new_stroke_cp.push_back(strokes[0]->getControlPoint(0));
Shinya Kitaoka 120a6e
  int i, j;
Shinya Kitaoka 120a6e
  for (i = 0; i < size_stroke_array; i++) {
Shinya Kitaoka 120a6e
    ref = strokes[i];
Shinya Kitaoka 120a6e
    if (!ref) return 0;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    size_cp = ref->getControlPointCount();
Shinya Kitaoka 120a6e
    for (j = 0; j < size_cp - 1; j++) {
Shinya Kitaoka 120a6e
      const TThickPoint &pnt = ref->getControlPoint(j);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      if (last != TConsts::natp && j == 0) {
Shinya Kitaoka 120a6e
        // new_stroke_cp.push_back( (last+pnt)*0.5 );
Shinya Kitaoka 120a6e
        new_stroke_cp.push_back(last);
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      if (j > 0) new_stroke_cp.push_back(pnt);
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
    // last point needs to be merged
Shinya Kitaoka 120a6e
    last = ref->getControlPoint(size_cp - 1);
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  new_stroke_cp.push_back(ref->getControlPoint(size_cp - 1));
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  TStroke *out = new TStroke(new_stroke_cp);
Shinya Kitaoka 120a6e
  return out;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
namespace {
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
class CpsReader {
Shinya Kitaoka 120a6e
  std::vector<tthickpoint> &m_cps;</tthickpoint>
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  typedef TPointD value_type;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  CpsReader(std::vector<tthickpoint> &cps) : m_cps(cps) {}</tthickpoint>
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  void openContainer(const TPointD &point) { addElement(point); }
Shinya Kitaoka 120a6e
  void addElement(const TPointD &point) {
Shinya Kitaoka 120a6e
    m_cps.push_back(TThickPoint(point, 0.0));
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  void closeContainer() {}
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//===========================================================
Toshihiro Shimizu 890ddd
//    Triplet to Quadratics
Toshihiro Shimizu 890ddd
//===========================================================
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
template <typename iter_type=""></typename>
Shinya Kitaoka 120a6e
double buildLength(const iter_type &begin, const iter_type &end, double tol) {
Shinya Kitaoka 120a6e
  // Build direction
Shinya Kitaoka 120a6e
  iter_type it = begin, jt;
Shinya Kitaoka 120a6e
  ++it;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  const TPointD &a = *begin, &b = *it;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  TPointD dir(normalize(b - a)), segDir;
Shinya Kitaoka 120a6e
  double dist;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  for (jt = it, ++it; it != end; jt = it, ++it) {
Shinya Kitaoka 120a6e
    segDir = *it - *jt;
Shinya Kitaoka 120a6e
    if (dir * segDir < 0) break;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    dist = tcg::point_ops::lineSignedDist(*it, a, dir);
Shinya Kitaoka 120a6e
    if (fabs(dist) > tol) {
Shinya Kitaoka 120a6e
      double s, t;
Shinya Kitaoka 120a6e
      if (dist > 0) {
Shinya Kitaoka 120a6e
        tcg::point_ops::intersectionCoords(
Shinya Kitaoka 120a6e
            *jt, segDir, a + tol * tcg::point_ops::ortLeft(dir), dir, s, t);
Shinya Kitaoka 120a6e
      } else {
Shinya Kitaoka 120a6e
        tcg::point_ops::intersectionCoords(
Shinya Kitaoka 120a6e
            *jt, segDir, a + tol * tcg::point_ops::ortRight(dir), dir, s, t);
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      s = tcrop(s, 0.0, 1.0);
Shinya Kitaoka 120a6e
      return (*jt + s * segDir - a) * dir;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  return (*jt - a) * dir;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
/*
Shinya Kitaoka 120a6e
  Converts the specified points triplet into a sequence of quadratics' CPs
Shinya Kitaoka 120a6e
  (point
Toshihiro Shimizu 890ddd
  a is not included, whereas c is).
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
  Conversion takes 4 parameters:
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
   - Adherence:     How much quadratics bend toward corners
Toshihiro Shimizu 890ddd
   - Angle:         Inner product of corner's edges - full corners threshold
Toshihiro Shimizu 890ddd
   - Relative:      Curvature radius/edge length    - full corners threshold
Toshihiro Shimizu 890ddd
   - RelativeDist:  Tolerance about edge length build-ups
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
  See below for extended explanation.
Toshihiro Shimizu 890ddd
*/
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
class TripletsConverter {
Shinya Kitaoka 120a6e
  typedef std::vector<tpointd>::const_iterator iter_type;</tpointd>
Shinya Kitaoka 120a6e
  typedef std::reverse_iterator<iter_type> riter_type;</iter_type>
Shinya Kitaoka 120a6e
  typedef tcg::cyclic_iterator<iter_type> cyclic_iter_type;</iter_type>
Shinya Kitaoka 120a6e
  typedef std::reverse_iterator<cyclic_iter_type> rcyclic_iter_type;</cyclic_iter_type>
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  bool m_circular;
Shinya Kitaoka 120a6e
  iter_type m_first, m_end, m_last;
Shinya Kitaoka 120a6e
  double m_adherenceTol, m_angleTol, m_relativeTol, m_relativeDistTol;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  TripletsConverter(const iter_type &begin, const iter_type &end,
Shinya Kitaoka 120a6e
                    double adherenceTol, double angleTol, double relativeTol,
Shinya Kitaoka 120a6e
                    double relativeDistTol)
Shinya Kitaoka 120a6e
      : m_circular(*begin == *(end - 1))
Shinya Kitaoka 120a6e
      , m_first(m_circular ? begin + 1 : begin)
Shinya Kitaoka 120a6e
      , m_end(end)
Shinya Kitaoka 120a6e
      , m_adherenceTol(adherenceTol)
Shinya Kitaoka 120a6e
      , m_angleTol(angleTol)
Shinya Kitaoka 120a6e
      , m_relativeTol(relativeTol)
Shinya Kitaoka 120a6e
      , m_relativeDistTol(relativeDistTol) {}
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Using bisector to convert a triplet
Shinya Kitaoka 120a6e
  void operator()(const TPointD &a, const iter_type &bt, const TPointD &c,
Shinya Kitaoka 120a6e
                  tcg::sequential_reader<std::vector<tpointd>> &output) {</std::vector<tpointd>
Shinya Kitaoka 120a6e
    const TPointD &b = *bt;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    double prod =
Shinya Kitaoka 120a6e
        tcg::point_ops::direction(b, a) * tcg::point_ops::direction(b, c);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if (prod > m_angleTol) {
Shinya Kitaoka 120a6e
      // Full corner
Shinya Kitaoka 120a6e
      output.addElement(0.5 * (a + b));
Shinya Kitaoka 120a6e
      output.addElement(b);
Shinya Kitaoka 120a6e
      output.addElement(0.5 * (b + c));
Shinya Kitaoka 120a6e
    } else {
Shinya Kitaoka 120a6e
      // Build the angle bisector
Shinya Kitaoka 120a6e
      TPointD a_b(a - b);
Shinya Kitaoka 120a6e
      TPointD c_b(c - b);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      double norm_a_b = norm(a_b);
Shinya Kitaoka 120a6e
      double norm_c_b = norm(c_b);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      a_b = a_b * (1.0 / norm_a_b);
Shinya Kitaoka 120a6e
      c_b = c_b * (1.0 / norm_c_b);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      TPointD v(tcg::point_ops::normalized(a_b + c_b));
Shinya Kitaoka 120a6e
      double cos_v_dir = fabs(a_b * v);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      double t1 = tcrop(m_adherenceTol / (cos_v_dir * norm_a_b), 0.0, 0.5);
Shinya Kitaoka 120a6e
      double t2 = tcrop(m_adherenceTol / (cos_v_dir * norm_c_b), 0.0, 0.5);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      if (t1 == 0.5 && t2 == 0.5) {
Shinya Kitaoka 120a6e
        // Direct conversion
Shinya Kitaoka 120a6e
        output.addElement(b);
Shinya Kitaoka 120a6e
      } else {
Shinya Kitaoka 120a6e
        // Build the quadratic split
Shinya Kitaoka 120a6e
        TPointD d(b + t1 * (a - b)), f(b + t2 * (c - b)), e(0.5 * (d + f));
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
        // Build curvature radiuses at the corner
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
        // NOTE: Both speed and acceleration would hold 2.0 as multiplier, which
Shinya Kitaoka 120a6e
        // is calculated implicitly.
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
        TPointD speed(f - d);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
        double num = norm(speed);
Shinya Kitaoka 120a6e
        if (num <= TConsts::epsilon) {
Shinya Kitaoka 120a6e
          // Curvature radius is 0 - full corner
Shinya Kitaoka 120a6e
          output.addElement(0.5 * (a + b));
Shinya Kitaoka 120a6e
          output.addElement(b);
Shinya Kitaoka 120a6e
          output.addElement(0.5 * (b + c));
Shinya Kitaoka 120a6e
        } else {
Shinya Kitaoka 120a6e
          num = 2.0 * num * num *
Shinya Kitaoka 120a6e
                num;  // would be * 8 = 2^3, divided by the 4 below
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
          double den1 =
Shinya Kitaoka 120a6e
              fabs(cross(speed, a - d));  // * 4, from both args of the cross
Shinya Kitaoka 120a6e
          double den2 = fabs(cross(speed, c - f));
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
          double radius1 = (den1 == 0.0) ? 0.0 : num / den1;
Shinya Kitaoka 120a6e
          double radius2 = (den1 == 0.0) ? 0.0 : num / den2;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
          // Build edges length
Shinya Kitaoka 120a6e
          double length1, length2;
Shinya Kitaoka 120a6e
          if (m_circular) {
Shinya Kitaoka 120a6e
            cyclic_iter_type it(bt, m_first, m_end, 0);
Shinya Kitaoka 120a6e
            cyclic_iter_type it1(bt, m_first, m_end, 1);
Shinya Kitaoka 120a6e
            cyclic_iter_type it_1(bt, m_first, m_end, -1);
Shinya Kitaoka 120a6e
            rcyclic_iter_type rit(it + 1), rit1(it_1 + 1);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
            length1 = buildLength(rit, rit1, 0.25);
Shinya Kitaoka 120a6e
            length2 = buildLength(it, it1, 0.25);
Shinya Kitaoka 120a6e
          } else {
Shinya Kitaoka 120a6e
            riter_type rit(bt + 1), rend(m_first);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
            length1 = buildLength(rit, rend, m_relativeDistTol);
Shinya Kitaoka 120a6e
            length2 = buildLength(bt, m_end, m_relativeDistTol);
Shinya Kitaoka 120a6e
          }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
          // Test curvature radiuses against edge length
Shinya Kitaoka 120a6e
          if (radius1 / length1 < m_relativeTol &&  // both must hold
Shinya Kitaoka 120a6e
              radius2 / length2 < m_relativeTol) {
Shinya Kitaoka 120a6e
            // Full corner
Shinya Kitaoka 120a6e
            output.addElement(0.5 * (a + b));
Shinya Kitaoka 120a6e
            output.addElement(b);
Shinya Kitaoka 120a6e
            output.addElement(0.5 * (b + c));
Shinya Kitaoka 120a6e
          } else {
Shinya Kitaoka 120a6e
            // Quadratic split
Shinya Kitaoka 120a6e
            output.addElement(d);
Shinya Kitaoka 120a6e
            output.addElement(e);
Shinya Kitaoka 120a6e
            output.addElement(f);
Shinya Kitaoka 120a6e
          }
Shinya Kitaoka 120a6e
        }
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    output.addElement(c);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
}  // namespace
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
void polylineToQuadratics(const std::vector<tpointd> &polyline,</tpointd>
Shinya Kitaoka 120a6e
                          std::vector<tthickpoint> &cps, double adherenceTol,</tthickpoint>
Shinya Kitaoka 120a6e
                          double angleTol, double relativeTol,
Shinya Kitaoka 120a6e
                          double relativeDistTol, double mergeTol) {
Shinya Kitaoka 120a6e
  CpsReader cpsReader(cps);
Shinya Kitaoka 120a6e
  TripletsConverter op(polyline.begin(), polyline.end(), adherenceTol, angleTol,
Shinya Kitaoka 120a6e
                       relativeTol, relativeDistTol);
Shinya Kitaoka 120a6e
  tcg::polyline_ops::toQuadratics(polyline.begin(), polyline.end(), cpsReader,
Shinya Kitaoka 120a6e
                                  op, mergeTol);
Toshihiro Shimizu 890ddd
}