Home
JAQForum Ver 24.01
Log In or Join  
Active Topics
Local Time 08:26 12 Nov 2025 Privacy Policy
Jump to

Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.

Forum Index : Microcontroller and PC projects : Help with Graphics

Author Message
OA47

Guru

Joined: 11/04/2012
Location: Australia
Posts: 1013
Posted: 02:11am 06 Jan 2019
Copy link to clipboard 
Print this post

@ Geoff : Wondering if you would consider adding ARC to the basic drawing commands (Part circle)

In the meantime I am having trouble drawing a right half circle 60 pixels wide on a micromite. To draw the first quadrant I tried:
For X=1 To 60
Pixel X,60-(60^2-X^2)^-2
Next X

But because the values in the equation over run the 32bit calculator I cannot use this equation.

Any thoughts?

Found the error. SQRT should be represented ^0.5
Edited by OA47 2019-01-07
 
panky

Guru

Joined: 02/10/2012
Location: Australia
Posts: 1116
Posted: 02:49am 06 Jan 2019
Copy link to clipboard 
Print this post

OA47,

Try this code - slow, especially for thick arcs.


' Routine to draw an arc segment of a circle
' - based on segment code from Peter Mather.
' test parameters and test code
x% =400
y% =200
outsize% = 100
startradial% = 225
endradial% = 135
col% = 65534
insize% = 50
cls
for outsize% = 100 to 102
insize% = outsize%
arc x%,y%,outsize%,startradial%,endradial%,col%,insize%
next outsize%
end
' End of test code

' Routine to draw an arc segment of a circle
' Parameters are:
' x-coordinate of centre of arc circle
' y-coordinate of centre of arc circle
' outsize or radius of circle
' start radial of arc to be drawn (0-360 degrees)
' NB: 0 degrees is at North (top)
' end radial of arc to be drawn (0-360 degrees)
' colour to draw arc
' inner radius for drawing radial lines (thickness or arc), leave blank or set to zero if not required
' NB arc is composed of radial lines so if insize is more than about
' 5 different from outsize, the arc will not be smooth. If a thick, smooth arc
' is required, draw multiple arcs with differing radii
Sub arc(x%, y%, outsize%, startradial%, endradial%, col%, insize%)
Local integer i%, x1%, x2%, y1%, y2%, sr%, er%
Local stepsize% = 1
If startradial% < endradial% Then
sr%=startradial%
er%=endradial%
' now draw arc
For i%=sr% To er% Step stepsize%
x2%=Sin(Rad(i%))*outsize% + x%
y2%=-Cos(Rad(i%))*outsize% + y%
x1%=Sin(Rad(i%-stepsize%))*insize% + x%
y1%=-Cos(Rad(i%-stepsize%))*insize% + y%
Line x1%,y1%,x2%,y2%,,col%
Next i%
Else ' draw 2 part arc via zero
er%=360
sr%=startradial%
' now draw arc from sr to 0
For i%=sr% To er% Step stepsize%
x2%=Sin(Rad(i%))*outsize% + x%
y2%=-Cos(Rad(i%))*outsize% + y%
x1%=Sin(Rad(i%-stepsize%))*insize% + x%
y1%=-Cos(Rad(i%-stepsize%))*insize% + y%
Line x1%,y1%,x2%,y2%,,col%
Next i%
er%=endradial%
sr%=0
' now draw arc from 0 to er
For i%=sr% To er% Step stepsize%
x2%=Sin(Rad(i%))*outsize% + x%
y2%=-Cos(Rad(i%))*outsize% + y%
x1%=Sin(Rad(i%-stepsize%))*insize% + x%
y1%=-Cos(Rad(i%-stepsize%))*insize% + y%
Line x1%,y1%,x2%,y2%,,col%
Next i%
EndIf
End Sub


... almost all of the Maximites, the MicromMites, the MM Extremes, the ArmMites, the PicoMite and loving it!
 
AussieWombat
Newbie

