drawing thick lines

I need some thick textured line segments. Actually I need curves made out of short line segments. Been messing around for a couple days and I’m start to lose my freaking mind. Here is what I have:

http://n4te.com/temp/lines.png

In the upper left is an example of two connected line segments. Below that shows once the lines are given width, they overlap. If I apply a texture that uses translucency, the overlap looks terrible. Below that shows what I’ve come up with, I move both segment endpoints away from each other half the amount necessary to make the thick line corners just touch. This way I can fill the space between the lines with a triangle. On the right you can see this works well (ignore the horizontal line when the crappy texture repeats).

But it doesn’t always work well. In the bottom left there are many short line segments, to represent a sharp curve. Note the curve is jagged along the edges. So many triangles so close together causes some strange anomalies, I guess from triangles just barely overlapping. I’ve played around with this a lot, but I’m having a tough time preventing this.

Is there a better/easier approach than what I’m doing? I assume this has been solved many times by others, but I’m not able to find much online that is applicable. I guess it is called line tessellation or stroke tessellation or some such thing. I would appreciate any help you might provide!

I’ve done similar stuff and run into similar problems, but didn’t come up with a clear solution. I reckon it’s just a fundamental limit that your line can’t turn tighter than the perimeter of the circle with the diameter of your line thickness.
This webstart show what my code does. Click to add a point, hit 1, 2 and 3 to change the join and cap decorations, Q and A to change line thickness, W to toggle wireframe, L to toggle between line loop and strip, and right-click to remove the last point added. Things will get ugly (back facing polygons etc) with thick lines and sharp corners.

How are you calculating your texture coordinates?

Looks like an interesting problem. What graphics library are you using Nate? LWJGL or java2D? I only ask because with LWJGL you should be able to stretch the texture over the triangle points to make it look pretty smooth and avoid the bumpyness on the corners.

@Bleb Nice demo. Can’t you just kill those weird gray polygons that face backwards? Otherwise it looks perfect.

By the way, Java2D’s implementation of thick strokes aren’t very good either. There are often jagged corners, but I’m not sure if it’s a software bug or a D3D driver bug.

If you exceed the limit of smooth turning you could maybe just fill under with a circle? Explanation: this is a cheat, but if you plot circles in a tight situation it will be graphically correct, albeit x slower.
If this thing is going fast, nobody will notice. If you want it perfect for screenshots even in tight situations, you may have to handcraft the extreme angle situation to be ‘pretty’

Also there seems to be some kind of zigzag thing going on in the red lines, why is that?
If you are doing this with triangles on the fly its more like a harpsichord than segments…

Doing so while avoiding overdraw is becomes extremely complex.

When I did something similar (but less complex) I think I handed the really tricky stuff off to OpenGL’s tesselation.

Yeah, that’s what I would suggest doing.

Very nice demo bleb! Your approach seems to handle tight situations better than mine. Any chance you would share your code? I would gladly submit back if I can manage any improvements.

Here is my (ugly ass) code:
http://pastebin.com/d6XNdxnj
It is a mixture of Slick and LWJGL. I’ve been trying a bunch of things so it isn’t very pretty nor optimized. My needs only require straight horizontal lines and a specific bezier curve.

Got an exception on some weird corner case with your JWS, bleb:

[quote]java.lang.ArrayIndexOutOfBoundsException: 0
at com.rugl.geom.line.MiterDecoration.getFarthestIntersection(MiterDecoration.java:108)
at com.rugl.geom.line.MiterDecoration.createVerts(MiterDecoration.java:61)
at com.rugl.geom.line.Line.corner(Line.java:458)
at com.rugl.geom.line.Line.buildLine(Line.java:137)
at com.ryanm.glscratch.LineTest.loop(LineTest.java:188)
at com.ryanm.glscratch.LineTest.execute(LineTest.java:77)
at com.ryanm.glscratch.LineTest.main(LineTest.java:39)
[/quote]
CommanderKeith, yeah, I’m stretching a texture over it. The problem is if any triangles overlap, causes jagged edges and/or messed up texturing.

Karmington, I’m not sure what you mean? You can only really draw triangles. You mean to use a texture with a circle on it? The problem is with translucent textures, nothing can overlap. The zigzag is the triangle strip being rendered as a line strip.

I will play around with the GLU tessellation stuff.

