Curve that touches three points
Draw a curve that touches 3 points (1 starting point, 2 medium, 3 final point)
- Do not use functions of a library, implement the curve() function yourself
- coordinates:(x,y) starting point (10,10) medium point (100,200) final point (200,10)
Go
There are, of course, an infinity of curves which can be fitted to 3 points. The most obvious solution is to fit a quadratic curve (using Lagrange interpolation) and so that's what we do here.
As we're not allowed to use library functions to draw the curve, we instead divide the x-axis of the curve between successive points into equal segments and then join the resulting points with straight lines.
The resulting 'curve' is then saved to a .png file where it can be viewed with a utility such as EOG. <lang go>package main
import "github.com/fogleman/gg"
var p = [3]gg.Point{{10, 10}, {100, 200}, {200, 10}}
func lagrange(x float64) float64 {
return (x-p[1].X)*(x-p[2].X)/(p[0].X-p[1].X)/(p[0].X-p[2].X)*p[0].Y + (x-p[0].X)*(x-p[2].X)/(p[1].X-p[0].X)/(p[1].X-p[2].X)*p[1].Y + (x-p[0].X)*(x-p[1].X)/(p[2].X-p[0].X)/(p[2].X-p[1].X)*p[2].Y
}
func getPoints(n int) []gg.Point {
pts := make([]gg.Point, 2*n+1) dx := (p[1].X - p[0].X) / float64(n) for i := 0; i < n; i++ { x := p[0].X + dx*float64(i) pts[i] = gg.Point{x, lagrange(x)} } dx = (p[2].X - p[1].X) / float64(n) for i := n; i < 2*n+1; i++ { x := p[1].X + dx*float64(i-n) pts[i] = gg.Point{x, lagrange(x)} } return pts
}
func main() {
const n = 50 // more than enough for this dc := gg.NewContext(210, 210) dc.SetRGB(1, 1, 1) // White background dc.Clear() for _, pt := range getPoints(n) { dc.LineTo(pt.X, pt.Y) } dc.SetRGB(0, 0, 0) // Black curve dc.SetLineWidth(1) dc.Stroke() dc.SavePNG("quadratic_curve.png")
}</lang>
Perl 6
Kind of bogus. There are an infinite number of curves that pass through those three points. I'll assume a quadratic curve. Lots of bits and pieces borrowed from other tasks to avoid relying on library functions.
Saved as a png for wide viewing support. Note that png coordinate systems have 0,0 in the upper left corner.
<lang perl6>use Image::PNG::Portable;
- Solve for a quadratic line that passes through those points
my (\a, \b, \c) =
rref([[10², 10, 1, 10],[100², 100, 1, 200],[200², 200, 1, 10]])[*;*-1];
- General case quadratic line equation
sub f (\x) { a*x² + b*x + c }
- Scale it up a bit for display
my $scale = 2;
my ($w, $h) = (500, 500); my $png = Image::PNG::Portable.new: :width($w), :height($h);
my ($lastx, $lasty) = 8, f(8).round; (9 .. 202).map: -> $x {
my $f = f($x).round; line($lastx, $lasty, $x, $f, $png, [0,255,127]); ($lastx, $lasty) = $x, $f;
}
- Highlight the 3 defining points
dot(|$_, $png, 2) for (10,10,[255,0,0]), (100,200,[255,0,0]), (200,10,[255,0,0]);
$png.write: 'Curve-3-points-perl6.png';
- Assorted helper routines
sub rref (@m) {
return unless @m; my ($lead, $rows, $cols) = 0, +@m, +@m[0]; for ^$rows -> $r { $lead < $cols or return @m; my $i = $r; until @m[$i;$lead] { ++$i == $rows or next; $i = $r; ++$lead == $cols and return @m; } @m[$i, $r] = @m[$r, $i] if $r != $i; my $lv = @m[$r;$lead]; @m[$r] »/=» $lv; for ^$rows -> $n { next if $n == $r; @m[$n] »-=» @m[$r] »*» (@m[$n;$lead] // 0); } ++$lead; } @m
}
sub line($x0 is copy, $y0 is copy, $x1 is copy, $y1 is copy, $png, @rgb) {
my $steep = abs($y1 - $y0) > abs($x1 - $x0); ($x0,$y0,$x1,$y1) »*=» $scale; 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; next if $y < 0; for $x0 .. $x1 -> $x { next if $x < 0; if $steep { $png.set($y, $x, |@rgb); } else { $png.set($x, $y, |@rgb); } $error += $Δerror; if $error >= 0.5 { $y += $y-step; $error -= 1.0; } }
}
sub dot ($X is copy, $Y is copy, @rgb, $png, $radius = 3) {
($X, $Y) »*=» $scale; for ($X X+ -$radius .. $radius) X ($Y X+ -$radius .. $radius) -> ($x, $y) { $png.set($x, $y, |@rgb) if ( $X - $x + ($Y - $y) * i ).abs <= $radius; }
}</lang> See Curve-3-points-perl6.png (offsite .png image)
zkl
<lang zkl></lang> <lang zkl></lang>
- Output: