|
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 |
}
|