mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-08 13:43:43 +00:00
wg_engine: winding fill rule
[issues 1479: FillRule](#1479) Introduced fill rule winding This rule makes sense only if path have some self intersections. In all other cases shapes are filled by even-odd behavor.
This commit is contained in:
parent
fc1e1cee39
commit
1a28f837a8
3 changed files with 160 additions and 1 deletions
|
@ -38,6 +38,51 @@ void WgGeometryData::computeTriFansIndexes()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void WgGeometryData::computeContour(WgGeometryData* data)
|
||||||
|
{
|
||||||
|
assert(data);
|
||||||
|
assert(data->positions.count > 1);
|
||||||
|
clear();
|
||||||
|
uint32_t istart = data->getIndexMinX(); // index start
|
||||||
|
uint32_t icnt = data->positions.count; // index count
|
||||||
|
|
||||||
|
uint32_t iprev = istart == 0 ? icnt - 1 : istart - 1;
|
||||||
|
uint32_t inext = (istart + 1) % icnt; // index current
|
||||||
|
WgPoint p0 = data->positions[istart]; // current segment start
|
||||||
|
bool isIntersected = false;
|
||||||
|
bool isClockWise = !isCW(data->positions[iprev], data->positions[istart], data->positions[inext]);
|
||||||
|
|
||||||
|
uint32_t ii{}; // intersected segment index
|
||||||
|
WgPoint pi{}; // intersection point
|
||||||
|
positions.push(p0);
|
||||||
|
while(!((inext == istart) && (!isIntersected))) {
|
||||||
|
if (data->getClosestIntersection(p0, data->positions[inext], pi, ii)) {
|
||||||
|
isIntersected = true;
|
||||||
|
p0 = pi;
|
||||||
|
// operate intersection point
|
||||||
|
if (isClockWise) { // clock wise behavior
|
||||||
|
if (isCW(p0, pi, data->positions[ii])) {
|
||||||
|
inext = ii;
|
||||||
|
} else {
|
||||||
|
inext = ((ii + 1) % icnt);
|
||||||
|
}
|
||||||
|
} else { // contr-clock wise behavior
|
||||||
|
if (isCW(p0, pi, data->positions[ii])) {
|
||||||
|
inext = ((ii + 1) % icnt);
|
||||||
|
} else {
|
||||||
|
inext = ii;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else { // simple next point
|
||||||
|
isIntersected = false;
|
||||||
|
p0 = data->positions[inext];
|
||||||
|
inext = (inext + 1) % icnt;
|
||||||
|
}
|
||||||
|
positions.push(p0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void WgGeometryData::appendCubic(WgPoint p1, WgPoint p2, WgPoint p3)
|
void WgGeometryData::appendCubic(WgPoint p1, WgPoint p2, WgPoint p3)
|
||||||
{
|
{
|
||||||
WgPoint p0 = positions.count > 0 ? positions.last() : WgPoint(0.0f, 0.0f);
|
WgPoint p0 = positions.count > 0 ? positions.last() : WgPoint(0.0f, 0.0f);
|
||||||
|
@ -159,6 +204,83 @@ void WgGeometryData::appendMesh(const RenderMesh* rmesh)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
bool WgGeometryData::getClosestIntersection(WgPoint p1, WgPoint p2, WgPoint& pi, uint32_t& index)
|
||||||
|
{
|
||||||
|
bool finded = false;
|
||||||
|
pi = p2;
|
||||||
|
float dist = p1.dist(p2);
|
||||||
|
for (uint32_t i = 0; i < positions.count - 1; i++) {
|
||||||
|
WgPoint p3 = positions[i+0];
|
||||||
|
WgPoint p4 = positions[i+1];
|
||||||
|
float bot = (p1.x - p2.x)*(p3.y - p4.y) - (p1.y - p2.y)*(p3.x - p4.x);
|
||||||
|
if (bot != 0) {
|
||||||
|
float top0 = (p1.x - p3.x)*(p3.y - p4.y) - (p1.y - p3.y)*(p3.x - p4.x);
|
||||||
|
float top1 = (p1.x - p2.x)*(p1.y - p3.y) - (p1.y - p2.y)*(p1.x - p3.x);
|
||||||
|
float t0 = +top0 / bot;
|
||||||
|
float t1 = -top1 / bot;
|
||||||
|
if ((t0 > 0.0f) && (t0 < 1.0f) && (t1 > 0.0f) && (t1 < 1.0f)) {
|
||||||
|
WgPoint p = { p1.x + (p2.x - p1.x) * top0 / bot, p1.y + (p2.y - p1.y) * top0 / bot };
|
||||||
|
float d = p.dist(p1);
|
||||||
|
if (d < dist) {
|
||||||
|
pi = p;
|
||||||
|
index = i;
|
||||||
|
dist = d;
|
||||||
|
finded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return finded;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WgGeometryData::isCW(WgPoint p1, WgPoint p2, WgPoint p3)
|
||||||
|
{
|
||||||
|
WgPoint v1 = p2 - p1;
|
||||||
|
WgPoint v2 = p3 - p1;
|
||||||
|
return (v1.x*v2.y - v1.y*v2.x) < 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t WgGeometryData::getIndexMinX()
|
||||||
|
{
|
||||||
|
assert(positions.count > 0);
|
||||||
|
uint32_t index = 0;
|
||||||
|
for (uint32_t i = 1; i < positions.count; i++)
|
||||||
|
if (positions[index].x > positions[i].x) index = i;
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t WgGeometryData::getIndexMaxX()
|
||||||
|
{
|
||||||
|
assert(positions.count > 0);
|
||||||
|
uint32_t index = 0;
|
||||||
|
for (uint32_t i = 1; i < positions.count; i++)
|
||||||
|
if (positions[index].x < positions[i].x) index = i;
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t WgGeometryData::getIndexMinY()
|
||||||
|
{
|
||||||
|
assert(positions.count > 0);
|
||||||
|
uint32_t index = 0;
|
||||||
|
for (uint32_t i = 1; i < positions.count; i++)
|
||||||
|
if (positions[index].y > positions[i].y) index = i;
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t WgGeometryData::getIndexMaxY()
|
||||||
|
{
|
||||||
|
assert(positions.count > 0);
|
||||||
|
uint32_t index = 0;
|
||||||
|
for (uint32_t i = 1; i < positions.count; i++)
|
||||||
|
if (positions[index].y < positions[i].y) index = i;
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void WgGeometryData::close()
|
void WgGeometryData::close()
|
||||||
{
|
{
|
||||||
if (positions.count > 1) {
|
if (positions.count > 1) {
|
||||||
|
@ -166,6 +288,14 @@ void WgGeometryData::close()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void WgGeometryData::clear()
|
||||||
|
{
|
||||||
|
indexes.clear();
|
||||||
|
positions.clear();
|
||||||
|
texCoords.clear();
|
||||||
|
}
|
||||||
|
|
||||||
//***********************************************************************
|
//***********************************************************************
|
||||||
// WgGeometryDataGroup
|
// WgGeometryDataGroup
|
||||||
//***********************************************************************
|
//***********************************************************************
|
||||||
|
@ -222,6 +352,17 @@ void WgGeometryDataGroup::stroke(const RenderShape& rshape)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void WgGeometryDataGroup::contours(WgGeometryDataGroup& outlines)
|
||||||
|
{
|
||||||
|
for (uint32_t i = 0 ; i < outlines.geometries.count; i++) {
|
||||||
|
WgGeometryData* geometry = new WgGeometryData();
|
||||||
|
geometry->computeContour(outlines.geometries[i]);
|
||||||
|
geometry->computeTriFansIndexes();
|
||||||
|
this->geometries.push(geometry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void WgGeometryDataGroup::release()
|
void WgGeometryDataGroup::release()
|
||||||
{
|
{
|
||||||
for (uint32_t i = 0; i < geometries.count; i++)
|
for (uint32_t i = 0; i < geometries.count; i++)
|
||||||
|
|
|
@ -84,6 +84,7 @@ struct WgGeometryData
|
||||||
// webgpu did not support triangle fans primitives type
|
// webgpu did not support triangle fans primitives type
|
||||||
// so we can emulate triangle fans using indexing
|
// so we can emulate triangle fans using indexing
|
||||||
void computeTriFansIndexes();
|
void computeTriFansIndexes();
|
||||||
|
void computeContour(WgGeometryData* data);
|
||||||
|
|
||||||
void appendCubic(WgPoint p1, WgPoint p2, WgPoint p3);
|
void appendCubic(WgPoint p1, WgPoint p2, WgPoint p3);
|
||||||
void appendBox(WgPoint pmin, WgPoint pmax);
|
void appendBox(WgPoint pmin, WgPoint pmax);
|
||||||
|
@ -92,7 +93,17 @@ struct WgGeometryData
|
||||||
void appendImageBox(float w, float h);
|
void appendImageBox(float w, float h);
|
||||||
void appendBlitBox();
|
void appendBlitBox();
|
||||||
void appendMesh(const RenderMesh* rmesh);
|
void appendMesh(const RenderMesh* rmesh);
|
||||||
|
|
||||||
|
bool getClosestIntersection(WgPoint p1, WgPoint p2, WgPoint& pi, uint32_t& index);
|
||||||
|
bool isCW(WgPoint p1, WgPoint p2, WgPoint p3);
|
||||||
|
|
||||||
|
uint32_t getIndexMinX();
|
||||||
|
uint32_t getIndexMaxX();
|
||||||
|
uint32_t getIndexMinY();
|
||||||
|
uint32_t getIndexMaxY();
|
||||||
|
|
||||||
void close();
|
void close();
|
||||||
|
void clear();
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WgGeometryDataGroup
|
struct WgGeometryDataGroup
|
||||||
|
@ -103,6 +114,7 @@ struct WgGeometryDataGroup
|
||||||
void getBBox(WgPoint& pmin, WgPoint& pmax);
|
void getBBox(WgPoint& pmin, WgPoint& pmax);
|
||||||
void tesselate(const RenderShape& rshape);
|
void tesselate(const RenderShape& rshape);
|
||||||
void stroke(const RenderShape& rshape);
|
void stroke(const RenderShape& rshape);
|
||||||
|
void contours(WgGeometryDataGroup& outlines);
|
||||||
void release();
|
void release();
|
||||||
private:
|
private:
|
||||||
static void decodePath(const RenderShape& rshape, WgGeometryDataGroup* outlines);
|
static void decodePath(const RenderShape& rshape, WgGeometryDataGroup* outlines);
|
||||||
|
|
|
@ -216,7 +216,13 @@ void WgRenderDataShape::updateMeshes(WgContext &context, const RenderShape &rsha
|
||||||
releaseMeshes(context);
|
releaseMeshes(context);
|
||||||
// update shapes geometry
|
// update shapes geometry
|
||||||
WgGeometryDataGroup shapes;
|
WgGeometryDataGroup shapes;
|
||||||
|
if(rshape.rule == tvg::FillRule::EvenOdd) {
|
||||||
shapes.tesselate(rshape);
|
shapes.tesselate(rshape);
|
||||||
|
} else if(rshape.rule == tvg::FillRule::Winding) {
|
||||||
|
WgGeometryDataGroup lines;
|
||||||
|
lines.tesselate(rshape);
|
||||||
|
shapes.contours(lines);
|
||||||
|
}
|
||||||
meshGroupShapes.update(context, &shapes);
|
meshGroupShapes.update(context, &shapes);
|
||||||
// update shapes bbox
|
// update shapes bbox
|
||||||
WgPoint pmin{}, pmax{};
|
WgPoint pmin{}, pmax{};
|
||||||
|
|
Loading…
Add table
Reference in a new issue