Joined: 04/05/2018
Location: Australia
Posts: 21
Posted: 02:55am 06 Jan 2019
Copy link to clipboard 
Print this post

Isn't it just cosine and sine of the angle multiplied by the radius

ie:

For angle = 0 to 90 'One quadrant
myradius = 60
RADangle= Rad(angle)
Pixel int(Cos(RADangle)*myradius) , int(Sin(RADangle)*myradius)

Next angle
 
erbp
Senior Member

Joined: 03/05/2016
Location: Australia
Posts: 195
Posted: 11:18am 06 Jan 2019
Copy link to clipboard 
Print this post

You might be interested in a variant of the above, it uses considerably less program memory space.

The parameters to the Arc subroutine are slightly different, in that it uses a "lw" parameter to specify the required line width for the arc, and that parameter is inserted in the parameter list following the "inradius" parameter. The 'outsize' parameter has been removed. Note that there is no validation of the 'lw' parameter - it should be 1 -> whatever value you need. 0 or negative values will probably result in no arc being drawn!

It also implements line widths greater than 1 by drawing multiple concentric arcs, with each arc having a radius 1 pixel larger than the previous. This seems to provide a better result than the radial lines method, but is probably a bit slower.

The code below includes a test program designed for a 800x480 LCD.

DIM i%, j%, w%, lc%
' This test code is designed to run on a 800x480 LCD panel

CLS RGB(144,144,144)

For j% = 0 To 2
w% = j% + 1 'to test wider line widths, replace the 1 by a larger number

For i% = 0 To 7
select Case i%
case 0
lc% = RGB(White)
case 1
lc% = RGB(Yellow)
case 2
lc% = RGB(Green)
case 3
lc% = RGB(Blue)
case 4
lc% = RGB(Black)
case 5
lc% = RGB(Magenta)
case 6
lc% = RGB(Red)
case 7
lc% = RGB(Cyan)
End Select

Arc((i% * 100),((j% * 150) + 100),80,w%,0,135,lc%)
Next i%
Next j%

END 'of test code

' Routine to draw an arc segment of a circle
' based on segment code from Peter Mather.
'
' Parameters are:
' x-coordinate of centre of arc circle
' y-coordinate of centre of arc circle
' inradius - inner radius of arc circle
' lw - line width (thickness of arc)
' NB: line widths > 1 are achieved by drawing arcs of concentric circles
' with the arc radius increased by 1 pixel each time.
' start radial of arc to be drawn (0-360 degrees)
' NB: 0 degrees is at North (top)
' end radial of arc to be drawn (0-360 degrees)
' colour of arc line
Sub Arc(x%, y%, inradius%, lw%, startradial%, endradial%, col%)
If startradial% < endradial% Then
' draw arc
DrawArc(x%, y%, inradius%, lw%, startradial%, endradial%, col%)

Else ' draw 2 part arc via zero
' draw arc from startradial to 360
DrawArc(x%, y%, inradius%, lw%, startradial%, 360, col%)
' draw arc from 0 to endradial
DrawArc(x%, y%, inradius%, lw%, 0, endradial%, col%)
EndIf
End Sub

Sub DrawArc(x%, y%, inrad%, lw%, sr%, er%, col%)
Local c%, i%, r%
For c% = 0 To (lw%-1) Step 1
r% = inrad% + c%
For i%=sr% To er% Step 1
x1%=Sin(Rad(i%-1))*r% + x%
y1%=-Cos(Rad(i%-1))*r% + y%
Pixel x1%,y1%,col%
Next i%
Next c%
End Sub
 
robert.rozee
Guru

Joined: 31/12/2012
Location: New Zealand
Posts: 2464
Posted: 11:32am 06 Jan 2019
Copy link to clipboard 
Print this post

drawing circles and parts thereof is relatively complicated, your initial approach is pretty much the best:

For X=1 To 60
Pixel X,60-SQR(60^2-X^2)
Next X


