Reverse the Fish-eye Distortion(I've used openCV with VC++) - c

I've made a simulation of fish eye distortion.
I want to develop a reverse program that can convert the distorted image to normal image.
I've tried to use undistortPonts() function but couldn't understand the input(dist-coefficient).
cv.UndistortPoints(distorted, undistorted, intrinsics, dist_coeffs)
My code for fish eye distortion:
#include "stdio.h"
#include <cv.h>
#include <highgui.h>
#include <math.h>
#include <iostream>
void sampleImage(const IplImage* arr, float idx0, float idx1, CvScalar& res)
{
if(idx0<0 || idx1<0 || idx0>(cvGetSize(arr).height-1) || idx1>(cvGetSize(arr).width-1))
{
res.val[0]=0;
res.val[1]=0;
res.val[2]=0;
res.val[3]=0;
return;
}
float idx0_fl=floor(idx0);
float idx0_cl=ceil(idx0);
float idx1_fl=floor(idx1);
float idx1_cl=ceil(idx1);
CvScalar s1=cvGet2D(arr,(int)idx0_fl,(int)idx1_fl);
CvScalar s2=cvGet2D(arr,(int)idx0_fl,(int)idx1_cl);
CvScalar s3=cvGet2D(arr,(int)idx0_cl,(int)idx1_cl);
CvScalar s4=cvGet2D(arr,(int)idx0_cl,(int)idx1_fl);
float x = idx0 - idx0_fl;
float y = idx1 - idx1_fl;
res.val[0]= s1.val[0]*(1-x)*(1-y) + s2.val[0]*(1-x)*y + s3.val[0]*x*y + s4.val[0]*x*(1-y);
res.val[1]= s1.val[1]*(1-x)*(1-y) + s2.val[1]*(1-x)*y + s3.val[1]*x*y + s4.val[1]*x*(1-y);
res.val[2]= s1.val[2]*(1-x)*(1-y) + s2.val[2]*(1-x)*y + s3.val[2]*x*y + s4.val[2]*x*(1-y);
res.val[3]= s1.val[3]*(1-x)*(1-y) + s2.val[3]*(1-x)*y + s3.val[3]*x*y + s4.val[3]*x*(1-y);
}
float xscale;
float yscale;
float xshift;
float yshift;
float getRadialX(float x,float y,float cx,float cy,float k)
{
x = (x*xscale+xshift);
y = (y*yscale+yshift);
float res = x+((x-cx)*k*((x-cx)*(x-cx)+(y-cy)*(y-cy)));
return res;
}
float getRadialY(float x,float y,float cx,float cy,float k)
{
x = (x*xscale+xshift);
y = (y*yscale+yshift);
float res = y+((y-cy)*k*((x-cx)*(x-cx)+(y-cy)*(y-cy)));
return res;
}
float thresh = 1;
float calc_shift(float x1,float x2,float cx,float k)
{
float x3 = x1+(x2-x1)*0.5;
float res1 = x1+((x1-cx)*k*((x1-cx)*(x1-cx)));
float res3 = x3+((x3-cx)*k*((x3-cx)*(x3-cx)));
// std::cerr<<"x1: "<<x1<<" - "<<res1<<" x3: "<<x3<<" - "<<res3<<std::endl;
if(res1>-thresh && res1 < thresh)
return x1;
if(res3<0)
{
return calc_shift(x3,x2,cx,k);
}
else
{
return calc_shift(x1,x3,cx,k);
}
}
int main(int argc, char** argv)
{
IplImage* src = cvLoadImage( "D:\\2012 Projects\\FishEye\\Debug\\images\\grid1.bmp", 1 );
IplImage* dst = cvCreateImage(cvGetSize(src),src->depth,src->nChannels);
IplImage* dst2 = cvCreateImage(cvGetSize(src),src->depth,src->nChannels);
float K=0.002;
float centerX=(float)(src->width/2);
float centerY=(float)(src->height/2);
int width = cvGetSize(src).width;
int height = cvGetSize(src).height;
xshift = calc_shift(0,centerX-1,centerX,K);
float newcenterX = width-centerX;
float xshift_2 = calc_shift(0,newcenterX-1,newcenterX,K);
yshift = calc_shift(0,centerY-1,centerY,K);
float newcenterY = height-centerY;
float yshift_2 = calc_shift(0,newcenterY-1,newcenterY,K);
// scale = (centerX-xshift)/centerX;
xscale = (width-xshift-xshift_2)/width;
yscale = (height-yshift-yshift_2)/height;
std::cerr<<xshift<<" "<<yshift<<" "<<xscale<<" "<<yscale<<std::endl;
std::cerr<<cvGetSize(src).height<<std::endl;
std::cerr<<cvGetSize(src).width<<std::endl;
for(int j=0;j<cvGetSize(dst).height;j++)
{
for(int i=0;i<cvGetSize(dst).width;i++)
{
CvScalar s;
float x = getRadialX((float)i,(float)j,centerX,centerY,K);
float y = getRadialY((float)i,(float)j,centerX,centerY,K);
sampleImage(src,y,x,s);
cvSet2D(dst,j,i,s);
}
}
#if 0
cvNamedWindow( "Source1", 1 );
cvShowImage( "Source1", dst);
cvWaitKey(0);
#endif
cvSaveImage("D:\\2012 Projects\\FishEye\\Debug\\images\\grid3.bmp",dst,0);
cvNamedWindow( "Source1", 1 );
cvShowImage( "Source1", src);
cvWaitKey(0);
cvNamedWindow( "Distortion", 2 );
cvShowImage( "Distortion", dst);
cvWaitKey(0);
#if 0
for(int j=0;j<cvGetSize(src).height;j++)
{
for(int i=0;i<cvGetSize(src).width;i++)
{
CvScalar s;
sampleImage(src,j+0.25,i+0.25,s);
cvSet2D(dst,j,i,s);
}
}
cvNamedWindow( "Source1", 1 );
cvShowImage( "Source1", src);
cvWaitKey(0);
#endif
}

Actually, my original anwser was about the undistortion algorithm for individual points. If you want to undistort a complete image, there is a much simpler technique, as explained in this other thread:
Understanding of openCV undistortion
The outline of the algorithm (which is the one used in OpenCV function undistort()) is as follow. For each pixel of the destination lens-corrected image do:
Convert the pixel coordinates (u_dst, v_dst) to normalized coordinates (x', y') using the inverse of the calibration matrix K,
Apply your lens-distortion model, to obtain the distorted normalized coordinates (x'', y''),
Convert (x'', y'') to distorted pixel coordinates (u_src, v_src) using the calibration matrix K,
Use the interpolation method of your choice to find the intensity/depth associated with the pixel coordinates (u_src, v_src) in the source image, and assign this intensity/depth to the current destination pixel (u_dst, v_dst).
Original answer:
Here is the undistortion algorithm extracted from OpenCV function undistortPoints() :
void dist2norm(const cv::Point2d &pt_dist, cv::Point2d &pt_norm) const {
pt_norm.x = (pt_dist.x-Kcx)/Kfx;
pt_norm.y = (pt_dist.y-Kcy)/Kfy;
int niters=(Dk1!=0.?5:0);
double x0=pt_norm.x, y0=pt_norm.y;
for(int i=0; i<niters; ++i) {
double x2=pt_norm.x*pt_norm.x,
y2=pt_norm.y*pt_norm.y,
xy=pt_norm.x*pt_norm.y,
r2=x2+y2;
double icdist = 1./(1 + ((Dk3*r2 + Dk2)*r2 + Dk1)*r2);
double deltaX = 2*Dp1*xy + Dp2*(r2 + 2*x2);
double deltaY = Dp1*(r2 + 2*y2) + 2*Dp2*xy;
pt_norm.x = (x0-deltaX)*icdist;
pt_norm.y = (y0-deltaY)*icdist;
}
}
If you provide the coordinates of a point in the distorted image in argument pt_dist, it will calculate the normalized coordinates of the associated point and return them in pt_norm. Then, you can obtain the coordinates of the associated point in the undistorted image as
pt_undist = K . [pt_norm.x; pt_norm.y; 1]
where K is the camera matrix.
The standard lens distortion model used by OpenCV is explained at the beginning of this page:
where the distortion coefficients are (k1,k2,p1,p2,k3, k4,k5,k6) (most often we use k4=k5=k6=0).
I don't know what is your model for FishEye distortion, but you can surely adapt the above algorithm to your case. Otherwise, you may use a non-linear optimization algorithm (e.g. Levenberg-Marquardt or any other), to recover the undistorted coordinates from the distorted one.

Related

Scaling a point from one 2d-space to another

The input to my program is a (x, y) integer coordinate inside the blue region of this circle of radius 100. I want to scale the input coordinate from the blue area to the red area, maintaining the the x and y ratios.
(link to the Desmos plot: https://www.desmos.com/calculator/61f4y2r7r4)
I know how to do this with one dimension - this answer gives a good overview on performing linear scaling. I attempted to apply this approach to the x and y axes separately. Here is some example code that I wrote to model the image.
#include <math.h>
#include <stdio.h>
#define RADIUS 100
static int find_point_on_circumference(int val) {
return sqrt(RADIUS * RADIUS - val * val);
}
static int scale(int val, int min_old, int max_old, int min_new, int max_new) {
return (max_old == min_old)
? val
: (((val - min_old) * (max_new - min_new)) / (max_old - min_old) + min_new);
}
int main() {
int x = 15;
int y = 96;
int boundary_old_x = 10;
int boundary_old_y = 10;
int boundary_new_x = 30;
int boundary_new_y = 20;
int new_x = scale(
x,
boundary_old_x,
find_point_on_circumference(y),
boundary_new_x,
find_point_on_circumference(y));
int new_y = scale(
y,
boundary_old_y,
find_point_on_circumference(x),
boundary_new_y,
find_point_on_circumference(x));
if (sqrt(new_x * new_x + new_y * new_y) <= RADIUS) {
printf("SUCCESS\n");
} else {
printf("FAIL\n");
}
return 0;
}
For this particular input, (15, 96), the result is outside of the circle. I can see that the reason for that is that my max-x bound is less than my min-x bound. I'm just not sure how I should be applying this scaling correctly in the first place.

C Mandelbrot Set Coloring

I am working on the following code in C. So far it has all been working and it is zoomed to the correct level, etc, however I am struggling with getting the colors to work as I want. Ideally I would like to end up with something like this regardless of color:
however my program as it is below currently produces something like this:
Therefore, I would appreciate any help I could get with making the colors turn out as I want them to.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define ITERMAX 100.0
#define DIVERGING 1.1
#define XMAX 500
#define YMAX 500
#define COLORINTENSITY 255
/* allow up to ITERMAX feedbacks searching for convergence
for the feedback
z0 = 0 + 0i
znew = z^2 + c
If we have not diverged to distance DIVERGING before ITERMAX feedbacks
we will assume the feedback is convergent at this value of c.
We will report divergence if |z|^2 > DIVERGING
*/
/* We will print color values for each pixel from (0, 0) to (XMAX, YMAX)
The color of pixel (cx, cy) will be set by convergent()
or by divergent()
depending on the convergence or divergence of the feedback
when c = cx + icy
*/
/* The max value of the red, green, or blue component of a color */
void convergent(); /* one color for convergence */
void divergent(); /* a different color for divergence */
void feedback(double *x, double *y, double cx, double cy);
void pixel(char red, char green, char blue);
FILE *fp;
int main()
{
fp = fopen("mandelbrot.ppm", "wb");
double x, y, cx, cy;
int iteration,squarex, squarey, pixelx, pixely;
double grow=1.0;
/* header for PPM output */
fprintf(fp, "P6\n# CREATOR: EK, BB, RPJ via the mandel program\n");
fprintf(fp, "%d %d\n%d\n",XMAX, YMAX, COLORINTENSITY);
for (pixely = 0; pixely < YMAX; pixely++) {
for (pixelx = 0; pixelx < XMAX; pixelx++) {
cx = (((float)pixelx)/((float)XMAX)-0.5)/grow*3.0-0.7;
cy = (((float)pixely)/((float)YMAX)-0.5)/grow*3.0;
x = 0.0; y = 0.0;
for (iteration=1;iteration<ITERMAX;iteration++) {
feedback(&x, &y, cx, cy);
if (x*x + y*y > 100.0) iteration = 1000;
}
if (iteration==ITERMAX) {
iteration = x*x + y*y;
pixel((char) 0, (char) 0, (char) 0);
}
else {
iteration = sqrt(x*x + y*y);
pixel((char) iteration, (char) 0, (char) iteration);
}
}
}
}
void feedback(double *x, double *y, double cx, double cy) {
/* Update x and y according to the feedback equation
xnew = x^2 - y^2 + cx
ynew = 2xy + cy
(these are the real and imaginary parts of the complex equation:
znew = z^2 + c)
*/
double xnew = (*x) * (*x) - (*y) * (*y) + cx;
double ynew = 2 * *x * *y + cy;
*x = xnew;
*y = ynew;
}
void pixel(char red, char green, char blue) {
/* put a r-g-b triple to the standard out */
fputc(red, fp);
fputc(green, fp);
fputc(blue, fp);
}
To fix the banding, you need to iterate over your tables to find your maximum value for the iteration count, then scale your other values to be relative to this max (ie. normalize the values). You may also wish to logarithmically rescale the values to adjust the slope of the color-change.
And you probably don't want to work directly in RGB space. If you define your colors in HSB space, you can set a constant hue and saturation, and vary the brightness proportionally to the normalized iteration counts.

Why is my ray-traced image entirely black?

I had to generate an image that's a black circle, black being (0, 0 , 0) and white being (1, 1, 1), but I keep getting a completely black image. Here's all my code:
#include "cast.h"
#include "collisions.h"
#include <stdio.h>
#include "math.h"
int cast_ray(struct ray r, struct sphere spheres[], int num_spheres)
{
int isFound;
struct maybe_point mp;
isFound = 0;
for (int i = 0; i < num_spheres; i++)
{
mp = sphere_intersection_point(r, spheres[i]);
if (mp.isPoint == 1)
{
isFound = 1;
}
else
{
isFound = 0;
}
}
return isFound;
}
void print_pixel(double a, double b, double c)
{
int i, j, k;
i = a * 255;
j = b * 255;
k = c * 255;
printf("%d %d %d ", i, j, k);
}
void cast_all_rays(double min_x, double max_x, double min_y, double max_y,
int width, int height, struct point eye,
struct sphere spheres[], int num_spheres)
{
double width_interval, height_interval, y, x;
int intersect;
width_interval = (max_x - min_x)/width;
height_interval = (max_y - min_y)/height;
for (y = max_y; y > min_y; y = y - height_interval)
{
for (x = min_x; x < max_x; x = x + width_interval)
{
struct ray r;
r.p = eye;
r.dir.x = x;
r.dir.y = y;
r.dir.z = 0.0;
intersect = cast_ray(r, spheres, num_spheres);
if (intersect != 0)
{
print_pixel (0, 0, 0);
}
else
{
print_pixel (1, 1, 1);
}
}
I already had functions that I know are correct which find whether or not the ray intersects with a sphere. The function that I used to find intersection points was in the function cast_ray.
sphere_intersection_point(r, spheres[i]);
The print_pixel function translates the integer values by multiplying them with the max color value, which is 255.
And the cast_all_rays function casts rays into the whole scene from our eyes (going through all the x coordinates before changing the y). If the ray intersects with a sphere, the pixel is black, thus, forming a black circle in the end.
And here are the limits for the x, y, and radius (NOTE: I'M USING THE PPM FORMAT):
Eye at <0.0, 0.0, -14.0>.
A sphere at <1.0, 1.0, 0.0> with radius 2.0.
A sphere at <0.5, 1.5, -3.0> with radius 0.5.
min_x at -10, max_x at 10, min_y of -7.5, max_y at 7.5, width=1024, and height=768.
I need to generate an image of a black circle, but I keep getting an image that's completely black. I have a feeling that the problem lies inside the cast_all_rays function, but I just can't seem to find what it is. Help is appreciated! Thanks.
And just in case something went wrong with my testing, here's my test.c file for cast_all_rays:
#include "collisions.h"
#include "data.h"
#include "cast.h"
#include <stdio.h>
void cast_all_rays_tests(void)
{
printf("P3\n");
printf("%d %d\n", 1024, 768);
printf("255\n");
double min_x, max_x, min_y, max_y;
int width, height;
struct point eye;
struct sphere spheres[2];
eye.x = 0.0;
eye.y = 0.0;
eye.z = -14.0;
spheres[0].center.x = 1.0;
spheres[0].center.y = 1.0;
spheres[0].center.z = 0.0;
spheres[0].radius = 2.0;
spheres[1].center.x = 0.5;
spheres[1].center.y = 1.5;
spheres[1].center.z = -3.0;
spheres[1].radius = 0.5;
min_x = -10;
max_x = 10;
min_y = -7.5;
max_y = 7.5;
cast_all_rays(min_x, max_x, min_y, max_y, width, height, eye, spheres, num_spheres);
}
int main()
{
cast_all_rays_tests();
return 0;
}
Not sure if this is the problem you're having, but you should only set isFound if you intersect a sphere. Don't set it if you don't intersect. Otherwise your image will be governed by only the last sphere in the list.
if (mp.isPoint == 1)
{
isFound = 1;
}
//else
//{
// isFound = 0;
//}
Since your image is entirely black, it seems like your intersection code is bung or your field of view is too narrow. If you don't have any joy with the above change, maybe you should post details on your x- and y-limits, the eye position, and the position and radius of the sphere.
One more thing I noticed is r.dir.z = 0.0. Do you subtract the eye position from this to get a direction, or is that your true ray direction? Surely you need to give a non-zero z-direction. Normally you set the x and the y based on your view plane and provide a constant z such as 1 or -1.
[edit]
To make it clearer from the comments below, I believe that you haven't correctly set up your ray direction. Instead you have simply set the direction to be the view-plane's pixel position, ignoring the eye position. The following would be more usual:
struct ray r;
r.p = eye;
r.dir.x = x - eye.x;
r.dir.y = y - eye.y;
r.dir.z = 0.0 - eye.z;

Doesn't move and rotate the triangle around a point - C Bgi graphics

Greeting,
I have this graphic homework in BGI graphic. We must use DevCPP and BGI, and matrices.
I wrote this code, and I think the transformations is good. But my triangle doesn't move and rotate around the circle, And I don't understand, why not it moves around the circle...
I don't know where and what I have to rewrite.
#include <math.h>
#include "graphics.h"
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#define PI 3.14159265
typedef float Matrix3x3[3][3];
Matrix3x3 theMatrix;
int Round( double n ){
return (int)( n + 0.5 );
}
void matrix3x3SetIdentity(Matrix3x3 m)
{
int i, j;
for(i=0; i<3; i++)
for(j=0; j<3;j++)
m[i][j]=(i==j);
}
/* Multiplies matrix, result in b matrix */
void matrix3x3PreMultiply(Matrix3x3 a, Matrix3x3 b)
{
int r, c;
Matrix3x3 tmp;
for(r=0; r<3;r++)
for(c=0; c<3;c++)
tmp[r][c]=
a[r][0]*b[0][c]+a[r][1]*b[1][c]+a[r][2]*b[2][c];
for(r=0; r<3;r++)
for(c=0; c<3; c++)
b[r][c]-tmp[r][c];
}
void translate2(int tx, int ty)
{
Matrix3x3 m;
matrix3x3SetIdentity (m);
m[0][2] = tx;
m[1][2] = ty;
matrix3x3PreMultiply(m, theMatrix);
}
void scale2 (float sx, float sy, pont2d refpt)
{
Matrix3x3 m;
matrix3x3SetIdentity(m);
m[0][0]=sx;
m[0][2]=(1-sx)*refpt.x;
m[1][1]=sy;
m[1][2]=(1-sy)*refpt.y;
matrix3x3PreMultiply(m, theMatrix);
}
void rotate2 (float a, pont2d refpt)
{
Matrix3x3 m;
matrix3x3SetIdentity(m);
a=a/PI;
m[0][0] = cosf(a);
m[0][1] = -sinf(a);
m[0][2] = refpt.x * (1-cosf(a)) + refpt.y * sinf(a);
m[1][0] = sinf (a);
m[1][1] = cosf (a);
m[1][2] = refpt.y * (1-cosf(a)) - refpt.x * sinf(a);
matrix3x3PreMultiply(m, theMatrix);
}
void transformPoints2 (int npts, pont2d *pts)
{
int k;
float tmp;
for (k = 0; k < npts; k++) {
tmp = theMatrix[0][0] * pts[k].x + theMatrix[0][1] *
pts[k].y + theMatrix[0][2];
pts[k].y = theMatrix[1][0] * pts[k].x + theMatrix[1][1] *
pts[k].y + theMatrix[1][2];
pts[k].x = tmp;
}
}
int main()
{
int gd, gm, i, page=0;
gd=VGA;gm=VGAHI;
initgraph(&gd,&gm,"");
int ap;
while(!kbhit())
{
setactivepage(page);
cleardevice();
pont2d P[3] = { 50.0, 50.0, 150.0, 50.0, 100.0, 150.0};
pont2d refPt = {200.0, 250.0};
// Drawing the Triangle
moveto( Round( P[ 0 ].x ), Round( P[ 0 ].y ) );
for( i = 1; i < 3; i++ )
lineto( Round( P[ i ].x ), Round( P[ i ].y ) );
lineto( Round( P[ 0 ].x ), Round( P[ 0 ].y ) );
// Drawing the Circle
fillellipse(200, 250, 5,5);
setcolor (BLUE);
matrix3x3SetIdentity (theMatrix);
scale2 (0.5, 0.5, refPt);
//scale2 (20, 20, refPt);
rotate2 (90.0, refPt);
translate2 (0, 150);
transformPoints2 (3, P);
setvisualpage(page);
page = 1-page;
}
getch();
closegraph();
return 0;
}
If you want to see the object "spin", then rotations should be performed about the local origin. Rotation about the global origin will cause the object to "orbit" the global origin. Thus, to spin the object:
Translate the object to global origin
Apply the rotation
Translate the object back to its original position
Look at the discussion regarding transformation order here for an illustration. Specifically, look for the section entitled "Demonstration of the importance of transformation order".
To rotate a triangle, get the three points and use the formula:
x' = x + r cos (theta)
y' = y - r sin (theta)
The above formula can be applied into a loop where there being 0 to 360. You can have a graphics simulation by putting a delay (200) milliseconds in the loop.

How to generate a set of points that are equidistant from each other and lie on a circle

I am trying to generate an array of n points that are equidistant from each other and lie on a circle in C. Basically, I need to be able to pass a function the number of points that I would like to generate and get back an array of points.
It's been a really long time since I've done C/C++, so I've had a stab at this more to see how I got on with it, but here's some code that will calculate the points for you. (It's a VS2010 console application)
// CirclePoints.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "stdio.h"
#include "math.h"
int _tmain()
{
int points = 8;
double radius = 100;
double step = ((3.14159265 * 2) / points);
double x, y, current = 0;
for (int i = 0; i < points; i++)
{
x = sin(current) * radius;
y = cos(current) * radius;
printf("point: %d x:%lf y:%lf\n", i, x, y);
current += step;
}
return 0;
}
Try something like this:
void make_circle(float *output, size_t num, float radius)
{
size_t i;
for(i = 0; i < num; i++)
{
const float angle = 2 * M_PI * i / num;
*output++ = radius * cos(angle);
*output++ = radius * sin(angle);
}
}
This is untested, there might be an off-by-one hiding in the angle step calculation but it should be close.
This assumes I understood the question correctly, of course.
UPDATE: Redid the angle computation to not be incrementing, to reduce float precision loss due to repeated addition.
Here's a solution, somewhat optimized, untested. Error can accumulate, but using double rather than float probably more than makes up for it except with extremely large values of n.
void make_circle(double *dest, size_t n, double r)
{
double x0 = cos(2*M_PI/n), y0 = sin(2*M_PI/n), x=x0, y=y0, tmp;
for (;;) {
*dest++ = r*x;
*dest++ = r*y;
if (!--n) break;
tmp = x*x0 - y*y0;
y = x*y0 + y*x0;
x = tmp;
}
}
You have to solve this in c language:
In an x-y Cartesian coordinate system, the circle with centre coordinates (a, b) and radius r is the set of all points (x, y) such that
(x - a)^2 + (y - b)^2 = r^2
Here's a javascript implementation that also takes an optional center point.
function circlePoints (radius, numPoints, centerX, centerY) {
centerX = centerX || 0;
centerY = centerY || 0;
var
step = (Math.PI * 2) / numPoints,
current = 0,
i = 0,
results = [],
x, y;
for (; i < numPoints; i += 1) {
x = centerX + Math.sin(current) * radius;
y = centerY + Math.cos(current) * radius;
results.push([x,y]);
console.log('point %d # x:%d, y:%d', i, x, y);
current += step;
}
return results;
}

Resources