| 
				
				
				
				 | 
			
			 | 
			@@ -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); | 
		
		
	
	
		
			
				| 
				
					
				
				
				
				 | 
			
			 | 
			
  |