-2

I am creating my own 2D paint engine that is able to stroke and fill paths. I am having trouble with the corners of line segments connecting.

The way I do it is calculate the normal of each line segment and in the corners, I use the average normal of the two adjacent edges. To calculate the vertices for the thickness of the line, I use the line segment coordinate and subtract/add the average normal multiplied by half the line thickness.

sketch of what I am trying to do

On the sketch, this means that I calculate the light blue normals (I use them for the free line endings too, except when it's a loop) and calculate the average of the two for the corner, which is the red normal (and the green is the negative version).

I scale the normals with a 1/2 thickness (orange shape) to get the vertices.

Although the direction of the red/green normals are ok, the magnitude calculation isn't correct for the corners.

This looks quite nice, but not good enough: depending on the angle of the corner, the line gets thinner/thicker than the requested thickness.

So taking the average normal doesn't seem to be enough.

This is how it looks: look at how bad the corners look... can anyone give me tips on how to improve this?

screenshot of the varying line thickness

p.s. no need to tell me to use a 3rd party library and forget about this problem

scippie
  • 2,011
  • 1
  • 26
  • 42
  • Which language? Which technology? Please update the tags to call the right persons. –  May 24 '20 at 15:04
  • What does the language and technology matter? It's C++ and OpenGL, but this question is exactly as valid on Visual Basic with Vulkan (if that's even possible). But to get the attention, I will add C++ and OpenGL. – scippie May 24 '20 at 17:42
  • Can you post an image *without* corners handling? Just to see where your issue comes from. – Ripi2 May 24 '20 at 17:51
  • I can't really show you an image without the corners handling because I am drawing the lines with triangle strips. Thus the corners are shared vertices and will be correct for only one of the two lines coming together if not calculated for both of them. – scippie May 24 '20 at 18:17
  • If you are drawing each segment as a strip of triangles, then I thing you should modiy vertices moving them to intersections of borders of segments. Some code you have not posted yet may help. – Ripi2 May 24 '20 at 18:33
  • Wow, that actually makes a lot of sense. I keep thinking that a unit normal should suffice and should always be scaled correctly, but maybe that's incorrect (can anyone collaborate?). Using the edge normals and then finding the intersection points at corners will definitely give the correct result because single lines are always drawn correctly. Thanks for the eye opener! – scippie May 24 '20 at 18:35
  • Actually, now I am starting to see that the average normal is indeed incorrect. Draw it on paper: to have the average normal stretch as far as the edge normal, you need to make it longer (or shorter). I feel that this must be related to the angle (dot product?) between the two edges, longer for big angles, shorter for small angles. – scippie May 24 '20 at 19:04

2 Answers2

0

Your description of the mathematics is unclear, but I think the factor you're looking for is one over the sine of the angle between the two edges (which you can get with the cross product).

Watch out for angles near zero or pi; decide what you want to do about such angles before you encounter them.

Beta
  • 96,650
  • 16
  • 149
  • 150
  • I added a sketch and some extra explanation of what I am doing in my OP. – scippie May 25 '20 at 06:21
  • This sounds right, but can you tell me how to do it? I just tried calculating the cross product (B.x * A.y - A.x * B.y) between the two and multiplied it with the half thickness, but the result was terrible... – scippie May 25 '20 at 06:24
  • @scippie: What are the numerical values of the blue vectors and the half-width? – Beta May 25 '20 at 14:19
  • Ehm... there are hundreds of connected line segments. I can pick out two, but it won't really matter I think. Say A has a normal of (-1, 0) and B has a normal of (0, 1), and thickness = 1.0, then the average will be (-0.707, 0.707) after normalization. If you draw this on paper, you see the effect I have in my OP: the line will go from 2x0.5 wide to 2x0.3535 wide. – scippie May 25 '20 at 18:52
  • So, taking the intersection point as the origin (0,0), the sum of the normals is (-1, 1), and the scaling factor is (w/2)/(Bx Ay - Ax By) = (1.0 / 2)/(0-(-1)) = 0.5, so the corner vertices are (-0.5, 0.5) and (0.5, -0.5). The lines have width 1.0 and the distance between the vertices is sqrt(2). – Beta May 25 '20 at 19:54
  • You mean I shouldn't have averaged the normals, but just add them and not normalize the end result? – scippie May 25 '20 at 20:11
  • I just now solved the problem by calculating the intersection points of the extruded edges. I may still look into your answer as well as I think it is computationally less intensive. – scippie May 25 '20 at 20:14
0

I used ripi2's answer to switch from trying to get the correct normal length to calculating the intersection points of the extruded edges and clipping the lines to that intersection point.

And that actually works! Although Beta's answer might be better as it looks less computationally intensive. I might look into that later.

The result looks nice:

enter image description here

scippie
  • 2,011
  • 1
  • 26
  • 42