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