Bitmap/Bézier curves/Cubic: Difference between revisions
Content added Content deleted
Thundergnat (talk | contribs) (Rename Perl 6 -> Raku, alphabetize, minor clean-up) |
|||
Line 3: | Line 3: | ||
Using the data storage type defined [[Basic_bitmap_storage|on this page]] for raster images, and the <tt>draw_line</tt> function defined in [[Bresenham's_line_algorithm|this other one]], draw a '''cubic bezier curve''' |
Using the data storage type defined [[Basic_bitmap_storage|on this page]] for raster images, and the <tt>draw_line</tt> function defined in [[Bresenham's_line_algorithm|this other one]], draw a '''cubic bezier curve''' |
||
([[wp:Bezier_curves#Cubic_B.C3.A9zier_curves|definition on Wikipedia]]). |
([[wp:Bezier_curves#Cubic_B.C3.A9zier_curves|definition on Wikipedia]]). |
||
=={{header|Ada}}== |
=={{header|Ada}}== |
||
Line 55: | Line 54: | ||
H |
H |
||
</pre> |
</pre> |
||
=={{header|ALGOL 68}}== |
=={{header|ALGOL 68}}== |
||
{{trans|Ada}} |
{{trans|Ada}} |
||
Line 241: | Line 241: | ||
#undef plot |
#undef plot |
||
#undef line</lang> |
#undef line</lang> |
||
=={{header|D}}== |
=={{header|D}}== |
||
Line 333: | Line 332: | ||
points |> List.fold renderPoint points.Head |
points |> List.fold renderPoint points.Head |
||
</lang> |
</lang> |
||
=={{header|Factor}}== |
|||
The points should probably be in a sequence... |
|||
<lang factor>USING: arrays kernel locals math math.functions |
|||
rosettacode.raster.storage sequences ; |
|||
IN: rosettacode.raster.line |
|||
! this gives a function |
|||
:: (cubic-bezier) ( P0 P1 P2 P3 -- bezier ) |
|||
[ :> x |
|||
1 x - 3 ^ P0 n*v |
|||
1 x - sq 3 * x * P1 n*v |
|||
1 x - 3 * x sq * P2 n*v |
|||
x 3 ^ P3 n*v |
|||
v+ v+ v+ ] ; inline |
|||
! gives an interval of x from 0 to 1 to map the bezier function |
|||
: t-interval ( x -- interval ) |
|||
[ iota ] keep 1 - [ / ] curry map ; |
|||
! turns a list of points into the list of lines between them |
|||
: points-to-lines ( seq -- seq ) |
|||
dup rest [ 2array ] 2map ; |
|||
: draw-lines ( {R,G,B} points image -- ) |
|||
[ [ first2 ] dip draw-line ] curry with each ; |
|||
:: bezier-lines ( {R,G,B} P0 P1 P2 P3 image -- ) |
|||
! 100 is an arbitrary value.. could be given as a parameter.. |
|||
100 t-interval P0 P1 P2 P3 (cubic-bezier) map |
|||
points-to-lines |
|||
{R,G,B} swap image draw-lines ;</lang> |
|||
=={{header|FBSL}}== |
=={{header|FBSL}}== |
||
Line 396: | Line 423: | ||
END SUB</lang> |
END SUB</lang> |
||
'''Output:''' [[File:FBSLBezierCube.PNG]] |
'''Output:''' [[File:FBSLBezierCube.PNG]] |
||
=={{header|Factor}}== |
|||
The points should probably be in a sequence... |
|||
<lang factor>USING: arrays kernel locals math math.functions |
|||
rosettacode.raster.storage sequences ; |
|||
IN: rosettacode.raster.line |
|||
! this gives a function |
|||
:: (cubic-bezier) ( P0 P1 P2 P3 -- bezier ) |
|||
[ :> x |
|||
1 x - 3 ^ P0 n*v |
|||
1 x - sq 3 * x * P1 n*v |
|||
1 x - 3 * x sq * P2 n*v |
|||
x 3 ^ P3 n*v |
|||
v+ v+ v+ ] ; inline |
|||
! gives an interval of x from 0 to 1 to map the bezier function |
|||
: t-interval ( x -- interval ) |
|||
[ iota ] keep 1 - [ / ] curry map ; |
|||
! turns a list of points into the list of lines between them |
|||
: points-to-lines ( seq -- seq ) |
|||
dup rest [ 2array ] 2map ; |
|||
: draw-lines ( {R,G,B} points image -- ) |
|||
[ [ first2 ] dip draw-line ] curry with each ; |
|||
:: bezier-lines ( {R,G,B} P0 P1 P2 P3 image -- ) |
|||
! 100 is an arbitrary value.. could be given as a parameter.. |
|||
100 t-interval P0 P1 P2 P3 (cubic-bezier) map |
|||
points-to-lines |
|||
{R,G,B} swap image draw-lines ;</lang> |
|||
=={{header|Fortran}}== |
=={{header|Fortran}}== |
||
Line 457: | Line 456: | ||
end subroutine cubic_bezier</lang> |
end subroutine cubic_bezier</lang> |
||
=={{header|FreeBASIC}}== |
=={{header|FreeBASIC}}== |
||
{{trans|BBC BASIC}} |
{{trans|BBC BASIC}} |
||
Line 860: | Line 860: | ||
by_pair pts (fun p0 p1 -> line ~p0 ~p1); |
by_pair pts (fun p0 p1 -> line ~p0 ~p1); |
||
;;</lang> |
;;</lang> |
||
=={{header|Perl 6}}== |
|||
{{works with|Rakudo|2017.09}} |
|||
Uses pieces from [[Bitmap#Perl_6| Bitmap]], and [[Bitmap/Bresenham's_line_algorithm#Perl_6| Bresenham's line algorithm]] tasks. They are included here to make a complete, runnable program. |
|||
<lang perl6>class Pixel { has UInt ($.R, $.G, $.B) } |
|||
class Bitmap { |
|||
has UInt ($.width, $.height); |
|||
has Pixel @!data; |
|||
method fill(Pixel $p) { |
|||
@!data = $p.clone xx ($!width*$!height) |
|||
} |
|||
method pixel( |
|||
$i where ^$!width, |
|||
$j where ^$!height |
|||
--> Pixel |
|||
) is rw { @!data[$i + $j * $!width] } |
|||
method set-pixel ($i, $j, Pixel $p) { |
|||
self.pixel($i, $j) = $p.clone; |
|||
} |
|||
method get-pixel ($i, $j) returns Pixel { |
|||
self.pixel($i, $j); |
|||
} |
|||
method line(($x0 is copy, $y0 is copy), ($x1 is copy, $y1 is copy), $pix) { |
|||
my $steep = abs($y1 - $y0) > abs($x1 - $x0); |
|||
if $steep { |
|||
($x0, $y0) = ($y0, $x0); |
|||
($x1, $y1) = ($y1, $x1); |
|||
} |
|||
if $x0 > $x1 { |
|||
($x0, $x1) = ($x1, $x0); |
|||
($y0, $y1) = ($y1, $y0); |
|||
} |
|||
my $Δx = $x1 - $x0; |
|||
my $Δy = abs($y1 - $y0); |
|||
my $error = 0; |
|||
my $Δerror = $Δy / $Δx; |
|||
my $y-step = $y0 < $y1 ?? 1 !! -1; |
|||
my $y = $y0; |
|||
for $x0 .. $x1 -> $x { |
|||
if $steep { |
|||
self.set-pixel($y, $x, $pix); |
|||
} else { |
|||
self.set-pixel($x, $y, $pix); |
|||
} |
|||
$error += $Δerror; |
|||
if $error >= 0.5 { |
|||
$y += $y-step; |
|||
$error -= 1.0; |
|||
} |
|||
} |
|||
} |
|||
method dot (($px, $py), $pix, $radius = 2) { |
|||
for $px - $radius .. $px + $radius -> $x { |
|||
for $py - $radius .. $py + $radius -> $y { |
|||
self.set-pixel($x, $y, $pix) if ( $px - $x + ($py - $y) * i ).abs <= $radius; |
|||
} |
|||
} |
|||
} |
|||
method cubic ( ($x1, $y1), ($x2, $y2), ($x3, $y3), ($x4, $y4), $pix, $segments = 30 ) { |
|||
my @line-segments = map -> $t { |
|||
my \a = (1-$t)³; |
|||
my \b = $t * (1-$t)² * 3; |
|||
my \c = $t² * (1-$t) * 3; |
|||
my \d = $t³; |
|||
(a*$x1 + b*$x2 + c*$x3 + d*$x4).round(1),(a*$y1 + b*$y2 + c*$y3 + d*$y4).round(1) |
|||
}, (0, 1/$segments, 2/$segments ... 1); |
|||
for @line-segments.rotor(2=>-1) -> ($p1, $p2) { self.line( $p1, $p2, $pix) }; |
|||
} |
|||
method data { @!data } |
|||
} |
|||
role PPM { |
|||
method P6 returns Blob { |
|||
"P6\n{self.width} {self.height}\n255\n".encode('ascii') |
|||
~ Blob.new: flat map { .R, .G, .B }, self.data |
|||
} |
|||
} |
|||
sub color( $r, $g, $b) { Pixel.new(R => $r, G => $g, B => $b) } |
|||
my Bitmap $b = Bitmap.new( width => 600, height => 400) but PPM; |
|||
$b.fill( color(2,2,2) ); |
|||
my @points = (85,390), (5,5), (580,370), (270,10); |
|||
my %seen; |
|||
my $c = 0; |
|||
for @points.permutations -> @this { |
|||
%seen{@this.reverse.join.Str}++; |
|||
next if %seen{@this.join.Str}; |
|||
$b.cubic( |@this, color(255-$c,127,$c+=22) ); |
|||
} |
|||
@points.map: { $b.dot( $_, color(255,0,0), 3 )} |
|||
$*OUT.write: $b.P6;</lang> |
|||
See [https://github.com/thundergnat/rc/blob/master/img/Bezier-cubic-perl6.png example image here], (converted to a .png as .ppm format is not widely supported). |
|||
=={{header|Phix}}== |
=={{header|Phix}}== |
||
Line 1,325: | Line 1,219: | ||
bm |
bm |
||
</lang> |
</lang> |
||
=={{header|Raku}}== |
|||
(formerly Perl 6) |
|||
{{works with|Rakudo|2017.09}} |
|||
Uses pieces from [[Bitmap#Perl_6| Bitmap]], and [[Bitmap/Bresenham's_line_algorithm#Perl_6| Bresenham's line algorithm]] tasks. They are included here to make a complete, runnable program. |
|||
<lang perl6>class Pixel { has UInt ($.R, $.G, $.B) } |
|||
class Bitmap { |
|||
has UInt ($.width, $.height); |
|||
has Pixel @!data; |
|||
method fill(Pixel $p) { |
|||
@!data = $p.clone xx ($!width*$!height) |
|||
} |
|||
method pixel( |
|||
$i where ^$!width, |
|||
$j where ^$!height |
|||
--> Pixel |
|||
) is rw { @!data[$i + $j * $!width] } |
|||
method set-pixel ($i, $j, Pixel $p) { |
|||
self.pixel($i, $j) = $p.clone; |
|||
} |
|||
method get-pixel ($i, $j) returns Pixel { |
|||
self.pixel($i, $j); |
|||
} |
|||
method line(($x0 is copy, $y0 is copy), ($x1 is copy, $y1 is copy), $pix) { |
|||
my $steep = abs($y1 - $y0) > abs($x1 - $x0); |
|||
if $steep { |
|||
($x0, $y0) = ($y0, $x0); |
|||
($x1, $y1) = ($y1, $x1); |
|||
} |
|||
if $x0 > $x1 { |
|||
($x0, $x1) = ($x1, $x0); |
|||
($y0, $y1) = ($y1, $y0); |
|||
} |
|||
my $Δx = $x1 - $x0; |
|||
my $Δy = abs($y1 - $y0); |
|||
my $error = 0; |
|||
my $Δerror = $Δy / $Δx; |
|||
my $y-step = $y0 < $y1 ?? 1 !! -1; |
|||
my $y = $y0; |
|||
for $x0 .. $x1 -> $x { |
|||
if $steep { |
|||
self.set-pixel($y, $x, $pix); |
|||
} else { |
|||
self.set-pixel($x, $y, $pix); |
|||
} |
|||
$error += $Δerror; |
|||
if $error >= 0.5 { |
|||
$y += $y-step; |
|||
$error -= 1.0; |
|||
} |
|||
} |
|||
} |
|||
method dot (($px, $py), $pix, $radius = 2) { |
|||
for $px - $radius .. $px + $radius -> $x { |
|||
for $py - $radius .. $py + $radius -> $y { |
|||
self.set-pixel($x, $y, $pix) if ( $px - $x + ($py - $y) * i ).abs <= $radius; |
|||
} |
|||
} |
|||
} |
|||
method cubic ( ($x1, $y1), ($x2, $y2), ($x3, $y3), ($x4, $y4), $pix, $segments = 30 ) { |
|||
my @line-segments = map -> $t { |
|||
my \a = (1-$t)³; |
|||
my \b = $t * (1-$t)² * 3; |
|||
my \c = $t² * (1-$t) * 3; |
|||
my \d = $t³; |
|||
(a*$x1 + b*$x2 + c*$x3 + d*$x4).round(1),(a*$y1 + b*$y2 + c*$y3 + d*$y4).round(1) |
|||
}, (0, 1/$segments, 2/$segments ... 1); |
|||
for @line-segments.rotor(2=>-1) -> ($p1, $p2) { self.line( $p1, $p2, $pix) }; |
|||
} |
|||
method data { @!data } |
|||
} |
|||
role PPM { |
|||
method P6 returns Blob { |
|||
"P6\n{self.width} {self.height}\n255\n".encode('ascii') |
|||
~ Blob.new: flat map { .R, .G, .B }, self.data |
|||
} |
|||
} |
|||
sub color( $r, $g, $b) { Pixel.new(R => $r, G => $g, B => $b) } |
|||
my Bitmap $b = Bitmap.new( width => 600, height => 400) but PPM; |
|||
$b.fill( color(2,2,2) ); |
|||
my @points = (85,390), (5,5), (580,370), (270,10); |
|||
my %seen; |
|||
my $c = 0; |
|||
for @points.permutations -> @this { |
|||
%seen{@this.reverse.join.Str}++; |
|||
next if %seen{@this.join.Str}; |
|||
$b.cubic( |@this, color(255-$c,127,$c+=22) ); |
|||
} |
|||
@points.map: { $b.dot( $_, color(255,0,0), 3 )} |
|||
$*OUT.write: $b.P6;</lang> |
|||
See [https://github.com/thundergnat/rc/blob/master/img/Bezier-cubic-perl6.png example image here], (converted to a .png as .ppm format is not widely supported). |
|||
=={{header|Ruby}}== |
=={{header|Ruby}}== |