using trig functions and going around the circle in small steps is a bad approach, as apart from the excessive computational overhead of the trig functions you will end up either plotting the same point multiple times or leaving gaps. there is also the issue of having 'too many' pixels drawn along the arc - within any 2x2 square there should be no more than two pixels filled otherwise you'll get the appearance of 'clumping'.

a full circle can be divided up into eight segments (octants). the segment starting at 12 o'clock and going round to 1:30 should be stepped across horizontally, the two segments from 1:30 to 4:30 should be stepped across vertically, the two segments from 4:30 to 7:30 should be stepped across horizontally, etc.

i'd suggest looking up "bresenham's line algorithm" to understand the clumping problem in the straight-line case, then "bresenham's circle algorithm" for arcs. there is a bit of an explanation here for circles ,that seems to look about right:
https://medium.com/@rakeshnchinta/bresenhams-circle-drawing-algorithm-95792c9f991b
(my own experience is purely with drawing lines)

this is a good example of the old adage: for every complicated problem there is a simple solution that does not work. although, once you get your head around it, bresenham's algorithms are actually quite simple to understand.


cheers,
rob :-)
 
Geoffg

Guru

Joined: 06/06/2011
Location: Australia
Posts: 3308
Posted: 11:49am 06 Jan 2019
Copy link to clipboard 
Print this post

Some time ago I went searching for an efficient algorithm for drawing arcs with variable line width and could not find anything. My maths is not good enough to invent such a thing so if anyone (Peter ?) can find a good solution I would be very interested.

In the meantime I will take a look at panky and erbp's code.

GeoffEdited by Geoffg 2019-01-07
Geoff Graham - http://geoffg.net
 
robert.rozee
Guru

Joined: 31/12/2012
Location: New Zealand
Posts: 2464
Posted: 12:51pm 06 Jan 2019
Copy link to clipboard 
Print this post

from:
https://en.wikipedia.org/wiki/Midpoint_circle_algorithm

void drawcircle(int x0, int y0, int radius)
{
int x = radius-1;
int y = 0;
int dx = 1;
int dy = 1;
int err = dx - (radius << 1);

while (x >= y)
{
putpixel(x0 + x, y0 + y);
putpixel(x0 + y, y0 + x);
putpixel(x0 - y, y0 + x);
putpixel(x0 - x, y0 + y);
putpixel(x0 - x, y0 - y);
putpixel(x0 - y, y0 - x);
putpixel(x0 + y, y0 - x);
putpixel(x0 + x, y0 - y);

if (err <= 0)
{
y++;
err += dy;
dy += 2;
}

if (err > 0)
{
x--;
dx += 2;
err += dx - (radius << 1);
}
}
}


cheers,
rob :-)
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 10572
Posted: 01:10pm 06 Jan 2019
Copy link to clipboard 
Print this post

  Quote  from:
https://en.wikipedia.org/wiki/Midpoint_circle_algorithm


not what is needed

  Quote  efficient algorithm for drawing arcs with variable line width
 
robert.rozee
Guru

Joined: 31/12/2012
Location: New Zealand
Posts: 2464
Posted: 01:45pm 06 Jan 2019
Copy link to clipboard 
Print this post

  matherp said  
  Quote  from:
https://en.wikipedia.org/wiki/Midpoint_circle_algorithm

not what is needed
  Quote  efficient algorithm for drawing arcs with variable line width


the fundamental algorithm is sound.

to achieve what geoff wants, one can:

1) draw two arcs, joined together across the ends with either straight line segments or two 180 degree arcs, and then simply flood-fill within the enclosed area, or,

2) within an octant run the inner and outer arcs within the same loop (suspending drawing either arc as appropriate while the loop counter is out of range), and then instead of drawing points, draw short line segments between inner and outer arcs, or,

3) follow a single arc down the middle with a circular pen, at each step calculating the leading edge of the pen and drawing a 180 degree arc across that edge (windows seems to do it this way).

