So, the first thing I did to get SVG working with Nubs and Polynomial was to write a simple class which converts from "normal" XY space to SVG coordinates (which I'm referring to as NM coordinates in this code).

class Vector { ... }

subset Vector2 of Vector where { $^v.Dim == 2 };

class SVGPad

{

has Vector2 $.xy_min;

has Vector2 $.xy_max;

has Vector2 $.mn_min;

has Vector2 $.mn_max;

multi method new(Vector2 $xy_min, Vector2 $xy_max, Vector2 $mn_min, Vector2 $mn_max)

{

self.bless(*, xy_min => $xy_min, xy_max => $xy_max, mn_min => $mn_min, mn_max => $mn_max);

}

multi method xy2mn(Vector2 $xy)

{

my $t = ($xy - $.xy_min).coordinates >>/<< ($.xy_max - $.xy_min).coordinates;

return $.mn_min + Vector.new(($.mn_max - $.mn_min).coordinates >>*<< $t);

}

So the code to use this (for now) is just

class SVGPad { ... }

class Nubs { ... }

class Polynomial { ... }

sub MakePath($curve, Range $range, SVGPad $pad)

{

my @points = RangeOfSize($range.from, $range.to, 10).map({$pad.xy2mn($curve.evaluate($_))});

my $start = @points.shift;

my $path = "M {$start.coordinates[0]} {$start.coordinates[1]}";

for @points -> $v

{

$path ~= " L {$v.coordinates[0]} {$v.coordinates[1]}";

}

return $path;

}

my @control_points = (Vector.new(-1, -2),

Vector.new(1, 0),

Vector.new(1, 1),

Vector.new(0, 1),

Vector.new(1, 2),

Vector.new(1, 2),

Vector.new(1, 2));

my @knots = (-1, -1, -1, -1, 1, 2, 2, 3, 3, 3, 3);

my Nubs $nubs = Nubs.new(3, KnotVector.new(@knots), @control_points);

my Polynomial $poly1 = $nubs.evaluate(0, Polynomial.new(0.0, 1.0));

my Polynomial $poly2 = $nubs.evaluate(1.5, Polynomial.new(0.0, 1.0));

my Polynomial $poly3 = $nubs.evaluate(2.5, Polynomial.new(0.0, 1.0));

my $pad = SVGPad.new(Vector2.new(-2.5, -2.5), Vector2.new(2.5, 2.5),

Vector2.new(0, 0), Vector2.new(400, 400));

my $svg = svg => [

:width(400), :height(400),

path => [

:d(MakePath($nubs, -1..3, $pad)), :stroke("blue"), :stroke-width(2), :fill("none")

],

path => [

:d(MakePath($poly1, -2..2, $pad)), :stroke("green"), :stroke-width(1), :fill("none")

],

path => [

:d(MakePath($poly2, 0..3, $pad)), :stroke("red"), :stroke-width(1), :fill("none")

],

path => [

:d(MakePath($poly3, 1..4, $pad)), :stroke("white"), :stroke-width(1), :fill("none")

],

];

say SVG.serialize($svg);

MakePath is a simple function which takes a "curve" (that is, an object which has an evaluate function which goes from t to x, y), a Perl 6 Range to evaluate it over, and an SVGPad, and returns a SVG path object. Then we set up a fairly simple NUBS curve, use the Nubs to Polynomial version of the evaluate function to generate the corresponding Polynomial for each segment of the curve, and output all four curves to SVG. This is still a crude first approximation, but it does work; if I knew how to include SVG in this post I could show it. Next time, hopefully.

Hi, I have a couple jobs that I would like to advertise on your site or via an email list to inform your readers about Perl programming jobs. Please get back to me as soon as you get a chance.

ReplyDeleteLook forward to hearing from you.

Chris

crose@enticelabs.com