A nice analog clock using nanoSVG

198 views
Skip to first unread message

Greg Ercolano

unread,
Jun 3, 2018, 10:20:39 PM6/3/18
to fltkg...@googlegroups.com
I knew I'd get around to this eventually.. here I'm using FLTK's
new nanoSVG support to draw the analog clock's face and hands:

http://seriss.com/people/erco/fltk/#FLTK-Simplex-Clock

The main reason I'm using SVG is to leverage its nicer antialiased
rendering ability, good for the smooth sweep of the clock's analog hands.

And since the whole clock (hands and face) is in vector format, it could
be scaled "indefinitely" without suffering from raster pixelation that,
for instance, a PNG image would.

NOTE: I haven't /actually/ made this application's window resizable
to take full advantage of vector scaling, but it easily could be.

Manolo

unread,
Jun 5, 2018, 11:52:53 AM6/5/18
to fltk.general

Very nice.
And it scales gracefuly without pixelation in response to ctrl/+/ keystrokes.

Greg Ercolano

unread,
Jun 5, 2018, 8:18:25 PM6/5/18
to fltkg...@googlegroups.com
On 06/05/18 08:52, Manolo wrote:
> Very nice.
> And it scales gracefuly without pixelation in response to ctrl/+/ keystrokes.

Cool -- I tried shrinking the clock to 50% with ctrl(-), so it could run
as a little "icon" on my desktop.

I think I found a weird bug though with the scaling on linux;
if my display goes to sleep, when it wakes up, the scaled down
window remained 50%, but the clock graphics had "grown" back to 100%.

I'll open a bug if I can replicate.


Greg Ercolano

unread,
Jun 6, 2018, 5:14:07 AM6/6/18
to fltkg...@googlegroups.com
On 06/05/18 17:18, Greg Ercolano wrote:>
> Cool -- I tried shrinking the clock to 50% with ctrl(-), so it could run
> as a little "icon" on my desktop.
>
> I think I found a weird bug though with the scaling on linux;
> if my display goes to sleep, when it wakes up, the scaled down
> window remained 50%, but the clock graphics had "grown" back to 100%.
>
> I'll open a bug if I can replicate.


Hmm, I'm not sure exactly how to replicate, other than to shrink it to 50%,
leave it for a while, wake up the screen when I come back, and the clock
has 'grown' back to 100%.

I've attached a screenshot of what it looks like when I wake up the screen.

If I then try to manipulate it with Ctrl(0) or Ctrl(+), the window grows/shrinks,
but the clock image remains out of sync with the window size, the clock always
being larger than the screen, the clock re-centering inside the window.

I tried shortening my screen sleep time (DPMS) to 30 seconds and waiting for
the display to time out, then waking it up, but that /doesn't/ replicate the
problem.

I don't have a screen saver configured, just the DPMS timeout that powers down
the screen after 10 mins, so I'm not sure why there's anything different about
waiting a long time.
clock50.png

Manolo

unread,
Jun 6, 2018, 5:28:32 AM6/6/18
to fltk.general


On Wednesday, 6 June 2018 11:14:07 UTC+2, Greg Ercolano wrote:

   Hmm, I'm not sure exactly how to replicate, other than to shrink it to 50%,
   leave it for a while, wake up the screen when I come back, and the clock
   has 'grown' back to 100%.

   I've attached a screenshot of what it looks like when I wake up the screen.

   If I then try to manipulate it with Ctrl(0) or Ctrl(+), the window grows/shrinks,
   but the clock image remains out of sync with the window size, the clock always
   being larger than the screen, the clock re-centering inside the window.

   I tried shortening my screen sleep time (DPMS) to 30 seconds and waiting for
   the display to time out, then waking it up, but that /doesn't/ replicate the
   problem.

   I don't have a screen saver configured, just the DPMS timeout that powers down
   the screen after 10 mins, so I'm not sure why there's anything different about
   waiting a long time.

Is it SVG-specific? or only related to GUI scaling?
Could you, please, experiment with test/pixmap_browser + an SVG image or a non-SVG image
and see if scaling to 50% damages either image?

Greg Ercolano

unread,
Jun 6, 2018, 2:41:59 PM6/6/18
to fltkg...@googlegroups.com
On 06/06/18 02:28, Manolo wrote:
> Is it SVG-specific? or only related to GUI scaling?
> Could you, please, experiment with test/pixmap_browser + an SVG image or a non-SVG image
> and see if scaling to 50% damages either image?

Seems to be all FLTK apps, even "buttons" and "pixmap_browser".

I can replicate on two different linux machines; Sci Linux 6.x and 7.x
with the same window manager setup (flwm).