approach 3) is simple, but will lead to a certain percentage of pixels being drawn twice and is not so great for drawing gauges. 2) is complicated when it comes to dealing with the ends of octants that may have 'triangular' bits to fill in. and 1) while simple requires an effective flood-fill that may be beyond mmbasic's graphics subsystem.


cheers,
rob :-)Edited by robert.rozee 2019-01-07
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 10572
Posted: 03:55pm 06 Jan 2019
Copy link to clipboard 
Print this post

  Quote  the fundamental algorithm is sound.


The algorithm is the one already used. It works by drawing 1/8th circle arcs starting from the 90 degree points. It cannot draw arbitrary arcs.

Flood fill can't be done without a memory image

You can draw any arbitrary arcs using bezier curves - I posted code to do this previously but the challenge is drawing thick arcs efficiently
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 10572
Posted: 06:47pm 06 Jan 2019
Copy link to clipboard 
Print this post

  Quote  if anyone (Peter ?) can find a good solution I would be very interested.


Here you go - two new drawing commands. Definitely fast enough on the Armmite H7 but haven't tried on anything else.

Parameters for the ARC command
X coordinate of centre of arc
Y coordinate of centre of arc
inner radius of arc
outer radius of arc - can be omitted if 1 pixel wide
start radial of arc in degrees
end radial of arc in degrees
Colour of arc

Parameters for the BEZIER command
X coordinate of start point
Y coordinate of start point
X coordinate of first control point
Y coordinate of first control point
X coordinate of second control point
Y coordinate of second control point
X coordinate of end point
Y coordinate of end point
Colour of curve

Test code:


CLS
bezier 10,10,106,80,212,80,309,10,rgb(yellow)
bezier 10,230,106,150,212,150,309,230,rgb(yellow)
bezier 10,10,80,80,80,160,10,230,rgb(green)
bezier 309,10,239,80,239,160,309,230,rgb(green)
text 85,110,"Cubic Bezier Curves"
pause 5000
CLS
timer=0
for i=0 to 99 step 4
Arc mm.hres/2,mm.vres/2,20+i,,325+2*i,5+5*i,rgb(yellow)
next i
print timer 'takes 30mSec on Armmite H7
pause 5000
CLS
timer=0
arc mm.hres\2,mm.vres\2,100,110,315,45,rgb(red)
arc mm.hres\2,mm.vres\2,100,110,45,315,rgb(green)
print timer ' takes 12mSec on Armmite H7


C code, can be included in draw.c:

