Post Reply 
FILLCIRCLE_P()
02-22-2020, 10:57 PM
Post: #1
FILLCIRCLE_P()
HP Prime does not have a function which draws a filled circle. I have seen many attempts to implement a substitute by drawing concentric circles with one pixel increment radii, but it does not work well, and the area of such a circle is usually not uniform.

Recently I was working on an application that really could use such a filled circle... it was not absolutely necessary, but it definitely look nice... I decided to spend an hour or two on writing such a procedure.
Maybe somebody will find it useful, and will save time he would otherwise have to spend on writing it.

A program below consists of a procedure drawing a filled circle (FILLCIRCLE_P), and a few small demo/test additions. FILLCIRCLE_P() can be included in the program, or in a library. It draws a filled polygon (a function supported by HP Prime) which vertices are placed on the perimeter of the circle. (One of many methods to draw a filled circle; I am not saying the best one or the fastest one.)

Code:
EXPORT FILLCIRCLE_P (nG, nX, nY, nRadius, nSmoothness, nColor)
BEGIN
  // Print filled circle ver. 1.2, (c) dap, 2020
  // Arguments:
  // nG - graphic variable to use for printing
  // nX, nY - pixel coordinates of the center of the circle
  // nRadius - radius of the circle (in pixels)
  // nSmoothness - smoothness of the circle defined as length of sagitta (in pixels, may be fractional)
  // nColor - color of the circle
  // Returns 0 if no error, (-1) if nSmoothness below limit

  LOCAL lVertices = {};
  LOCAL I, nHAngleBuffer, nAlpha, nNumberOfPoints;

  // check input parameters
  IF nSmoothness<0.05 THEN
    RETURN (-1);
  END;

  nHAngleBuffer := HAngle;
  HAngle := 1;    // set angle to degrees

  // caclulate angle increment
  nNumberOfPoints := CEILING(90/(2*ACOS(1-nSmoothness/nRadius)));
  nAlpha := 90/nNumberOfPoints;
    
  // calculate polygon vertices
  nNumberOfPoints := FLOOR (nNumberOfPoints);
  FOR I FROM 1 TO nNumberOfPoints DO
    lVertices(I) := (nX+ROUND(nRadius*SIN((I-1)*nAlpha),0), nY-ROUND(nRadius*COS((I-1)*nAlpha),0));
    lVertices(nNumberOfPoints+I):= (nX+(nY-IM(lVertices(I))), nY+(RE(lVertices(I))-nX));
    lVertices(2*nNumberOfPoints+I):= (2*nX-(RE(lVertices(I))), 2*nY-(IM(lVertices(I))));
    lVertices(3*nNumberOfPoints+I):= (nX+(IM(lVertices(I))-nY), nY-(RE(lVertices(I))-nX));
  END;

  // print polygon
  FILLPOLY_P (nG, lVertices, nColor);

  // *** for debugging ***
  // print vertices
  // when enabled, vertices are marked with white dots
  // (to see vertices only, comment out  FILLPOLY_P above)
  // FOR I FROM 1 TO SIZE(lVertices) DO
  //   PIXON_P (G0, RE(lVertices(I)), IM(lVertices(I)), RGB(255,255,255));
  // END;
  
  HAngle := nHAngleBuffer;  // restore previous angle setting
  RETURN 0;
END;

// *** DEBUGGING/TEST PROCEDURES ***
//      (exit by pressing Esc)

// prototypes
 WAIT_FOR_ESCAPE_KEY ();

EXPORT SPEED_TEST ()
BEGIN
  // test speed 
  // prints concentric circles of given smoothness and calculates printing times
  LOCAL I, J, nIndex, R, G, B;
  LOCAL nTStart, nTAccumulated;
  LOCAL nRuns := 100;
  LOCAL nSmoothness;
  LOCAL lSmoothness :=  {0.1, 0.2, 0.5, 1.0, 2.0, 5.0};

  CHOOSE(nIndex, "Choose smoothness", "0.05", "0.1", "0.2", "0.5", "1.0", "2.0","0.05 one time","2.0 one time");
  CASE
    IF (nIndex==7) THEN
      nSmoothness := 0.05;
      nRuns := 1;
    END;
     IF (nIndex==8) THEN
      nSmoothness := 2.0;
      nRuns := 1;
    END;
    DEFAULT
      nSmoothness := lSmoothness(nIndex);
    END;  // CASE
  
  STARTVIEW(-1);
  RECT(RGB(0,0,0));

  FOR I FROM 16 DOWNTO 1 DO
    nTAccumulated := 0;
    FOR J FROM 0 TO nRuns DO
      R := RANDINT(0,255);
      G := RANDINT(0,255);
      B := RANDINT(0,255); 
      nTStart := TICKS();
      FILLCIRCLE_P (G0, 160, 120, I*10, nSmoothness, RGB(R, G, B));
      nTAccumulated := nTAccumulated+(TICKS()-nTStart);
    END;
    TEXTOUT_P (nTAccumulated/nRuns, 2, 10+12*(I-1), 1, RGB(255,255,0));
  END;

   WAIT_FOR_ESCAPE_KEY ();

