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