memononen / nanovg

Antialiased 2D vector drawing library on top of OpenGL for UI and visualizations.
zlib License
5.16k stars 774 forks source link

Proposal: Add FillPreserve/StrokePreserve, kill BeginPath. #61

Open paniq opened 10 years ago

paniq commented 10 years ago

At the moment, drawing is done via Fill/Stroke, which both preserve state; The user is expected to call BeginPath to clear the current path. This becomes a perpetual nuisance, as calling BeginPath is easy to forget, so paths may be redundantly redrawn, and the mistake is very hard to detect if the paint doesn't change; Additionally, calling BeginPath turns out to be quite annoying if the path needs to be cleared after each operation, which is the usual case.

Additionally, one would expect that BeginPath has a matching EndPath, much like BeginFrame/EndFrame, but it's missing; this is confusing/surprising.

Therefore I would like to suggest following changes in the API, in the same way the Cairo API solves this, which has worked very well for me over the past years:

  1. nvgFill is renamed to nvgFillPreserve, nvgStroke is renamed to nvgStrokePreserve. The functionality stays the same.
  2. nvgBeginPath becomes an internal function and is removed from the header.
  3. A new nvgFill implementation calls nvgFillPreserve, then nvgBeginPath; Likewise, the new nvgStroke implementation calls nvgStrokePreserve, then nvgBeginPath.

It would optionally be possible to rename nvgBeginPath to nvgClearPath and still keep it around, for situations where paths are started but then need not be drawn (for whatever reason), and for cases where existing users just want to do an 1:1 update of their code with a simple rename job.

At this point I should say that I'm totally fine with my proposals being shot down. It's just been things on my mind while I wrote the bindings, and I am also thinking about people (like me) who consider porting code over from cairo. The library is young and I figured this would still be a good time for basic API improvements.

Ziflin commented 10 years ago

We had some similar issues and came up with the following changes as we're drawing more 'full' shapes than arbitrary paths:

So normally now, we just have to call: Rectangle( ... ); Fill( ... ); or Stroke( ... );

to further reduce the number of calls required to draw, we moved the paint-related items into a "Pen" and "Brush" (similar to WPF Graphics class) struct. So we should really only need to do: SetTransform( ... ); Rectangle( .. ); Fill( myBrush ); Stroke( myPen );

So far the only issues that I've seen are that the paint's transform must be transformed per Fill/Stroke call instead of per SetFillPaint/SetStrokePaint, but is seems rare that we would be drawing the same transformed shape with the same paint more than once. (?)

This was all just something to make life easier for us. The code's great though!