|
Forum Index : Microcontroller and PC projects : Help with Graphics
| Author | Message | ||||
| OA47 Guru Joined: 11/04/2012 Location: AustraliaPosts: 1013 |
@ 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 |
||||
| panky Guru Joined: 02/10/2012 Location: AustraliaPosts: 1116 |
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: AustraliaPosts: 21 |
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: AustraliaPosts: 195 |
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 ZealandPosts: 2464 |
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: AustraliaPosts: 3308 |
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. Geoff Geoff Graham - http://geoffg.net |
||||
| robert.rozee Guru Joined: 31/12/2012 Location: New ZealandPosts: 2464 |
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 KingdomPosts: 10572 |
not what is needed |
||||
| robert.rozee Guru Joined: 31/12/2012 Location: New ZealandPosts: 2464 |
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 :-) |
||||
| matherp Guru Joined: 11/12/2012 Location: United KingdomPosts: 10572 |
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 KingdomPosts: 10572 |
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); } |
||||
| The Back Shed's forum code is written, and hosted, in Australia. | © JAQ Software 2025 |