matomato
matomato

Reputation: 21

Fill triangle with 3 color gradient in Java

I was not able to find a solution for this problem. I would like to paint a filled triangle in Java with gradient where each corner has different color. Something like this:

enter image description here

I found some posts online but I was not able to figure out how to do the gradient in Java. The problem is that in Java you can do GradientPaint only from one color to another, which is not suitable to fill a triangle.

So far I have come up with this code which does not work as expected:

triangle.p1().getValue();
Color color1 = calculateColor(triangle.p1().getValue());
Color color2 = calculateColor(triangle.p2().getValue());
Color color3 = calculateColor(triangle.p3().getValue());
Color transparent = new Color(0, 0, 0, 0);
Polygon polygon = new Polygon(
        new int[]{(int) triangle.p1().x(), (int) triangle.p2().x(), (int) triangle.p3().x()},
        new int[]{(int) triangle.p1().y(), (int) triangle.p2().y(), (int) triangle.p3().y()},
        3);
GradientPaint gradient1 = new GradientPaint(
        (float) triangle.p1().x(), (float) triangle.p1().y(), color1,
        (float) triangle.p2().x(), (float) triangle.p2().y(), transparent);
GradientPaint gradient2 = new GradientPaint(
        (float) triangle.p2().x(), (float) triangle.p2().y(), color2,
        (float) triangle.p3().x(), (float) triangle.p3().y(), transparent);
GradientPaint gradient3 = new GradientPaint(
        (float) triangle.p3().x(), (float) triangle.p3().y(), color3,
        (float) triangle.p1().x(), (float) triangle.p1().y(), transparent);
graphics2d.setPaint(gradient1);
graphics2d.fill(polygon);
graphics2d.setPaint(gradient2);
graphics2d.fill(polygon);
graphics2d.setPaint(gradient3);
graphics2d.fill(polygon);

Here is the result I am getting: points A and B have the same color but the triangles adjacent to AB have different color

Some threads that mention similar thing: Triangle Gradient With Core Graphics and Java 3 Color Gradient

Upvotes: 2

Views: 3742

Answers (2)

hageldave
hageldave

Reputation: 63

I realize that this is an old question, still the issue has not been satisfactorily answered.

The problem is that there is no appropriate java.awt.Paint implementation that supports interpolating color within a triangle where colors are given at the triangle vertices. This is a common thing in computer graphics (rasterization) where barycentric coordinates are used to determine the mixing weights for a point within the triangle (see https://www.khronos.org/registry/OpenGL/specs/gl/glspec14.pdf section 3.5.1).

Since I also could not find any implementation of such a Paint implementation for java.awt.Graphics2D around the web, I implemented it myself. You can find it here (titled BarycentricGradientPaint ): https://gist.github.com/hageldave/391bacc787f31d2fb2c7a10d1446c5f6

A screenshot for reference, notice how the second triangle is partially transparent and edges are anti-aliased. AA is realized through a 4x MSAA approach.

screenshot of the demo of the paint implementation

Upvotes: 2

gpasch
gpasch

Reputation: 2682

This is based on the idea that if you pick any color inside the triangle it will create three areas from the three points of the triangle. Therefore we are extendng the principle of linear interpolation

color=(distance to p1)/(distance p1, p2)*c1+(distance to p2)/distance(p1, p2)*c2;

to the 2-D plane. Thus the weighting coefficents will be areas:

public int areaTriangle(int x1, int y1, int x2, int y2, int x3, int y3) {
  return (int)(0.5*Math.abs((x1-x3)*(y2-y1)-(x1-x2)*(y3-y1)));
}

BufferedImage b=new BufferedImage(500, 500, BufferedImage.TYPE_INT_RGB);
Polygon pl=new Polygon();
pl.addPoint(100, 100); pl.addPoint(200, 150); pl.addPoint(150, 200);
Rectangle r=pl.getBounds();
int a=areaTriangle(pl.xpoints[0], pl.ypoints[0], pl.xpoints[1], pl.ypoints[1], pl.xpoints[2], pl.ypoints[2]);
int[] c1={255, 0, 0}, c2={0, 255, 0}, c3={0, 0, 255};
for(i=0; i<r.width; i++)
for(j=0; j<r.height; j++)
  if(pl.contains(r.x+i, r.y+j)) {
    int ix=r.x+i, jy=r.y+j;
    int a1=areaTriangle(ix, jy, pl.xpoints[0], pl.ypoints[0], pl.xpoints[1], pl.ypoints[1]);
    int a2=areaTriangle(ix, jy, pl.xpoints[0], pl.ypoints[0], pl.xpoints[2], pl.ypoints[2]);
    int a3=areaTriangle(ix, jy, pl.xpoints[1], pl.ypoints[1], pl.xpoints[2], pl.ypoints[2]);

    int[] c=new int[3];
//      for(l=0; l<3; l++) c[l]=(int)((1-1.0*a1/a)*c1[l]+(1-1.0*a2/a)*c2[l]+(1-1.0*a3/a)*c3[l]);
    for(l=0; l<3; l++) c[l]=(int)((1.0*a1/a)*c3[l]+(1.0*a2/a)*c2[l]+(1.0*a3/a)*c1[l]);
    b.setRGB(ix, jy, 0xff000000|(c[0]<<16)|(c[1]<<8)|c[2]);
  }

If you try the commented line you will get the three complementary colors.

Upvotes: 2

Related Questions