Coloring a triangle "naturally" using SVG gradients
I have 3 points colored H1, H2, and H3, where each Hi is has 100% saturation and value, and only the开发者_Go百科 hue varies. In other words, these are "rainbow" colors.
I want to use SVG's gradient function to color the triangle "naturally". In other words, points close to H1 should have hue H1, the coloring should be continuous, etc.
Is this problem well-defined? Is there really such a (unique?) coloring?
Minor: I don't consider hues to "wrap around". In other words, the color between hues .995 and .003 is .499, not .999.
If this problem has a solution, can it be extended to "naturally" color the convex hull of any set of colored points on the plane, using Delaunay triangulation?
This thread is long-dead, I realize. But I post this answer in hopes that it will be useful for someone in the future. If you can extend the equations into the right SVG markup, then we've done it. I developed this particular solution for cocoa, but the math is entirely relevant.
The approach involves a little matrix math to find the triangle's gradient vector, which gives the (x,y) direction of steepest ascent with respect to z -- this is the direction of the color-gradient. The start/end points of the color-gradient are determined by the intersection of the gradient vector slope (constrained through the x,y origin) with the iso-lines on the triangle's plane describing zmin and zmax.
To begin, the plane intersecting three points {p1, p2, p3}
of a triangle can be described by the equation:
A1(x) + A2(y) + A3(z) - A = 0
where A is the determinant:
|p1x p1y p1z|
A = |p2x p2y p2z|
|p3x p3y p3z|
and Ai
is the same determinant, but replace column i
with the column vector:
1 |p1x 1 p1z|
column(i) = 1 e.g., A2 = |p2x 1 p2z|
1 |p3x 1 p3z|
The gradient vector grad(z)
describes the direction of steepest ascent, which is also the trajectory of the color-gradient:
grad(z) = [-A1/A3 (i), -A2/A3 (j)]
so in the x,y plane, this gradient vector lies along a line:
y = x * A2/A1 + b,
where b can be anything, but let's set b = 0
. this constrains the color-gradient trajectory to a line intersecting the origin:
y = x * A2/A1 [eqn 1]
This line describes the color-gradient direction. The start and end points will be determined by the intersection of this line with the zmax and zmin iso-lines.
now, for any defined values zmax
and zmin
, we can describe parallel lines on the plane defined by our triangle thus:
A1(x) + A2(y) + A3(zmax) - A = 0 [eqn 2]
and
A1(x) + A2(y) + A3(zmin) - A = 0 [eqn 3]
Using equations 1-3 from above, we can solve for G1
and G2
, the color-gradient start and end points, respectively.
G1 = (xmin,ymin)
G2 = (xmax,ymax)
where
xmin = (A - A3*zmin) / (A1 + A2^2 / A1)
ymin = xmin * A2/A1
xmax = (A - A3*zmax) / (A1 + A2^2 / A1)
ymax = xmax * A2/A1
Notice the special case where A1 = 0
, corresponding to a perfectly vertical color-gradient path. In this case:
for A1 == 0:
G1 = (0,ymin)
G2 = (0,ymax),
where
ymin = (A - A3*zmin) / A2
ymax = (A - A3*zmax) / A2
The only other special case is when p1z = p2z = p3z
. This would try to stretch the gradient path to be infinitely long. In this special case, the triangle should just be colored solidly, rather than going through all the math.
All that's left is to set the triangle as a clipping region and draw the gradient from G1
to G2
. I'm including a diagram of the problem domain with associated linear equations. Notice also that the color-gradient varies linearly along each triangle edge, so the OP's question about delaunay triangulation is right on target. I developed this approach for just that reason - to color the faces of a triangulated mesh. The image below shows a case where zmax == p3z > p1z > p2z > zmin
.
You need more than one gradient to achieve what you want on a triangle, since a gradient is an interpolation between two points in the colorspace, but you have three distinct noncollinear points. Using barycentric interpolation, you should apply one gradient per vertex, such that the gradient direction is directed away from the vertex, in the direction perpendicular to the opposite edge. The gradient runs from full saturation at the vertex to zero saturation when it hits the edge.
There are various analogs for barycentric interpolation on convex polygons, but I haven't read that paper in detail to know if it can be achieved as a superposition of linear gradients.
In the end, your problem boils down to interpolation within the polygon, and each interpolation scheme will yield a different (possibly unique) coloring.
Maybe you should be checking Gouraud Shading, it seems appropriate for what you are looking for. It interpolates given three colors across a triangle's vertices.
It's possible to use svg gradients in combination with svg filters to create certain effects, similar to what I think you're asking for.
Some examples can be seen here: http://www.chaos.org.uk/~eddy/when/2006/ColourCube.xhtml (I'd recommend looking at the results in Opera, the other browsers didn't seem to render the fused gradients correctly). See here for an example of a three-way gradient applied to a triangle.
If the hues don't wrap around it's pretty easy, but the solution is not unique.
Suppose the three hues are different, say H1<H2<H3
.
You are going to find a point x4 in the segment joining x1 and x3 (this is where you have choices), and let the whole line joining x2 and x4 the same color H2. Define then the gradient to be perpendicular to this line, having the required distances such as to give the three points the correct hue.
One possible choice of the x4 point is such that the hue varies linearly between x1 and x3. Another one would be the foot of the perpendicular. Any fixed solution won't join with a different trianngle with two common vertices, so for the general coloring it's of no help.
精彩评论