B-spline
A minimal translation of this C program, by Bernhard R. Fischer.
# 20211112 Raku programming solution
use Cairo;
# class point_t { has Num ($.x,$.y) is rw } # get by with two element lists
class line_t { has ($.A,$.B) is rw }
my (\WIDTH, \HEIGHT, \W_LINE, \CURVE_F, \DETACHED, \OUTPUT ) =
400, 400, 2, 0.25, 0, './b-spline.png' ;
my \cnt = #`(Number of points) ( my \pt = [
[171, 171], [185, 111], [202, 109], [202, 189], [328, 160], [208, 254],
[241, 330], [164, 252], [ 69, 278], [139, 208], [ 72, 148], [168, 172], ]
).elems;
sub angle(\g) { atan2(g.B.[1] - g.A.[1], g.B.[0] - g.A.[0]) }
sub control_points(\g, \l, @p1, @p2){
#`[ This function calculates the control points. It takes two lines g and l as
* arguments but it takes three lines into account for calculation. This is
* line g (P0/P1), line h (P1/P2), and line l (P2/P3). The control points being
* calculated are actually those for the middle line h, this is from P1 to P2.
* Line g is the predecessor and line l the successor of line h.
* @param g Pointer to first line (P0 to P1)
* @param l Pointer to third line (P2 to P3)
* @param p1 Pointer to memory of first control point.
* @param p2 Pointer to memory of second control point. ]
my \h = $ = line_t.new;
my \lgt = sqrt([+]([ g.B.[0]-l.A.[0], g.B.[1]-l.A.[1] ]>>²));#length of P1 to P2
h.B = l.A.clone; # end point of 1st tangent
# start point of tangent at same distance as end point along 'g'
h.A = g.B.[0] - lgt * cos(angle g) , g.B.[1] - lgt * sin(angle g);
my $a = angle h ; # angle of tangent
# 1st control point on tangent at distance 'lgt * CURVE_F'
@p1 = g.B.[0] + lgt * cos($a) * CURVE_F, g.B.[1] + lgt * sin($a) * CURVE_F;
h.A = g.B.clone; # start point of 2nd tangent
# end point of tangent at same distance as start point along 'l'
h.B = l.A.[0] + lgt * cos(angle l) , l.A.[1] + lgt * sin(angle l);
$a = angle h; # angle of tangent
# 2nd control point on tangent at distance 'lgt * CURVE_F'
@p2 = l.A.[0] - lgt * cos($a) * CURVE_F, l.A.[1] - lgt * sin($a) * CURVE_F;
}
given Cairo::Image.create(Cairo::FORMAT_ARGB32, WIDTH, HEIGHT) {
given Cairo::Context.new($_) {
my line_t ($g,$l);
my (@p1,@p2);
.line_width = W_LINE;
.move_to(pt[DETACHED - 1 + cnt].[0], pt[DETACHED - 1 + cnt].[1]);
for DETACHED..^cnt -> \j {
$g = line_t.new: A=>pt[(j + cnt - 2) % cnt], B=>pt[(j + cnt - 1) % cnt];
$l = line_t.new: A=>pt[(j + cnt + 0) % cnt], B=>pt[(j + cnt + 1) % cnt];
# Calculate controls points for points pt[j-1] and pt[j].
control_points($g, $l, @p1, @p2);
.curve_to(@p1[0], @p1[1], @p2[0], @p2[1], pt[j].[0], pt[j].[1]);
}
.stroke;
};
.write_png(OUTPUT) and die # C return
}
Output: (Offsite image file)
Last updated