END;

EXPORT SMOOTHNESS_TEST ()
BEGIN
  // prints 12 circles of different smoothness
  LOCAL I, J;
  LOCAL lSmoothness := {0.05, 0.1, 0.2, 0.5, 0.8, 1.0, 1.5, 2.0, 4.0, 6.0, 8.0, 9.0};

  STARTVIEW(-1);
  RECT(RGB(0,0,0));

  FOR J FROM 1 TO 3 DO
    FOR I FROM 1 TO 4 DO
      FILLCIRCLE_P (G0, 64*I, 60*J, 24, lSmoothness(I+(J-1)*4), RGB(255,0,0));
      TEXTOUT_P (STRING(lSmoothness(I+(J-1)*4),2,2),  64*I-10, 60*J-4, 2, RGB(255,255,255));
    END;
  END;

   WAIT_FOR_ESCAPE_KEY ();

END;

EXPORT JUST_ONE_CENTERED_CIRCLE ()
BEGIN
  // prints single centered circle
  STARTVIEW(-1);
  RECT(RGB(0,0,0));
  FILLCIRCLE_P (G0, 160, 120, 100, 0.05, RGB(255,0,0));

  LINE_P(G0, 0, 120, 319, 120, RGB(0,255,0));
  LINE_P(G0, 160, 0, 160, 239, RGB(0,255,0));

  WAIT_FOR_ESCAPE_KEY ();

END; 

EXPORT BUBBLES ()
BEGIN
  // prints circles of random color, size, and smoothness at random locations
  STARTVIEW(-1);
  RECT(RGB(255,255,255));
  REPEAT
    FILLCIRCLE_P (G0, RANDINT(0,319), RANDINT(0,239), RANDINT(5,80), RANDINT(5,500)/100, RGB(RANDINT(0,255),RANDINT(0,255),RANDINT(0,255)));
    WAIT(0.5);
  UNTIL GETKEY()==4;
END;

// *** supporting procedures ***
WAIT_FOR_ESCAPE_KEY ()
BEGIN
  // wait until Esc key is pressed
  LOCAL nKey;

  REPEAT
    nKey:=GETKEY;
  UNTIL nKey==4;
END;
The code is very simple and self explanatory.

One comment I would like to make is that the first, initial version of the code was not taking advantage of the symmetry of circle quadrants. I optimized the code later counting on increase of the speed of drawing. To my surprise, the increase of speed was smaller than 20%! This means that either calculation of trigonometric functions is fast, or that calculations related to list indexing and/or drawing filled polygon is slow. I did not have time to explore this subject deeper.

Darius
Find all posts by this user
Quote this message in a reply
02-22-2020, 11:22 PM
Post: #2
RE: FILLCIRCLE_P()
Some firmwares ago that feature was added.
Syntax:
ARC(G, x, y, r or {rx, ry}, [∠1, ∠2], [border_color, [fill_color]])
Also: Program catalog/New/Example/Demo_ARC

Viga C | TD | FB
Visit this user's website Find all posts by this user
Quote this message in a reply
02-23-2020, 01:10 AM
Post: #3
RE: FILLCIRCLE_P()
(02-22-2020 11:22 PM)Carlos295pz Wrote:  Some firmwares ago that feature was added.

Well, yeah.

You are absolutely correct, Carlos. The description of this feature can be found in the Prime help. But if you look at the user's manual, it says:
ARC_P(G, x, y, r [ , a1, a2, c])
Not a word about filling.

Lesson to learn: every time you use a function, look it up in the calculator's help. (I am being facetious. I don't have time to go through every help item in the calculator every time the f/w update is released. On the other hand, I would not mind getting an updated user's manual with it. Or, at least, errata/what's new file.)

A serious conclusion is that the user's manual is outdated and, as such, cannot be trusted.

Okay, let's end this rant here, and look for the bright side: at least I had some fun with solving a trivial math problem. Smile

Thanks, Carlos, for letting me know about this addition.

Darius
Find all posts by this user
Quote this message in a reply
03-01-2020, 01:25 AM
Post: #4
RE: FILLCIRCLE_P()
Thanks for pointing this out Carlos. I had no idea they expanded ARC to fill in circles. I just modified all my games that I wrote that used the old method (drawing concentric circles with one pixel increment radii)
Find all posts by this user
Quote this message in a reply
Post Reply 




User(s) browsing this thread: 2 Guest(s)