Please can we have more graphical stuff!

It one of the ways you can export from Spritely.

I would love to see more focus on vector graphics functions. The current set are very limited and the antialiasing does not seem to work. For example, if you turn on smoothing in my Spline demo (http://npryce-codea.posterous.com/splinetastic) the spline curve breaks up. I’d expect the lines to join up and be smoothed along their length. Likewise with antialiased rectangles: if you draw them next to one another they don’t abut properly.

How about taking an existing 2D vector drawing library, such as Cairo (http://cairographics.org/) and using that as the basis for Codea’s graphics API?

@Nat Cairo is pretty heavy weight, and I suspect its experimental OpenGL backend would be quite hard to port to OpenGL ES 2, which is used by Codea on iPad.

We really want to improve the vector shaders currently used in Codea, @Dylan has mentioned he’s going to re-work the line shader.

Note that splinetastic looks better if you use lineCapMode(PROJECT) instead of SQUARE. There are still artefacts, but these are due to the line caps overlapping. With a strokeWidth of 5 it looks pretty good. The AA is not “wrong” in this instance, it’s just that image intensity increases when AA’d edges overlap.

The reason it works with noSmooth() is because OpenGL lines are capped using a shearing method (they are just clipped on the left and right edges, basically). This means you get inconsistent line caps when you move the end points, but lines will always abut perfectly.

We could add a lineCapMode(SHEAR) to the smooth() line drawing shader. It would not anti-alias the abutting edges, only the long edges of the line. This would then be the ideal mode to use to render multi-segment shapes, like splines.

This would then be the ideal mode to use to render multi-segment shapes, like splines.

If I understand correctly, then this wouldn’t be the case. Joining multi-segment paths is complicated because they can join at arbitrary angles. Simply taking two paths that end at the same point is almost never going to work, no matter how much one shortens or extends the paths. The best way to join multi-segment paths without doing anything complicated is to use rounded ends (but these don’t look great at the moment in Codea, unfortunately).

I’ll post some screenshots later of my experiments with this.

In the case of a sheared line cap, all line segments would join, wouldn’t they? At least how I visualise it. The splinetastic example on noSmooth() demonstrates this.

Here’s a crude image illustrating what I mean:

Shear Line Cap Mode

Round wouldn’t work because the AA intensity on the round end-caps would increase the image intensity in those areas. While Codea does have issues with ROUND line caps (namely that the caps appear half a pixel larger than the line), the AA issue could not be avoided. It just occurs when you stack AA’d edges on top of each other.

With your image, now imagine what would happen if you “doubled back”. Try drawing a triangle with that mechanism and compare the three vertices. If you draw a “standard triangle” (flat base, apex lying above the base) then the apex will be pointy but the two vertices at the base will not be.

By “rounded” I meant in an “ideal” situation, where the path is constructed first and then rendered as a single object, so then all the alphas are combined in the correct manner rather than just lain on top of each other. I don’t see any way to do a nice joining of segmented paths by simply lying lines on top of each other without a really complicated set of “line ends” (some of which would involve shortening the lines).

Here’s some samples from my experiments.

Hexagon

Filled Hexagon

The corners in the hexagon are rounded (this is only noticeable on the outer edge), those in the filled hexagon are sharp. Both have slight blurring at the edges for a smoother look (which is what I consider antialiasing to be, but as I’ve never actually looked at the definition I may be wrong).

Both are constructed by working pixel-by-pixel on an image and then rendering the image to the screen. So both are proper shapes and not lines overlaid on top of each other. As they are images, they react properly to tinting, including transparency.

However, they take a loooooong time to create, of the order of a minute, so any sort of dynamism (beyond simple translation) is out (admittedly, the time is quadratic in the size of the image and these are particularly large ones for demonstration purposes).

The purple line in the hollow hexagon, incidentally, has proper rounded ends.

I see what you mean. Sheared ends would allow for reasonably nice joining in quite a few cases, though. (Like splines).

I’m not too concerned with getting the vector rendering perfect - for example, stroked ellipses use a subtraction method in Codea, which is technically incorrect. It would just be too expensive to do them properly. The vector graphics are there to make it easy to visualise things. We will work on allowing more types of things to be visualised (triangles, polygons) before we improve the accuracy of the vector graphics.

That said, lines definitely need an upgrade in quality and accuracy. That’s important and will happen.

Sheared ends work whenever the x-derivatives of the paths that are joining are in the same direction. Unfortunately, for a closed path then that’s guaranteed to fail a couple of times!

I’m quite happy with imperfect and if you do polygons (stroked and filled, irregular and not-necessarily-convex) then I might just shut up about this … for a day or so!

(Now that’s got to be an incentive.)

Polygons are tricky. Our plan is to do them as follows:

  • Tesselate the polygon into triangles using a convex decomposition algorithm
  • Render the non-antialiased polygon triangles
  • If smooth() - Render an antialiased outline using line-segments (in the stroke colour if stroked, in the fill colour if not-stroked)

I can’t think of a “clean” way to do it using shaders. Given that all the information we have to work with is a fragment location linearly interpolated between texture coordinates of triangle vertices.

I’m afraid I don’t really know what a shader is (and wikipedia wasn’t very enlightening). In doing the filled polygon above, I did the following: I iterated over each pixel and for each computed the dot product of the coordinate with the normal to each of the lines. That told me both what side of the line it was on and how far away it was. If it was on the wrong side of one line, and more than the line width (plus a few) pixels away, it got an alpha of 0. If it was between a line width and a line width plus a few pixels, it got an alpha somewhere between 0 and 1. Otherwise it got an alpha of 1. That produced the filled polygon.

For the hollow polygon, I used the absolute value of the dot product to figure out the distance to a line segment, plus a few other dot products for endpoints.

If any of that would be useful, I can post the actual code. But it may be way off what you would actually need to do.

(The filled routine works only on convex shapes, but that would fit with your algorithm.)

@Andrew it’s similar to what you describe. Except a shader has to draw each triangle independently. It is possible to do it nicely but it requires telling the shader which edge(s) of each triangle is an “outer” edge. Unfortunately the more instructions we include in a shader, the slower the rendering speed. So we try to keep them as simple as possible.

Unfortunately the more instructions we include in a shader, the slower the rendering speed

True, but then all the slower if one of us tries to write a version in lua instead!

Is it possible for the information from each triangle to be combined in some other way than just “last instruction wins”? If it is possible to take the max or min of the new alpha with the current alpha then it might be possible to work out a routine that can cope with the triangles independently which combines to give a reasonable solution for the whole shape.

Not really. Not in the shader (at least not without rendering the existing screen into a texture and passing that as a parameter into the shader). You can tell OpenGL how to blend the new pixel on the screen, but only in a very primitive way: http://www.opengl.org/sdk/docs/man/xhtml/glBlendFunc.xml (this would apply to the entire primitive, i.e. all the triangles in the polygon).

OpenGL is a very low level API, designed to mimic the graphics hardware closely. I think for the time being we will focus on making primitives available. Accuracy can always be improved later - look at Processing’s model, they have different rendering backends you can switch between to go for speed or accuracy.

Making the primitives available sounds like a good plan. Then if someone does come up with a super-wizzy way of doing something that would be good to have, you can recode it in whatever-language-this-is.

Andrew, google for GLSL. Dylan was teasing at one point about exposing it to us - it is actually a total other language for expressing pixel transforms and shading. It’s meant for 3d type stuff, but just like any language “meant for” something else - I have Conway’s Life implemented in it. It’s also exposed in most modern browsers in JavaScript, demo pages that let you tweak the code are easy to find.

See also my Pic class library for routines to draw a filled not-necessarily-convex polygon, hopefully fairly quickly. Speed is dependent on volume because we’re per-pixel plotting (the code predates setContext() - I suspect it could be sped up by 2 orders of magnitude or more by using line() )

What we need is a “fast” filled triangle. I haven’t worked out the math yet, but the pseudo code slowly forming in my brain says:

Make an image. Set context to that image. Draw a rect such that it is bisected diagonally by the edge of the image. Rotate, transform, and scale to the correct place and draw with sprite. (can we scale in only 1 direction? If not, we might also need clip() in there)

The filled hex above should be, ideally, a rect and 2 triangle draws.

See http://www.iquilezles.org/apps/shadertoy/

You’ll need a recent browser and graphics card (in the last couple of years) - this is available on the ipad, but not exposed in mobile safari, so the above link is desktop only, unfortunately.

(History: The S in GLSL is for “Shader”, because originally it was for shading only. One of the amusing things about people making a language “for METAVARIABLE only” is that inevitably it’s used for things other than METAVARIABLE. Is why I’m amused/annoyed/concerned when someone says “well, Codea is meant for METAVARIABLE only”)

I keep ranting about the GLSL Life implementation…

True story: I’m at work a few months back, thinking of almost anything but work (as does happen), and I came to the sudden realization that because in Conway’s Life the state of a pixel depended only on it’s immediate neighbors, and because the calculation of a generation was inherently something that could benefit from parallel processing, the GLSL would be an ideal place to implement it! So I dove in to start to do that - and found out my keen insight is (1) valid, and (2) about 3 years after the exact same keen insight by many other people, likely independently. It’s just an obvious fit, in hindsight.

So - found this: http://mikeash.com/software/gpulife/

and the itch was scratched. Very pretty.

If you don’t use OS X, there are versions of this for… well, basically any platform that supports it.

I really don’t get GLSL and shaders, but I suspect that I will get an idea once they’re available and I can play with them (I’m that kind of learner).

I did look at your Pic stuff, and it’s great for sharp edges but not for smooth edges as the thickness of the blur depends on the angle of the line and the algorithm that you use doesn’t take that into account.

I like your idea about drawing triangles as rotated rectangles, although it would only work for right-angled triangles. I guess obtuse would be okay as well, but an equilateral triangle would need two of these things. Hmm, it would need careful decomposing to ensure that ones arbitrary polygon were split into triangles with right angles.