Easy:
1. Put the C64 into your favorite graphics mode.
2. Get rid of the integers - you won't need them.
3. Find a marker. Preferably a dry erase one (color of your choice).
4. With your marker draw a circle on your screen.
5. Fill it in with marker.
Done!
Non-filled circle: http://en.wikipedia.org/wiki/Midpoint_circle_algorithm
On Thu, 9 Apr 2009, Harry Potter wrote:
> How do I draw a filled, rotated or non-rotated circle on a graphics
> screen, especially with integers?
>
My usual method of doing an OPEN circle has been something like this
algorithm (which is in more or less qbasic syntax as that's what I know):
pset (cx+arx, cy), c
for deg=0 to 360
rdn=(3.141593/180)*deg
x=cx+(cos(rdn)*arx*rad)
y=cy+(sin(rdn)*ary*rad)
line -(x, y), c
next deg
where (cx,cy) is the center, rad is the radius, arx:ary is the pixel
aspect ratio (I think you want 1.6:1 or 0.8:1 for a C64 depending on the
resolution), and c is the color.
Since the C64's BASIC don't have "pset", "line" or "paint" as GW-BASIC
did, you'd need to supply those yourself.
-uso.
>How do I draw a filled, rotated or non-rotated circle on a graphics screen,
>especially with integers?
Here's how I do it on the C64 in my Windows Xp Aztec C cross-compiler
library: (see below)
Why do you ask? If you actually want to do something about this why not
download the compiler with all the graphics libraries and demos and so forth
and actually work with it:
http://www.aztecmuseum.ca/compilers.htm#commodore
"This is a complete Aztec C build environment for Windows XP (MS-DOS) which
will enable you to produce efficient 6502 machine-language programs which
will, when properly built, load and run from BASIC on a Commodore 64 or in
the Vice C64 emulator, and which will exit cleanly to BASIC when done."
"An additional link library (B64NAT.LIB) is provided which supports, among
other things, the use of graphics and bit-mapped graphics images, sound
routines, and other useful routines for native mode C64 Aztec C programs."
"Several sample programs and C64 projects are provided, each with its own
MAKEFILE and each can be reviewed for information on how to write your own
C64 programs."
Bill
x--- snip ---x
/* Copyright (C) Bill Buckels 2008 */
/* standard implementation of bresenham's */
/* I am not using an external aspect ratio */
/* ellipsoids look pretty bad so no point */
circle(cx,cy,r,color,mode)
int cx,cy,r,color,mode;
{
int x,y,a,b,c,d,f,m,n;
x=r; y=0; b=1; f=0;
if (mode != 2)mode = 1; /* 320 x 200 mode 1 */
a=(-2)*x+1;
m=1;
n=mode; /* aspect of 2 for 160 x 200 mode 2 */
point:
c=x;
d=y;
plot(c*m+cx,y*n+cy,color,mode);
plot(d*m+cx,x*n+cy,color,mode);
plot(-d*m+cx,x*n+cy,color,mode);
plot(-c*m+cx,y*n+cy,color,mode);
plot(-c*m+cx,-y*n+cy,color,mode);
plot(-d*m+cx,-x*n+cy,color,mode);
plot(d*m+cx,-x*n+cy,color,mode);
plot(c*m+cx,-y*n+cy,color,mode);
if(b>= -a)
goto fin;
y+=1; f+=b;
b+=2;
if(f>r)
{
f+=a;
a+=2;
x-=1;
}
goto point;
fin:
;
}
x--- snip ---x
/* Copyright (C) Bill Buckels 2008 */
disk(cx,cy,r,color,mode)
int cx,cy,r,color,mode;
{
int xc, yc; /* xcenter, ycenter */
int x,y; /*current point around 1/8 disk*/
int rem; /*remainder value*/
cx -= r; /* draw from top left */
cy -= r;
xc = r; /* offset center by aspect */
yc = r;
y=0;
x=r; /*initial point is on axis*/
rem=r/2; /*remainder=1/2 for roundoff*/
while (x>=y) /*loop for 1/8 of a circle*/
{
/*fill in top half of circle*/
drawline(xc-x+cx,yc+y+cy,xc+x+cx,yc+y+cy,color, mode);
/*and bottom half*/
drawline(xc-x+cx,yc-y+cy,xc+x+cx,yc-y+cy,color,mode);
y += 1; /*always increment y*/
rem=rem-y; /*subtract from remainder*/
if (rem<0) /*if it's time,*/
{
x=x-1; /*then decrement x*/
rem=rem+x; /*and bump remainder back up*/
}
}
while(x>0) /*loop for the top 1/8th of circle*/
{
x=x-1; /*now you always move in x direction*/
rem=rem-x; /*and subtract x from the remainder*/
if (rem<0) /*when remainder underflows,*/
{
/*fill in the top side,*/
drawline(xc-x+cx, yc+y+cy,xc+x+cx,yc+y+cy,color,mode);
/*and the bottom*/
drawline(xc-x+cx,yc-y+cy,xc+x+cx,yc-y+cy,color,mode);
y=y+1;
rem=rem+y; /*bump the remainder back up*/
}
}
return;
}
x--- snip ---x
/* Copyright (C) Bill Buckels 2008 */
#include <poke.h>
/* the following function plots a pixel on the C64 using the currently
selected palette. It handles both of the standard videomodes;
HIRES and Multicolor. Since HIRES mode supports 2 colors, valid
color values are either 0 or 1.Multicolor mode (like the CGA on the
IBM-PC) has four colors; 0-3. */
plot(x,y,color,mode)
int x, y, color, mode;
{
/* mode 2 is MultiColor mode 160 pixels x 200 rasters x 1 bit */
/* mode 1 is HIRES mode 320 pixels x 200 rasters x 2 bits */
int c, ch, offset, row, column, temp;
/*
bmp = (char *)8192;
vram = (char *)1024;
cram = (char *)55296;
*/
/* we need to locate the 8 bit x 8 bit block that x and y refer to */
/* if in multi-color mode this is expressed in a 4 pixel x 8 bit block
*/
/* I am simplifying this by using an inclusive or */
/* get rid of whatever color was already there */
/* unless we are plotting a white pixel */
if (mode == 2) {
/* if we are in multicolor mode */
/* the matrix will be 4 pixels wide x 8 rasters deep */
row = y/8;
column = x/4;
y = y - (row * 8);
offset = (row * 320) + (column * 8) + y;
/* determine the pixel location in the byte */
x = 6 - ((x%4) * 2);
c = (3<<x);
if (color == 3) {
/* set the character at the raster */
temp = bmp[offset];
bmp[offset] = temp | c;
}
else {
/* get the character at the raster */
/* get rid of whatever color was already there */
temp = bmp[offset];
ch = (temp ^0xff) | c; /* reverse and unset pixel */
/* revert and set */
if (color == 1 || color == 2) bmp[offset] = (ch^0xff) | (color << x);
else bmp[offset] = (ch^0xff); /* revert */
}
}
else {
/* if we are in hires mode the matrix will be 8 pixels wide x 8 rasters
deep */
row = y/8;
column = x/8;
y = y - (row * 8);
offset = (row * 320) + (column * 8) + y;
/* determine the pixel location in the byte */
x = 7 - (x - (column * 8));
c = (1 << x);
/* set the character at the raster */
if (color == 1) {
temp = bmp[offset];
bmp[offset] = (temp | c);
}
else {
/* get the character at the raster */
/* get rid of whatever color was already there */
temp = bmp[offset];
ch = (temp ^0xff) | c; /* reverse and unset pixel */
bmp[offset] = (ch^0xff); /* revert */
}
}
}
x--- snip ---x
/* Copyright (C) Bill Buckels 2008 */
/* standard implementation of bresenham's */
drawline(x1,y1,x2,y2,color, mode)
int x1, y1, x2, y2, color, mode;
{
register int dx,dy,ix,iy;
int e,ei,ed,i;
dx=x2-x1;
dy=y2-y1;
ix=1;
if(dx<0)
{ ix=-1;
dx=-dx;
}
iy=1;
if(dy<0)
{ iy=-1;
dy=-dy;
}
if(dy>dx)
goto ylin;
ei=2*dy;
ed=ei-2*dx;
e=-dx+ei;
for(i=0;i<=dx;++i)
{
plot(x1,y1,color,mode);
x1=x1+ix;
if(e<0)
e=e+ei;
else{
y1=y1+iy;e=e+ed;
}
}
goto fin;
ylin:
ei=2*dx;
ed=ei-2*dy;
e=-dy+ei;
for(i=0;i<=dy;++i)
{
plot(x1,y1,color,mode);
y1=y1+iy;
if(e<0)
e=e+ei;
else{ x1=x1+ix;e=e+ed;}
}
fin:
;
}
Just wondering what you mean by "rotated"? Isn't a rotated circle
still the same circle? Could you steal some routines from Simon's
Basic?
> Just wondering what you mean by "rotated"? Isn't a rotated circle
> still the same circle? Could you steal some routines from Simon's
> Basic?
Maybe rotated about x or y axis... then you get an ellipse when projected
onto the Z axis.
--
| Mark McDougall | "Electrical Engineers do it
| <http://members.iinet.net.au/~msmcdoug> | with less resistance!"
Or maybe he could steal some routines from Commodore BASIC 7.0
The CIRCLE command can draw a circle, a rotated circle (ellipse), or even an
octagon, pentagon, diamond, or triangle. Then a routine could be borrowed
from the BOX command to fill the result.
Or maybe Harry could rip some code from a CAD program.
--
Best regards,
Sam Gillett
I saw Harry making crop circles,
But they turned out square!!
> Could you steal some routines from Simon's Basic?
Simons' Basic uses sine and cosine algorithms and therefore is *way* longer
code. And also way slower (as you all know).
--
Arndt
GoDot C64 Image Processing
www.godot64.de
> How do I draw a filled, rotated or non-rotated circle on a graphics
> screen, especially with integers?
I had an integer algorithm years ago that I used to make a primitive
graphics library for Turbo Pascal on the TRS-80. I needed an integer
algorithm because I wrote it in Z80 assembler.
Try this...
<http://en.wikipedia.org/wiki/Midpoint_circle_algorithm>
You could probably fill it easily after you've drawn it using edge-detection?
Regards,
Don't know if you'll believe me but when I had a think about it, that
was the way I was thinking I'd go about it myself. I wonder do any of
the few filled polygon routines I've seen on the C64 (Space Rogue for
example) use something similar. The sums get fiendish for more than
one curve, i.e. 3 or 4 lines, I'd say.
@godot
Depends on how much speed you need I guess. On Simon's Basic, drawing
concentric circles would be much quicker anyway as the fill routine is
glacial.
> @godot
> On Simon's Basic, drawing concentric circles would be
> much quicker anyway as the fill routine is glacial.
Really. As for the speed, I had a Bresenham CIRCLE in TSB (which is a
Simons' Basic rewrite), and you can compare the original speed to the
Bresenham one on this page (in the images, the text is only German):
http://www.c64-wiki.de/index.php/GRAPHICS_%28TSB%29 (fast)
http://www.c64-wiki.de/index.php/ANGL (slow)
The latter example shows for what sine and cosine are useful, anyway (see
the clock hands).
SB's fill is able of arbitrarily irregular shapes, though, as can be seen
here:
http://www.c64-wiki.de/index.php/BLOCK (just wait until the animation comes
to the fill part)
Actually not really. I just tried it and it's waaaay faster by PAINT
routine. Oops. Is there a reason why SB fills vertically? I would have
thought horizontal would be faster?
I can use the Pythagorean Theorem (spelling correct?) to calculate the
left and right end-points of a filled ellipse, but what about a
rotated filled ellipse? Also, what about using integers to calculate
the end-points? I need a SQR function to handle integers, but I don't
even know how to calculate it myself. I know of a way to do the same
with SIN and COS using Polynomial Approximations by multiplying the
numerators with the radius of the circle before factoring in the
denominators.
>Is there a reason why SB fills vertically? I would have thought horizontal
>would be faster?
I'd think it's easier (and thus faster) to control the y-coordinate because
it doesn't go beyond 255. Btw SB works with tables to store the test
coordinates, TSB uses the stack (it's completely different).
>I can use the Pythagorean Theorem (spelling correct?) to calculate the left
>and right end-points of a filled ellipse, but what about a rotated filled
>ellipse?
Solve your normal scenario first. By the way, I posted a disk algorithm
earlier. Why can't you apply an aspect scale and use this? Also for rotated
elements you may want to draw them first (in a buffer if necessary) then
rotate them as a bitmap around some axis point. Honestly, I can't remember
needing to create rotated ellipses to much degree and would wonder why you
are putting so much effort into this when you have so many other important
projects. I would suggest a general rotation algorithm if writing a paint
program, and to predraw and optimize your elements if writing some game and
stay away from vector graphics if you can.
Anyway, you could also apply something similar to the hands-on-a-clock
algorithm shown below with a suitable resolution.
This is taken from my Commodore 64 Version of "What Time Is It?" which comes
with the Aztec C64 cross-compiler for MS-DOS or Windows:
http://www.aztecmuseum.ca/compilers.htm#commodore
The Project itself is discussed in more detail here:
Variations by me with source code can be found all over the internet for
Apple II and MS-DOS clock programs. Best to download at least one of them to
see how chording of circles might be accomplished in this manner.
Here's the core functions and you will note that the economy of scale is
applied to reduce this to a table of only 92 integers and a calculation
function :
/* a memory saving table for sine cosine */
/* we don't want to haul around an entire math library for */
/* the sake of 184 bytes of data and a parser function */
/* the divisor is 32767 (a signed int) */
int sine_cosine[46][2]={
0, 32767,/* 0 degree offset*/
571, 32762,/* 1 degree offset*/
1143, 32747,/* 2 degree offset*/
1714, 32722,/* 3 degree offset*/
2285, 32687,/* 4 degree offset*/
2855, 32642,/* 5 degree offset*/
3425, 32587,/* 6 degree offset*/
3993, 32522,/* 7 degree offset*/
4560, 32448,/* 8 degree offset*/
5125, 32363,/* 9 degree offset*/
5689, 32269,/* 10 degree offset*/
6252, 32164,/* 11 degree offset*/
6812, 32050,/* 12 degree offset*/
7370, 31927,/* 13 degree offset*/
7927, 31793,/* 14 degree offset*/
8480, 31650,/* 15 degree offset*/
9031, 31497,/* 16 degree offset*/
9580, 31335,/* 17 degree offset*/
10125,31163,/* 18 degree offset*/
10667,30981,/* 19 degree offset*/
11206,30790,/* 20 degree offset*/
11742,30590,/* 21 degree offset*/
12274,30381,/* 22 degree offset*/
12803,30162,/* 23 degree offset*/
13327,29934,/* 24 degree offset*/
13847,29696,/* 25 degree offset*/
14364,29450,/* 26 degree offset*/
14875,29195,/* 27 degree offset*/
15383,28931,/* 28 degree offset*/
15885,28658,/* 29 degree offset*/
16383,28377,/* 30 degree offset*/
16876,28086,/* 31 degree offset*/
17363,27787,/* 32 degree offset*/
17846,27480,/* 33 degree offset*/
18323,27165,/* 34 degree offset*/
18794,26841,/* 35 degree offset*/
19259,26509,/* 36 degree offset*/
19719,26168,/* 37 degree offset*/
20173,25820,/* 38 degree offset*/
20620,25464,/* 39 degree offset*/
21062,25100,/* 40 degree offset*/
21497,24729,/* 41 degree offset*/
21925,24350,/* 42 degree offset*/
22347,23964,/* 43 degree offset*/
22761,23570,/* 44 degree offset*/
23169,23169};/*45 degree offset*/
circlepoints(x,y,baselength,fdegrees)
int *x, *y;
int baselength;
float fdegrees;
{
float xtemp;
float ytemp;
int degrees = (int )fdegrees;
float aspect_h, aspect_v;
aspect_h = (float)1.16;
aspect_v = (float)1;
/* starting at 12 O'clock */
/* use a switch for the break */
switch(degrees)
{
default:
/* within range */
if(degrees<0 || degrees >359)degrees=0;
/* 0-45 sin */
if(degrees < 45)
{
xtemp = (float) sine_cosine[degrees][0];
ytemp = (float) sine_cosine[degrees][1];
ytemp = ytemp * -1;
break;
}
/* 45-90 cos */
if(degrees < 90)
{
xtemp = (float) sine_cosine[90-degrees][1];
ytemp = (float) sine_cosine[90-degrees][0];
ytemp = ytemp * -1;
break;
}
/* 90 - 135 */
if(degrees < 135)
{
xtemp = (float) sine_cosine[degrees-90][1];
ytemp = (float) sine_cosine[degrees-90][0];
break;
}
/* 135 - 180 */
if(degrees< 180)
{
xtemp = (float) sine_cosine[180-degrees][0];
ytemp = (float) sine_cosine[180-degrees][1];
break;
}
/* 180 - 225 */
if(degrees< 225)
{
xtemp = (float) sine_cosine[degrees-180][0];
xtemp = xtemp * -1;
ytemp = (float) sine_cosine[degrees-180][1];
break;
}
/* 225 - 270 */
if(degrees< 270)
{
xtemp = (float) sine_cosine[270-degrees][1];
xtemp = xtemp * -1;
ytemp = (float) sine_cosine[270-degrees][0];
break;
}
/* 270 - 315 */
if(degrees< 315)
{
xtemp = (float) sine_cosine[degrees-270][1];
xtemp = xtemp * -1;
ytemp = (float) sine_cosine[degrees-270][0];
ytemp = ytemp * -1;
break;
}
/* 315 - 360 */
xtemp = (float) sine_cosine[360-degrees][0];
xtemp = xtemp * -1;
ytemp = (float) sine_cosine[360-degrees][1];
ytemp = ytemp * -1;
}
ytemp = ytemp/32767;
xtemp = xtemp/32767;
xtemp *= (aspect_h*baselength);
ytemp *= (aspect_v*baselength);
*x += (int )xtemp;
*y += (int )ytemp;
}
>By the way, I posted a disk algorithm earlier.
Here's an example of using the endpoints of a circle from my Win16 Lenslite
Screen Saver from ages ago(comes with source, just google VgaFan and Bill
Buckels):
// filled disk template structure - horizontal lines
typedef struct {
int x1;
int x2;
}DISK_STRUCT;
DISK_STRUCT disk_struct[LOGICAL_SIZE];
/* -------------------------------------------------------------------- */
/* add horizontal coordinates to a disk structure in memory */
/* helper function for BuildDisk Initialization Routine (below) */
/* -------------------------------------------------------------------- */
void HDisk(int y, int x1, int x2)
{
if (disk_struct[y].x1 == -1 || disk_struct[y].x1 > x1)
disk_struct[y].x1 = x1;
if (disk_struct[y].x2 == -1 || disk_struct[y].x2 < x2)
disk_struct[y].x2 = x2;
}
/* -------------------------------------------------------------------- */
/* BuildDisk */
/* precalculation routine - standard filled circle without floating pt. */
/* Creates a disk template structure in memory */
/* the disk structure consists of horizontal line vectors for start and */
/* end points of the specified disk size... this allows us to blit the */
/* portion of the scanline that fits within the disk by using a table */
/* reference instead of a calculated value which simplifies our actual */
/* calculations at run time... */
/* -------------------------------------------------------------------- */
void BuildDisk(int r)
{
int x,y; /*current point around 1/8 disk*/
int rem; /*remainder value*/
// initialize disk structure... make all values the same
// and make all values initially into unusable values...
for (y=0; y < LOGICAL_SIZE; y++) {
disk_struct[y].x1 = -1;
disk_struct[y].x2 = -1;
}
y=0;
x=r; /*initial point is on axis*/
rem=r/2; /*remainder=1/2 for roundoff*/
while (x>=y) /*loop for 1/8 of a circle*/
{
/*fill in top half of circle*/
HDisk(r+y,r-x,r+x);
/*and bottom half*/
HDisk(r-y,r-x,r+x);
y += 1; /*always increment y*/
rem=rem-y; /*subtract from remainder*/
if (rem<0) /*if it's time,*/
{
x=x-1; /*then decrement x*/
rem=rem+x; /*and bump remainder back up*/
}
}
while(x>0) /*loop for the top 1/8th of circle*/
{
x=x-1; /*now you always move in x direction*/
rem=rem-x; /*and subtract x from the remainder*/
if (rem<0) /*when remainder underflows,*/
{
/*fill in the top side,*/
HDisk(r+y,r-x,r+x);
/*and the bottom*/
HDisk(r-y,r-x,r+x);
y=y+1;
rem=rem+y; /*bump the remainder back up*/
}
}
return;
}
/* ---------------------------------------------------------------------- */
/* Blit a transparent disk onto the screen using the disk template */
/* to draw horizontal image lines */
/* ---------------------------------------------------------------------- */
void Disk(HWND hWnd, int xc, int yc, int r)
{
HDC hMemDC;
HBITMAP hbmOld;
int y, d, height, width;
int x1, y1;
int xoff;
if (hWorkBit == NULL) // must have a workbuffer
return; // try to avoid flicker by building
// a lens off screen...
hDC = GetDC(hWnd);
if (hDC == NULL)
return;
if (NULL != (hMemDC = CreateCompatibleDC(hDC))) {
hbmOld = SelectObject(hMemDC, hWorkBit);
d = (int)((wLiteLimit + LOGICAL_BUFFER) * 2);
if (bBlack == FALSE) {
// blacken the areas that we will not use first time thru...
BitBlt(hMemDC, 0, 0, d, d, hDCHidden, 0, 0, BLACKNESS);
bBlack = TRUE;
}
// build the lens in memory
y1 = yc - r;
x1 = xc - r;
height = (int)(wLiteLimit * 2);
xoff = NIL;
for (y = 0; y < height; y++) {
if (disk_struct[y].x1 == disk_struct[y].x2)
continue;
width = (disk_struct[y].x2 - disk_struct[y].x1) + 1;
if (!bFocus )
xoff = disk_struct[y].x1; // warp to curve on x-axis
// line by line
BitBlt(hMemDC, disk_struct[y].x1 + LOGICAL_BUFFER,
y + LOGICAL_BUFFER, width, 1,
hDCHidden, x1 + xoff,
y1 + y, SRCCOPY);
}
// do it - blit the finished lens on screen
BitBlt(hDC, x1-LOGICAL_BUFFER, y1 - LOGICAL_BUFFER,
d, d, hMemDC, 0, 0, SRCCOPY);
SelectObject(hMemDC, hbmOld); // make sure we don't
DeleteDC(hMemDC); // exhaust all resources
}
ReleaseDC(hWnd, hDC);
return;
}
http://www.ffd2.com/fridge/chacking/
http://www.ffd2.com/fridge/chacking/c=hacking9.txt
"2D Graphics Toolbox -- Circles", Stephen Judd. An awfully neat circle
drawing algorithm, if I do say so myself. (See also issue 10 or 11).
http://www.ffd2.com/fridge/chacking/c=hacking10.txt
"A Different Perspective, part III", by two old bums. Neat-o line
drawing enhancement, general polygon routine, general hidden face
removal, alternative filling method, and a misplaced and important
addendum to the Circle routine in issue 9.
http://www.ffd2.com/fridge/chacking/c=hacking11.txt
"The Graphics Toolbox: Ellipses", Stephen L. Judd. Follow-up to the
circle drawing algorithm, logarithmic division.
http://www.ffd2.com/fridge/discovery/
http://www.ffd2.com/fridge/discovery/issue3.gz
Issue #3
* "Rommaging around : $A480-$A856", Stephen Judd. Disassembly and
discussion of the BASIC intrepeter, and BLARG.
>Use BLARG
There's a thought:) Wonder what kind of a graphics screen he wants to draw
upon and what computer language he wants to write in? The guy just trolls in
and out. Regardless the links are great so as you say, what the heck!
Bill
Probably PETSCII graphics in BASIC ;) Talk about overkill...