Reputation: 75
I am currently working on a Processing (as in the language) sketch, which is driven by Turtle logic (see https://en.wikipedia.org/wiki/Turtle_graphics). This means that I draw a line from the current coordinate to a supplied coordinate. This supplied coordinate will then become the new current coordinate. I want to approximate a circle and have written a simple piece of code using trigonometrics. The code looks as follow:
void drawCircle(int radius){
// The circle center is radius amount to the left of the current xpos
int steps = 16;
double step = TWO_PI /steps;
for(double theta = step; theta <= TWO_PI; theta += step){
float deltaX = cos((float)theta) - cos((float)(theta - step));
float deltaY = sin((float)theta) - sin((float)(theta - step));
moveXY(deltaX*radius, deltaY*radius);
}
}
The program logic is simple. It will use the variable theta
to loop through all the radians in a circle. The amount of steps will indicate how large each theta
chunk is. It will then calculate the x,y values for the specific point in the circle governed by theta. It will then deduct the x,y values of the previous cycle (hence the theta-step
) to get the amount it will have to move from this position to attain the desired x,y position. It will finally supply those delta values to a moveXY function, which draws a line from the current point to the supplied values and makes them the new current position.
The program seems to work quite well when using a limited amount of steps. However, when the step count is increased, the circles become more and more like a Fibonacci spiral. My guess is that this is due to imprecision with the float number and the sine and cosine calculations, and that this adds up with each iteration.
Have I interpreted something wrong? I am looking to port this code to Javascript eventually, so I am looking for a solution in the design. Using BigDecimal might not work, especially since it does not contain its own cosine and sine functions. I have included a few images to detail the problem. Any help is much appreciated!
Step count 16:
Step count 32:
Step count 64:
Step count 128:
Upvotes: 1
Views: 5990
Reputation: 5034
At each iteration round the loop, you are calculating the delta without regard of what the current coordinate is. So effectively, you are "dead-reckoning", which is always going to be inaccurate, since errors at each step build up.
Since you know that you want a circle, an alternative approach would be at each iteration, to first determine the actual point on the circle you want to get to, and then calculate the delta to get there - so something like the following (but I must admit I haven't tested it !):
void drawCircle(int radius){
// The circle center is radius amount to the left of the current xpos
int steps = 16;
double step = TWO_PI /steps;
float previousX = 0;
float previousY = radius;
for(double theta = step; theta <= TWO_PI; theta += step){
float thisX = radius * sin((float)theta);
float thisY = radius * cos((float)theta);
moveXY(thisX - previousX, thisY - previousY);
previousX = thisX;
previousY = thisY;
}
}
Upvotes: 0
Reputation: 35805
Float and sine/cosine should be sufficiently precise. The question is: How precise is your position on the plane? If this position is measured in pixels, then each of your floating point values is rounded to integer after each step. The loss of precision then adds up.
Upvotes: 1