@@ -296,6 +296,154 @@ void NanoVGGraphicsContext::setPath(juce::Path const& path, juce::AffineTransfor
296296 }
297297}
298298
299+ std::vector<juce::Path> splitIntoSubPaths (const juce::Path& original)
300+ {
301+ std::vector<juce::Path> subPaths;
302+ juce::Path currentSubPath;
303+
304+ juce::Path::Iterator it (original);
305+ while (it.next ()) {
306+ switch (it.elementType ) {
307+ case juce::Path::Iterator::startNewSubPath:
308+ if (!currentSubPath.isEmpty ()) {
309+ subPaths.push_back (currentSubPath);
310+ currentSubPath.clear ();
311+ }
312+ currentSubPath.startNewSubPath (it.x1 , it.y1 );
313+ break ;
314+
315+ case juce::Path::Iterator::lineTo:
316+ currentSubPath.lineTo (it.x1 , it.y1 );
317+ break ;
318+
319+ case juce::Path::Iterator::quadraticTo:
320+ currentSubPath.quadraticTo (it.x1 , it.y1 , it.x2 , it.y2 );
321+ break ;
322+
323+ case juce::Path::Iterator::cubicTo:
324+ currentSubPath.cubicTo (it.x1 , it.y1 , it.x2 , it.y2 , it.x3 , it.y3 );
325+ break ;
326+
327+ case juce::Path::Iterator::closePath:
328+ currentSubPath.closeSubPath ();
329+ break ;
330+ }
331+ }
332+
333+ // Push the final path if it's not empty
334+ if (!currentSubPath.isEmpty ())
335+ subPaths.push_back (currentSubPath);
336+
337+ return subPaths;
338+ }
339+
340+ bool linesIntersect (juce::Point<float > a1, juce::Point<float > a2,
341+ juce::Point<float > b1, juce::Point<float > b2)
342+ {
343+ auto cross = [](juce::Point<float > v1, juce::Point<float > v2) {
344+ return v1.x * v2.y - v1.y * v2.x ;
345+ };
346+
347+ juce::Point<float > da = a2 - a1;
348+ juce::Point<float > db = b2 - b1;
349+ juce::Point<float > delta = b1 - a1;
350+
351+ float denom = cross (da, db);
352+ if (std::abs (denom) < 1e-6f )
353+ return false ; // Lines are parallel
354+
355+ float t = cross (delta, db) / denom;
356+ float u = cross (delta, da) / denom;
357+
358+ return (t >= 0 .0f && t <= 1 .0f && u >= 0 .0f && u <= 1 .0f );
359+ }
360+
361+ void NanoVGGraphicsContext::setEvenOddPath (juce::Path const & path, juce::AffineTransform const & transform)
362+ {
363+ juce::Path p (path);
364+ p.applyTransform (transform);
365+
366+ nvgBeginPath (nvg);
367+ nvgPathWinding (nvg, NVG_SOLID);
368+
369+ auto subpaths = splitIntoSubPaths (p);
370+
371+ for (auto & path : subpaths) {
372+ int crossings = 0 ;
373+ juce::Point<float > testPoint = path.getPointAlongPath (0 ); // point on current path
374+ juce::Point<float > outsidePoint = path.getBounds ().getTopLeft () - juce::Point<float >(10 .0f , 10 .0f ); // outside point
375+
376+ for (auto & other : subpaths) {
377+ if (other == path)
378+ continue ;
379+
380+ juce::Path::Iterator it (other);
381+ juce::Point<float > p0;
382+
383+ while (it.next ()) {
384+ switch (it.elementType ) {
385+ case juce::Path::Iterator::startNewSubPath:
386+ p0 = { it.x1 , it.y1 };
387+ break ;
388+
389+ case juce::Path::Iterator::lineTo: {
390+ juce::Point<float > p1 = { it.x1 , it.y1 };
391+ if (linesIntersect (testPoint, outsidePoint, p0, p1))
392+ crossings++;
393+ p0 = p1;
394+ break ;
395+ }
396+
397+ case juce::Path::Iterator::quadraticTo: {
398+ juce::Point<float > p1 = { it.x2 , it.y2 }; // end point
399+ if (linesIntersect (testPoint, outsidePoint, p0, p1))
400+ crossings++;
401+ p0 = p1;
402+ break ;
403+ }
404+
405+ case juce::Path::Iterator::cubicTo: {
406+ juce::Point<float > p1 = { it.x3 , it.y3 }; // end point
407+ if (linesIntersect (testPoint, outsidePoint, p0, p1))
408+ crossings++;
409+ p0 = p1;
410+ break ;
411+ }
412+
413+ case juce::Path::Iterator::closePath:
414+ break ;
415+ }
416+ }
417+ }
418+
419+ juce::Path::Iterator i (path);
420+ while (i.next ()) {
421+ switch (i.elementType ) {
422+ case juce::Path::Iterator::startNewSubPath:
423+ nvgMoveTo (nvg, i.x1 , i.y1 );
424+ break ;
425+ case juce::Path::Iterator::lineTo:
426+ nvgLineTo (nvg, i.x1 , i.y1 );
427+ break ;
428+ case juce::Path::Iterator::quadraticTo:
429+ nvgQuadTo (nvg, i.x1 , i.y1 , i.x2 , i.y2 );
430+ break ;
431+ case juce::Path::Iterator::cubicTo:
432+ nvgBezierTo (nvg, i.x1 , i.y1 , i.x2 , i.y2 , i.x3 , i.y3 );
433+ break ;
434+ case juce::Path::Iterator::closePath:
435+ nvgClosePath (nvg);
436+ break ;
437+ default :
438+ break ;
439+ }
440+ }
441+
442+ nvgPathWinding (nvg, (crossings % 2 == 1 ) ? NVG_HOLE : NVG_SOLID);
443+
444+ }
445+ }
446+
299447void NanoVGGraphicsContext::fillPath (juce::Path const & path, juce::AffineTransform const & transform)
300448{
301449 setPath (path, transform);
@@ -449,12 +597,23 @@ void NanoVGGraphicsContext::drawGlyph(int const glyphNumber, juce::AffineTransfo
449597 utf8.write (wc);
450598 utf8.writeNull ();
451599
600+ nvgSave (nvg);
601+
602+ juce::Path p;
603+ auto f = getFont ();
604+ f.getTypefacePtr ()->getOutlineForGlyph (glyphNumber, p);
605+ setEvenOddPath (p, juce::AffineTransform::scale (f.getHeight () * f.getHorizontalScale (), f.getHeight ())
606+ .followedBy (transform));
607+ nvgFill (nvg);
608+ nvgRestore (nvg);
609+
610+ /*
452611 nvgSave(nvg);
453612 setFont(getFont());
454613 nvgTransform(nvg, transform.mat00, transform.mat10, transform.mat01, transform.mat11, transform.mat02, transform.mat12);
455614 nvgTextAlign(nvg, NVG_ALIGN_BASELINE | NVG_ALIGN_LEFT);
456615 nvgText(nvg, 0, 1, txt, &txt[1]);
457- nvgRestore (nvg);
616+ nvgRestore(nvg);*/
458617}
459618
460619bool NanoVGGraphicsContext::drawTextLayout (juce::AttributedString const & str, juce::Rectangle<float > const & rect)
0 commit comments