0

I have a triangle with a value attached to each node (e.g. (A,B,C) -> (0.3, 0.1, 0.7)). The values are mapped to an RGB color with the following function (0.0: blue, 0.5: green, 1.0: red):

var color =  Color.FromArgb(alpha,
    (int)((value > 0.5 ? 2*value - 1 : 0) * 255),
    (int) ((value > 0.5 ? 2-2*value : 2*value) * 255),
    (int)((value > 0.5 ? 0 : (1 - 2 * value)) * 255)
);

These values are meant to be interpolated linearly on the triangle (e.g. weight by barycentric coordinates) and it's a larger mesh of triangles in the full application.

For drawing, I create a GraphicsPath with points on each of the three corners and several points in between (12 in the example which is probably overkill). On a PathGradientBrush, I set the SurroundColors colors to either the values of the corners or a linear interpolation between those. The CenterPoint and the CenterValue are set to (A+B+C)/3 and values.Sum()/3. This looks good for some values (here (0.0, 0.5, 1.0):

good looking example

but not so good for others (here (0.3, 0.8, 1.0), see the artifact at the top of the triangle):

not so good looking example

I tried several other strategies (e.g. weighting the values by distance to the center point, placing the center somerwhere else, etc.) but still getting those artifacts. I only need triangles but the drawing must happen in a Paint-handler and painting manually is too slow. How can I

  • either get rid of the center color and get a colorized triangle like in OpenGL
  • or get a center value and/ or position which doesn't lead to those artifacts.

Code to reproduce the issue and play around (a bit too much to paste it into the question): http://pastebin.com/gjjzqRvY

Peter Schneider
  • 1,683
  • 12
  • 31
  • I don't think LGBrush can do this. See [here for limits and a solution](http://stackoverflow.com/questions/30339553/fill-panel-with-gradient-in-three-colors/30341521?s=2|0.2326#30341521) – TaW Oct 21 '16 at 17:46
  • @TaW: it's not possible in general but possible for this specific use case. See my answer below. – Peter Schneider Oct 21 '16 at 18:22

1 Answers1

0

I can't remember when I hadn't answered my own question the last time.

PathGradientBrush does a linear interpolation of the color between

  • the surrounding points -> every point on the boundary has a color asigned
  • the interpolated color on the boundary and the center point

    This plays nicely with the following color mapping:

    var color = Color.FromArgb(alpha, (int)(value * 255), (int)((1-value) * 255), 0 );

For example: if an edge goes from 0 to 1, the interpolation of the brush would calculate for 30%:

0.3 * colorA + 0.7 * colorB
= 0.3 * (0, 255, 0) + 0.7 * (255,0,0)
= (76, 178)

which is exactly the result of the colormapping-function for 0.3. The same applies for the second interpolation towards the center.

But I had 3 colors but luckily only one extrem point at 0.5. 0.0 .. 0.5works fine and 0.5 .. 1.0works fine. But this is were the linear interpolation breaks down. The trick is to give the gradient brush this extrem point:

  • the minimum and maximum value of the corners do not cross the magic point, just go with the center value.
  • if they cross, find the edges and the point on them where they do that. There are always two. The center point for the brush is at the mean of them and the value is the extrem point.

hint for future readers:

  • if your mapping is not linear (square roots, sin/cos, log, ...) you are out of luck
  • if your mapping has more than 1 extrem point you are out of luck
  • 3 (not crossing) or 5 (crossing) points on the path are enough. Set the additional 2 points to the crossing points.
Peter Schneider
  • 1,683
  • 12
  • 31