Browse Source

Add implementation of ray tracing to determine whether a subpath is

solid or hole in SVGWidget
tags/v0.3.2
Andrew Belt 7 years ago
parent
commit
b684ef09db
1 changed files with 52 additions and 6 deletions
  1. +52
    -6
      src/widgets/SVGWidget.cpp

+ 52
- 6
src/widgets/SVGWidget.cpp View File

@@ -1,8 +1,8 @@
#include "widgets.hpp"


// #define DEBUG_ONLY(x) x
#define DEBUG_ONLY(x)
#define DEBUG_ONLY(x) x
// #define DEBUG_ONLY(x)

namespace rack {

@@ -15,9 +15,22 @@ static NVGcolor getNVGColor(uint32_t color) {
(color >> 24) & 0xff);
}

/** Returns the parameterized value of the line p2--p3 where it intersects with p0--p1 */
static float getLineCrossing(Vec p0, Vec p1, Vec p2, Vec p3) {
Vec b = p2.minus(p0);
Vec d = p1.minus(p0);
Vec e = p3.minus(p2);
float m = d.x * e.y - d.y * e.x;
// Check if lines are parallel, or if either pair of points are equal
if (fabsf(m) < 1e-6)
return NAN;
return -(d.x * b.y - d.y * b.x) / m;
}

static void drawSVG(NVGcontext *vg, NSVGimage *svg) {
DEBUG_ONLY(printf("new image: %g x %g px\n", svg->width, svg->height);)
int shapeIndex = 0;
// Iterate shape linked list
for (NSVGshape *shape = svg->shapes; shape; shape = shape->next, shapeIndex++) {
DEBUG_ONLY(printf(" new shape: %d id \"%s\", fillrule %d, from (%f, %f) to (%f, %f)\n", shapeIndex, shape->id, shape->fillRule, shape->bounds[0], shape->bounds[1], shape->bounds[2], shape->bounds[3]);)

@@ -26,11 +39,15 @@ static void drawSVG(NVGcontext *vg, NSVGimage *svg) {

nvgSave(vg);

// Only evenodd fillrule is supported
if (shape->fillRule == NSVG_FILLRULE_EVENODD) {}

if (shape->opacity < 1.0)
nvgGlobalAlpha(vg, shape->opacity);

// Build path
nvgBeginPath(vg);
// Iterate path linked list
for (NSVGpath *path = shape->paths; path; path = path->next) {
DEBUG_ONLY(printf(" new path: %d points, %s, from (%f, %f) to (%f, %f)\n", path->npts, path->closed ? "closed" : "open", path->bounds[0], path->bounds[1], path->bounds[2], path->bounds[3]);)

@@ -39,14 +56,42 @@ static void drawSVG(NVGcontext *vg, NSVGimage *svg) {
float *p = &path->pts[2*i];
nvgBezierTo(vg, p[0], p[1], p[2], p[3], p[4], p[5]);
// nvgLineTo(vg, p[4], p[5]);
DEBUG_ONLY(printf(" bezier (%f, %f) to (%f, %f)\n", p[-2], p[-1], p[4], p[5]);)
}

if (path->closed)
nvgClosePath(vg);

// TODO
// This is totally wrong but works by coincidence
if (!path->next)
// Compute whether this is a hole or a solid.
// Assume that no paths are crossing (usually true for normal SVG graphics).
// Also assume that the topology is the same if we use straight lines rather than Beziers (not always the case but usually true).
// Using the even-odd fill rule, if we draw a line from a point on the path to a point outside the boundary (e.g. top left) and count the number of times it crosses another path, the parity of this count determines whether the path is a hole (odd) or solid (even).
int crossings = 0;
Vec p0 = Vec(path->pts[0], path->pts[1]);
Vec p1 = Vec(path->bounds[0] - 1.0, path->bounds[1] - 1.0);
// Iterate all other paths
for (NSVGpath *path2 = shape->paths; path2; path2 = path2->next) {
if (path2 == path)
continue;

// Iterate all lines on the path
if (path2->npts < 4)
continue;
for (int i = 1; i < path2->npts + 3; i += 3) {
float *p = &path2->pts[2*i];
// The previous point
Vec p2 = Vec(p[-2], p[-1]);
// The current point
Vec p3 = (i < path2->npts) ? Vec(p[4], p[5]) : Vec(path2->pts[0], path2->pts[1]);
float crossing = getLineCrossing(p0, p1, p2, p3);
float crossing2 = getLineCrossing(p2, p3, p0, p1);
if (0.0 <= crossing && crossing < 1.0 && 0.0 <= crossing2) {
crossings++;
}
}
}

if (crossings % 2 == 0)
nvgPathWinding(vg, NVG_SOLID);
else
nvgPathWinding(vg, NVG_HOLE);
@@ -62,6 +107,7 @@ static void drawSVG(NVGcontext *vg, NSVGimage *svg) {
} break;
case NSVG_PAINT_LINEAR_GRADIENT: {
NSVGgradient *g = shape->fill.gradient;
(void)g;
DEBUG_ONLY(printf(" linear gradient: %f\t%f\n", g->fx, g->fy);)
} break;
case NSVG_PAINT_RADIAL_GRADIENT: {
@@ -75,7 +121,7 @@ static void drawSVG(NVGcontext *vg, NSVGimage *svg) {
NVGcolor color1 = getNVGColor(g->stops[g->nstops - 1].color);

float inverse[6];
Rect shapeBox = Rect::fromMinMax(Vec(shape->bounds[0], shape->bounds[1]), Vec(shape->bounds[2], shape->bounds[3]));
// Rect shapeBox = Rect::fromMinMax(Vec(shape->bounds[0], shape->bounds[1]), Vec(shape->bounds[2], shape->bounds[3]));
nvgTransformInverse(inverse, g->xform);
Vec c;
nvgTransformPoint(&c.x, &c.y, inverse, 5, 5);


Loading…
Cancel
Save