Does the new FLTK scaling code reset to 100% on a particular event message
that isn't keyboard related, where it resets the graphics scaling, but not the window size?

That seems to be what's happening here.

I've opened a bug for this where we can follow up: STR #3475.

It includes a screenshot of buttons + pixmap_browser in 3 sizes,
50%, 100% and 150%.

The "before" image is before I walk away from the machine,
the "after" image is after I wake up the desktop when I return.

chris

unread,
Jun 8, 2018, 6:11:36 AM6/8/18
to fltkg...@googlegroups.com
> And since the whole clock (hands and face) is in vector format, it could
> be scaled "indefinitely" without suffering from raster pixelation that,
> for instance, a PNG image would.
>
> NOTE: I haven't /actually/ made this application's window resizable
> to take full advantage of vector scaling, but it easily could be.

Nice!

I have played a little with your program, adding resize functionality
(which was easy using Fl_Image::scale()) and transparency for the clock
hands.

Hereby I have noticed some inaccuracy with the drawing of the clock
hands rounded cap *at certain angles*, which seems to be nanosvg
related. Also the second hand quit 'wobbles' at the top quarter of the
clock.

Best seen when rescaling to full screen.

I include my version, which has a TEST define, to draw the second hand
at a fixed angle, that shows the first issue quite well. You can
redirect the output of the program to a file, to get the svg data in
order to view it with another program. The svg seems to be ok, so I
conclude it must be a nanosvg rendering issue.

Do you also see these effects?

P.S.: I had to add some '/>' (at the hands drawing lines) to the svg
code, to make it XML well formed, though nanosvg seems not to care..
svg_clock_test.cxx
nanosvg_render_capround.png

Greg Ercolano

unread,
Jun 8, 2018, 11:15:23 AM6/8/18
to fltkg...@googlegroups.com
On 06/08/18 03:11, chris wrote:
> I have played a little with your program, adding resize functionality..

On 06/08/18 02:41, MacArthur, Ian (Leonardo, UK) wrote:
> So, I hacked that in. See attached.

On 06/08/18 08:09, Greg Ercolano wrote:
> Ha, I did the same thing, though we did it slightly differently..


LOL -- well, thanks you guys..
OK, I guess we now we have three versions then ;)

Guess I should have sent an email last night saying I updated it
to save you all the trouble, but it'll be interesting comparing the
differences between the three versions.

I decided to do all the scaling stuff using SVG's translate()/scale()
operations, as I wanted to learn a bit more about how well svg handled
that stuff (it did really well).

Greg Ercolano

unread,
Jun 8, 2018, 12:10:56 PM6/8/18
to fltkg...@googlegroups.com
On 06/08/18 03:11, chris wrote:
> Hereby I have noticed some inaccuracy with the drawing of the clock
> hands (1) rounded cap *at certain angles*, (2) the second hand 'wobbles'

Yes, I did notice 'something' going on, but assumed it was
either a result of my rounding floats to two digits (%.2f),
or an artifact of nanoSVG.

I was so happy/surprised by the overall result, I didn't
really investigate further.

But looking at it large as you say, indeed there is a noticeable
wobble at the center of the second hand as it passes from 10 o'clock
through to the 2 o'clock positions, and it doesn't seem to be due to
the "%.2f".

It's a pretty large wobble.. more than one pixel depending on the scale,
so it might be something weird in SVG's rotational matrix math.

> P.S.: I had to add some '/>' (at the hands drawing lines) to the svg
> code, to make it XML well formed, though nanosvg seems not to care..

OK, thanks -- I see what you did, and merged your /> mods into
my version of the resizable clock on the cheat page here:
http://seriss.com/people/erco/fltk/simplex_svg_clock.cxx

imm

unread,
Jun 8, 2018, 1:11:20 PM6/8/18
to general fltk
Wandering a bit off topic, but regarding the CPU load aspect, I wonder if the "better" option might be to render two images here : 

A "fixed" one for the clock face itself, that we'd only need to redraw if the window resized, and a second one for the hands. 

I imagine both of these rendered into RGBA buffers, then we blit them together into an RGB image (with the hands on top!) and that becomes the widget image.

That is probably less work than "editing" and then re-rendering the full SVG image every frame. I think...

--
Ian
From my Fairphone FP2
   

Greg Ercolano

unread,
Jun 8, 2018, 2:29:07 PM6/8/18
to fltkg...@googlegroups.com
Yes, I could see this being more efficient.

Indeed re-generating the SVG of the clock face each time
is unnecessary; unless re-sized, it only needs to be parsed once.

So resize() would regen the SVG clock face to a 'clock Fl_Box',
and the timer tick could just regenerate the hands on a separate 'hands Fl_Box',
and the two might be able to be rendered over each other.

