Curve that touches three points: Difference between revisions

From Rosetta Code
Content added Content deleted
m (→‎{{header|Perl 6}}: added zkl header)
(→‎{{header|zkl}}: added code)
Line 157: Line 157:


=={{header|zkl}}==
=={{header|zkl}}==
{{trans|Go}}
<lang zkl></lang>
Uses Image Magick and
<lang zkl></lang>
the PPM class from http://rosettacode.org/wiki/Bitmap/Bresenham%27s_line_algorithm#zkl
<lang zkl>const X=0, Y=1; // p.X == p[X]
var p=L(L(10.0, 10.0), L(100.0, 200.0), L(200.0, 10.0)); // (x,y)
fcn lagrange(x){ // float-->float
(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]
}
fcn getPoints(n){ // int-->( (x,y) ..)
pts:=List.createLong(2*n+1);
dx,pt,cnt := (p[1][X] - p[0][X])/n.toFloat(), p[0][X], n;
do(2){
foreach i in (cnt){
x:=pt + dx*i;
pts.append(L(x,lagrange(x)));
}
dx,pt,cnt = (p[2][X] - p[1][X])/n, p[1][X], n+1;
}
pts
}

fcn main{
var [const] n=50; // more than enough for this
img,color := PPM(210,210,0xffffff), 0; // white background, black curve
foreach x,y in (p){ img.cross(x.toInt(),y.toInt(), 0xff0000) } // mark 3 pts
a,b := p[0][X].toInt(), p[0][Y].toInt(); // curve starting point
foreach x,y in (getPoints(n)){
x,y = x.toInt(),y.toInt();
img.line(a,b, x,y, color); // can only deal with ints
a,b = x,y;
}
img.writeJPGFile("quadraticCurve.zkl.jpg");
}();</lang>
{{out}}
{{out}}
Image at [http://www.zenkinetic.com/Images/RosettaCode/quadraticCurve.zkl.jpg sunflower quadratic curve]
<pre>
</pre>

Revision as of 19:11, 3 December 2018

Curve that touches three points is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

Draw a curve that touches 3 points (1 starting point, 2 medium, 3 final point)

  1.  Do not use functions of a library, implement the curve() function yourself
  2.  coordinates:(x,y) starting point (10,10) medium point (100,200) final point (200,10)



Go

Library: Go Graphics


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

Works with: Rakudo version 2018.10

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;

  1. 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];
  1. General case quadratic line equation

sub f (\x) { a*x² + b*x + c }

  1. 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;

}

  1. 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';

  1. 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

Translation of: Go

Uses Image Magick and the PPM class from http://rosettacode.org/wiki/Bitmap/Bresenham%27s_line_algorithm#zkl <lang zkl>const X=0, Y=1; // p.X == p[X] var p=L(L(10.0, 10.0), L(100.0, 200.0), L(200.0, 10.0)); // (x,y)

fcn lagrange(x){ // float-->float

  (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]

}

fcn getPoints(n){ // int-->( (x,y) ..)

 pts:=List.createLong(2*n+1);
 dx,pt,cnt := (p[1][X] - p[0][X])/n.toFloat(), p[0][X], n;
 do(2){
    foreach i in (cnt){

x:=pt + dx*i; pts.append(L(x,lagrange(x)));

    }
    dx,pt,cnt = (p[2][X] - p[1][X])/n, p[1][X], n+1;
 }
 pts

}

fcn main{

  var [const] n=50; // more than enough for this
  img,color := PPM(210,210,0xffffff), 0;     // white background, black curve
  foreach x,y in (p){ img.cross(x.toInt(),y.toInt(), 0xff0000) } // mark 3 pts

  a,b := p[0][X].toInt(), p[0][Y].toInt(); // curve starting point
  foreach x,y in (getPoints(n)){
     x,y = x.toInt(),y.toInt();
     img.line(a,b, x,y, color);	 // can only deal with ints
     a,b = x,y;
  }
  img.writeJPGFile("quadraticCurve.zkl.jpg");

}();</lang>

Output:

Image at sunflower quadratic curve