The GLU tessellator rocks! Tracing the outline of a polygon is way easier than working out the triangles manually. I just trace the outline of my thick line and I’m done – none of the output triangles overlap. It works in all cases. Sweet!

I take it all back. Once I got the texture mapped on the line, I found it does well for most lines, but a sharper curve distorts the heck out of the texturing:

http://n4te.com/temp/lines2.png

Hopefully this would be fixed if I applied the weights properly. Here is my combining callback:


public void combine (double[] coords, Object[] vertexData, float[] weight, Object[] outData) {
	for (int i = 0; i < outData.length; i++) {
		float[] combined = new float[4];
		combined[0] = (float)coords[0];
		combined[1] = (float)coords[1];
		float[] data = (float[])vertexData[i];
		// combined[2] = data[2] * weight[2];
		// combined[3] = data[3] * weight[3];
		combined[2] = data[2];
		combined[3] = data[3];
		outData[i] = combined;
	}
}

I’m ignoring the weight. If I use the commented line (or the many other variants I’ve tried), the texture goes nuts (worse than the pic above). How should I apply the weight? The data variable contains: vertex x, vertex y, texture x, texture y. Why does the weight array have a length of 4?

All of my code is over here. Specifically, you’ll be wanting the stuff in here.

Cheers for the bug report, fix has been applied :slight_smile:

I still get Exceptions in your JWS when only moving the mouse: “Zero vector length”

???
Got a stacktrace for that? Searched my workspace for “zero” text matches but didn’t turn up anything useful.

It’s in LWJGL code.


java.lang.IllegalStateException: Zero length vector
	at org.lwjgl.util.vector.Vector.normalise(Vector.java:91)
	at com.rugl.geom.line.Line.end(Line.java:379)
	at com.rugl.geom.line.Line.buildLine(Line.java:171)
	at com.ryanm.glscratch.LineTest.loop(LineTest.java:188)
	at com.ryanm.glscratch.LineTest.execute(LineTest.java:77)
	at com.ryanm.glscratch.LineTest.main(LineTest.java:39)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at com.sun.javaws.Launcher.executeApplication(Unknown Source)
	at com.sun.javaws.Launcher.executeMainClass(Unknown Source)
	at com.sun.javaws.Launcher.doLaunchApp(Unknown Source)
	at com.sun.javaws.Launcher.run(Unknown Source)
	at java.lang.Thread.run(Unknown Source)


Sweet, bleb! Thanks! I’m digging into it now. First impressions of your project went like this… checked out trunk, erm, trunk is empty. Went back to the site, ah, everything is in the root (SVN conventions have trunk/branches/tags in root). Ok, get everything, nice they have Eclipse project files, import into Eclipse, compilation errors, missing a project called “lib”. Ok, grabbed latest LWJGL, added JARs to Util, checked to export them, complains about XML something or other, deleted NanoXMLFormatter, hacked ConfigurationSerialiser, and I’m up and running. :slight_smile:

First thing, I’ll see if I can plaster a texture on your lines… urgh, how to load a PNG into an Image to make a Texture so I can use TexturedShape…

Fixed, cheers! Not at all sure why it was an immediate fail for you but works fine for me though.

Good catch, it’s in the process of adding to the SVN now. Hrm, seems to have hung with 100% CPU actually…

Just hover your previous point, it will crash, naturally.

Couldn’t trigger this when hovering over the previously placed point (this is checked for explicitly), but it does happen when you click twice in the same place consecutively. Fixed now, cheers!
It always astonishes me that you can have code that you’ve been using trouble-free for ages, thinking it’s pretty solid. Give it to another person and it’s guaranteed to explode into greasy nuggets of utter fail.

Well, bleb’s code is very nice indeed. However, it basically does what my code does: shortens the lines based on the width to avoid overlapping. It doesn’t support texturing the line though, and that has proven difficult to shoehorn in.

My code is close. It works most of the time, just when the curve is short and vertical, it freaks out. I’m at my wits end after 3 days of messing with freaking lines. :slight_smile:

Edit: Worked it out. :slight_smile: I was fixing the start and end of the lines to be vertical, allowing it to rotate with the line reduces the angle the curve needs to bend. So I didn’t really solve it, I’m not sure it is solvable, but I no longer have the problem, so… cool! ;D