void bezier(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, int c){
float tmp,tmp1,tmp2,tmp3,tmp4,tmp5,tmp6,tmp7,tmp8,t=0.0,xt=x0,yt=y0;
int i, xti,yti,xtlast=-1, ytlast=-1;
for (i=0; i<1000; i++)
{
tmp = 1.0 - t;
tmp3 = t * t;
tmp4 = tmp * tmp;
tmp1 = tmp3 * t;
tmp2 = tmp4 * tmp;
tmp5 = 3.0 * t;
tmp6 = 3.0 * tmp3;
tmp7 = tmp5 * tmp4;
tmp8 = tmp6 * tmp;
xti=(int)xt;
yti=(int)yt;
xt = ((((tmp2 * x0) + (tmp7 * x1)) + (tmp8 * x2)) + (tmp1 * x3));
yt = ((((tmp2 * y0) + (tmp7 * y1)) + (tmp8 * y2)) +(tmp1 * y3));
if((xti!=xtlast) || (yti!=ytlast)) {
DrawPixel(xti, yti, c);
xtlast=xti;
ytlast=yti;
}
t+=0.001;
}
}
void SmallArc(float x, float y, float r, float a1i, float a2i, int col){
// First compute all four points for an arc that subtends the same total angle
// but is centered on the X-axis
float a1,a2,a,x1,y1,x2,y2,x3,y3,x4,y4,f,ar,cos_ar,sin_ar ;
float x2i,x3i,y2i,y3i ;
float pidiv2=PI_VALUE/2.0, k = 0.5522847498 ;
a1=Rad(a1i)- pidiv2 ;
a2=Rad(a2i)- pidiv2 ;
a = (a2-a1) / 2.0 ;
x4 = r * cos(a) ;
y4 = r * sin(a) ;
x1= x4 ;
y1= -y4 ;
f = k * tan(a) ;
x2 = x1 + f * y4 ;
y2 = y1 + f * x4 ;
x3 = x2 ;
y3= -y2 ;
// Now find the arc points actual locations by computing x1,y1 and x4,y4
// and rotating the control points by a + a1
ar = a + a1 ;
cos_ar = cos(ar) ;
sin_ar = sin(ar) ;
x1 = r * cos(a1) + x ;
y1 = r * sin(a1) + y ;
x2i = x2 * cos_ar - y2 * sin_ar + x ;
y2i = x2 * sin_ar + y2 * cos_ar + y ;
x3i = x3 * cos_ar - y3 * sin_ar + x ;
y3i = x3 * sin_ar + y3 * cos_ar + y ;
x4 = r * cos(a2) + x ;
y4 = r * sin(a2) + y ;
bezier(x1,y1,x2i,y2i,x3i,y3i,x4,y4,col) ;
}
void cmd_arc(void){
// Parameters are:
// X coordinate of centre of arc
// Y coordinate of centre of arc
// inner radius of arc
// outer radius of arc - omit it 1 pixel wide
// start radial of arc in degrees
// end radial of arc in degrees
// Colour of arc
int x, y, r1, r2, c;
float f, rad1, rad2, rstart;
getargs(&cmdline, 13,",");
if(Option.DISPLAY_TYPE == 0) error("Display not configured");
if(!(argc == 11 || argc == 13)) error("Argument count");
x = getinteger(argv[0]);
y = getinteger(argv[2]);
r1 = getinteger(argv[4]);
if(*argv[6])r2 = getinteger(argv[6]);
else r2=r1;
if(r2 < r1)error("Inner radius < outer");
rad1 = getnumber(argv[8]);
rad2 = getnumber(argv[10]);
if(argc == 13)
c = getint(argv[12], 0, WHITE);
else
c = gui_fcolour;
while(rad2<rad1)rad2+=360;
for(f = r1 ; f<= r2; f+=1.0){
rstart=rad1;
while(rad2 - rstart > 90){
SmallArc(x , y , f , rstart, rstart+90 , c);
rstart+=90;
}
SmallArc(x , y , f , rstart, rad2 , c);
}
}
void cmd_bezier(void) {
// X coordinate of start point
// Y coordinate of start point
// X coordinate of first control point
// Y coordinate of first control point
// X coordinate of second control point
// Y coordinate of second control point
// X coordinate of end point
// Y coordinate of end point
// Colour of curve
int xx0, yy0, xx1, yy1, xx2, yy2, xx3, yy3, c;
getargs(&cmdline, 17,",");
if(!(argc==15 || argc==17))error("Argument count");
if(Option.DISPLAY_TYPE == 0) error("Display not configured");
xx0 = getint(argv[0],0,HRes-1);
yy0 = getint(argv[2],0,VRes-1);
xx1 = getinteger(argv[4]);
yy1 = getinteger(argv[6]);
xx2 = getinteger(argv[8]);
yy2 = getinteger(argv[10]);
xx3 = getint(argv[12],0,HRes-1);
yy3 = getint(argv[14],0,VRes-1);
c = gui_fcolour; // setup the defaults
if(argc > 15 && *argv[16]) c = getint(argv[16], 0, WHITE);
bezier(xx0, yy0, xx1, yy1, xx2, yy2, xx3, yy3, c);
}
Edited by matherp 2019-01-08
 
Print this page


To reply to this topic, you need to log in.

The Back Shed's forum code is written, and hosted, in Australia.
© JAQ Software 2025