Or, I could pull out fl_draw_image() to render the two images over one
another, if having two Fl_Box's overlap breaks too many rules.

Manolo

unread,
Jun 9, 2018, 3:12:38 AM6/9/18
to fltk.general
I'd like to show how the (new in FLTK 1.4) Fl_Image::scale() function is relevant here,
to support rescaling of the clock :

1) make the window resizable adding, in main() after win->end() :
     win->resizable(clock);

2) keep the drawing inside the Fl_Box with this, anywhere in the SimplexClock constructor:
    align(FL_ALIGN_INSIDE | FL_ALIGN_CLIP);

3) set the image drawing size to the box size with, in the ShowTime function after the "new Fl_SVG_Image" statement:
     clock_svg->scale(w(), h(), 1, 1);

That procedure works with any kind of image and fits the image to the box, whatever the sizes
of the box and of the image raw data. The procedure is given in the Fl_Image::scale() function doc:

An SVG image has the additional property to be resolution independent. FLTK supports that
by rasterizing the image to the pixel size requested to draw it, independently from the image size
written in the SVG raw data. Thus, the clock is rasterized to a small or large pixel size,
according to the window size (with a strong impact on its cpu usage). On MacOS, the drawing size
of the SVG image is further multiplied by 2 to get the rastering size, to support retina displays.

Manolo

unread,
Jun 9, 2018, 3:36:06 AM6/9/18
to fltk.general

The above post fits the running clock to the changing window size. Since the SVG image follows time
and thus constantly changes, the Fl_Image::scale() call is placed right after construction of the Fl_SVG_Image.

A possibly more frequent situation has an unchanging image placed in a resizable window.
To have the image drawing size follow the changing window size,
reimplement the virtual resize() function of the image-containing, Fl_Box-derived class :

  void resize(int x,int y, int w, int h) {
    Fl_Box::resize(x,y,w,h);
    if (image()) image()->scale(w,h,1,1);
  }

chris

unread,
Jun 9, 2018, 1:58:06 PM6/9/18
to fltkg...@googlegroups.com
>
> Hereby I have noticed some inaccuracy with the drawing of the clock
> hands rounded cap *at certain angles*, which seems to be nanosvg
> related. Also the second hand quit 'wobbles' at the top quarter of the
> clock.
>
I did some debugging to narrow down the problem. After following the
whole processing through nanosvg code I eventually arrived at the
rasterization stage. Up to there everything is well, the rounded cap
perfectly in place (can be seen using the debug code in nanosvgrast.h
line 1435, which when enabled produces an svg file with the computed edges).

Following some intuition I tried probing the NSVG_SUBSAMPLES value,
which is defined in nanosvrast.h line 91 to the value of 5.

When changing this value to 2, all the effects disappear to a huge
degree. Of course this is not the solution, but it seems to indicate,
something might be wrong in nsvg__rasterizeSortedEdges() at line 1126.

Maybe somebody with greater understanding of what is done there can find
that out..

chris

unread,
Jun 11, 2018, 3:19:59 PM6/11/18
to fltkg...@googlegroups.com
>>
>> Hereby I have noticed some inaccuracy with the drawing of the clock
>> hands rounded cap *at certain angles*, which seems to be nanosvg
>> related. Also the second hand quit 'wobbles' at the top quarter of the
>> clock.
>>

I have found another "trial and error change" in nanosvgrast.h, that
seems to removes all these issues completely:

Just change the #define NSVG_FIXSHIFT (nanosvgrast.h line 92) from 10 to 14.

No more wobbling and a perfect rounded cap all along the circle.

But don't ask me why..

imm

unread,
Jun 11, 2018, 6:24:38 PM6/11/18
to general fltk
On 11 June 2018 at 20:19, chris wrote:

> I have found another "trial and error change" in nanosvgrast.h, that
> seems to removes all these issues completely:
>
> Just change the #define NSVG_FIXSHIFT (nanosvgrast.h line 92) from 10 to 14.
>
> No more wobbling and a perfect rounded cap all along the circle.
>
> But don't ask me why..


Well, it's a fixed-point number system, using integer maths: By
default they are using a shift of 10 bits, so assuming we are using a
32-bit integer (which seems like a safe assumption for now...) then we
have (setting aside the effect of sign bits etc) effectively 22-bits
of "integer" part and 10-bits of "fractional" part. (Or, 21-bits of
usable integer range, plus the sign bit...)

By setting FIXSHIFT larger, you are making for more resolution in the
fractional part, but at the cost of less range in the integral part -
something like 17-bits of integral range (plus the sign.)

Now, 17-bits of integral range might well be enough for a lot of
pictures, so maybe that is OK?

In any case, it seems like the additional fractional resolution
improves things in your test (I have not actually tried this test...)
and it does seem, intuitively, that improved fractional resolution
would help with the problem as reported.

Did it need to be as high as 14-bits for the fractional part? I'd
expect to see some improvement for each bit added to the fraction - in
effect each bit added to the fractional resolution ought to (roughly)
half the size of the rounding error (assuming rounding error is the
source of the problem, of course!)

chris

unread,
Jun 12, 2018, 10:22:12 AM6/12/18
to fltkg...@googlegroups.com
> Well, it's a fixed-point number system, using integer maths: By
> default they are using a shift of 10 bits, so assuming we are using a
> 32-bit integer (which seems like a safe assumption for now...) then we
> have (setting aside the effect of sign bits etc) effectively 22-bits
> of "integer" part and 10-bits of "fractional" part. (Or, 21-bits of
> usable integer range, plus the sign bit...)

Of course! The penny drops.. I saw these large coordinate numbers among
my debug statements in nanosvgrast.h, but did not immediately connect
them with the rounding issue. And this rounding issue is quite large
with 2 pixels offset error in a 512x512 image.

> Now, 17-bits of integral range might well be enough for a lot of
> pictures, so maybe that is OK?

I think the value chosen, could also be made runtime dependent on the
image size, to be on the safe side (but maybe with a slight performance
penalty). And with 64 bit systems there would always be plenty of range
left, so it at least for these systems the default could be higher.

> Did it need to be as high as 14-bits for the fractional part? I'd
> expect to see some improvement for each bit added to the fraction - in
> effect each bit added to the fractional resolution ought to (roughly)
> half the size of the rounding error (assuming rounding error is the
> source of the problem, of course!)

You are very probably right. It improved bit by bit and was optimal (for
this picture at least) when reaching 14.

So do you think, it would be worth filing a STR for such a change?

Albrecht Schlosser

unread,
Jun 12, 2018, 12:43:40 PM6/12/18
to fltkg...@googlegroups.com
On 12.06.2018 16:22 chris wrote:
>> Well, it's a fixed-point number system, using integer maths: By
>> default they are using a shift of 10 bits, so assuming we are using a
>> 32-bit integer (which seems like a safe assumption for now...) then we
>> have (setting aside the effect of sign bits etc) effectively 22-bits
>> of "integer" part and 10-bits of "fractional" part. (Or, 21-bits of
>> usable integer range, plus the sign bit...)
> ...
>> Did it need to be as high as 14-bits for the fractional part? I'd
>> expect to see some improvement for each bit added to the fraction - in
>> effect each bit added to the fractional resolution ought to (roughly)
>> half the size of the rounding error (assuming rounding error is the
>> source of the problem, of course!)
>
> You are very probably right. It improved bit by bit and was optimal (for
> this picture at least) when reaching 14.
>
> So do you think, it would be worth filing a STR for such a change?

I think if it's a general problem, then yes, an STR with a patch would
be very good as a reminder.

Then you could also open an issue at the maintainer's GitHub site [1] to
see how that works out and if there is a chance that your
proposal/changes might be accepted upstream. This way we would get them
back "automatically" when we update our internal copy [2].

If there is a chance for acceptance upstream you could also fork their
GitHub repo (just checked: there are 120 forks!), push a patch to your
clone, and issue a pull request. This could speed up the process, maybe.

[1] https://github.com/memononen/nanosvg

[2] I made "our" own (FLTK's) fork to check in our modifications for
easier maintenance at: https://github.com/fltk/nanosvg . GitHub says:
"This branch is 6 commits behind memononen:master". Well, that means I'd
have to check this and pull in the changes...

chris

unread,
Jun 14, 2018, 2:12:15 AM6/14/18
to fltkg...@googlegroups.com
Am 12.06.2018 um 18:43 schrieb Albrecht Schlosser:
>>
>> So do you think, it would be worth filing a STR for such a change?
>
> I think if it's a general problem, then yes, an STR with a patch would
> be very good as a reminder.
>

Done: http://www.fltk.org/str.php?L3476

> Then you could also open an issue at the maintainer's GitHub site [1] to
> see how that works out and if there is a chance that your
> proposal/changes might be accepted upstream. This way we would get them
> back "automatically" when we update our internal copy [2].
>
> If there is a chance for acceptance upstream you could also fork their
> GitHub repo (just checked: there are 120 forks!), push a patch to your
> clone, and issue a pull request. This could speed up the process, maybe.

This sounds like a lot of work, with uncertain out coming, which I
decided not invest at the moment..

Reply all
Reply to author
Forward
0 new messages