What I want to do?
Updates to this question: 7/10/2012 - "gradientTransform not quite" Inspired by Duopixel 7/11/2012 - "SVG Code from the example" 7/16/2012 - "@dignifiedquire take on this problem"
I'm trying to create a tool that let's the user dynamically resize polygons. Most of the polygons are filled with gradients fill="url(#top_surface_1_gradient)"
. The way I go about this is a simple JavaScript script that:
- looks for mousemove & click events over a certain polygon
- measures the amount of movement
- changes half of the coordinates of the polygon (in order to have the effect of stretching) using this algorithm to define new coordinates:
x = x_movement
,y = x_movement * Math.tan( 31 * (Math.PI/180) )
- polygons that are filled with a single colour are OK
- polygons that are filled with a gradient are not, let me demonstrate:
Visually
So this is step one, no stretching has been done by the user.
This is where the problem happens. Since I don't know how should I change the x1, y1
and x2, y2
coordinates for the gradient, it just stays hanging in it's old position while the polygon has been stretched. The result is a shape that fails to sustain the illusion of depth.
The end result I'm looking for. And bare in mind that the gradient might have a completely random angle from the get go. In this end result, that I'm looking for, both the x1, y1
and x2, y2
coordinates of the gradient have been changed. What algorithm should be used to calculate these positions? I'm looking for a solution that is completely blind to the angle of the gradient.
Below is the SVG with all the appropriate coordinates that was used to generate these examples:
Using SVG code
Step1:
<!-- Step 1 -->
<linearGradient id="top_surface_1_gradient" gradientUnits="userSpaceOnUse" x1="165.3425" y1="39.7002" x2="-49.991" y2="43.0337">
<stop offset="0" style="stop-color:#FFFFFF"/>
<stop offset="0.6687" style="stop-color:#CCCCCC"/>
<stop offset="1" style="stop-color:#FFFFFF"/>
</linearGradient>
<polygon id="top_surface_1" fill="url(#top_surface_1_gradient)" points="137.145,41.204 68.572,0 0,41.204 68.572,82.396"/>
Step 2
<!-- Step 2 -->
<linearGradient id="top_surface_2_gradient" gradientUnits="userSpaceOnUse" x1="250.0491" y1="233.8115" x2="23.7637" y2="237.3146">
<stop offset="0" style="stop-color:#FFFFFF"/>
<stop offset="0.6687" style="stop-color:#CCCCCC"/>
<stop offset="1" style="stop-color:#FFFFFF"/>
</linearGradient>
<polygon id="top_surface_2" fill="url(#top_surface_2_gradient)" points="205.788,215.557 137.215,174.354 0.078,256.629 68.649,297.823"/>
Step 3
<!-- Step 3 -->
<linearGradient id="top_surface_3_gradient" gradientUnits="userSpaceOnUse" x1="248.4543" y1="454.5225" x2="-75.535" y2="459.5381">
<stop offset="0" style="stop-color:#FFFFFF"/>
<stop offset="0.6687" style="stop-color:#CCCCCC"/>
<stop offset="1" style="stop-color:#FFFFFF"/>
</linearGradient>
<polygon id="top_surface_3" fill="url(#top_surface_3_gradient)" points="205.788,415.557 137.215,374.354 0.078,456.629 68.649,497.823"/>
I've spent countless hours developing solutions for this problem and I just couldn't get my head around it. Any help would be greatly appreciated.
Update: gradientTransform not quite
Using the gradientTransform attribute and no x1,y1; x2,y2 coordinates for the gradient, we achieve results that fill the polygon in a way that is almost as needed (This solution can be found here: http://jsfiddle.net/hqXx2/). The only place where the solution breaks is when the polygon is filled with a gradient that starts outside of the polygon and/or ends somewhere outside/inside. Let me illustrate:
This is what is achieved with the solution, that Duopixel is suggesting.
This is the usage case that is impossible to achieve using the solution mentioned above. I changed the colouring in order to visibly amplify the angle and gradient stops.
SVG Code from the example
Here's the code for the larger, correctly expanded group of polygons:
<g>
<linearGradient id="surface_center_inside_bottom_1_" gradientUnits="userSpaceOnUse" x1="167.7629" y1="634.5986" x2="-72.9039" y2="599.2647">
<stop offset="0" style="stop-color:#FFFFFF"/>
<stop offset="0.8528" style="stop-color:#CCCCCC"/>
<stop offset="0.9954" style="stop-color:#CCCCCC"/>
</linearGradient>
<polygon id="surface_center_inside_bottom_9_" fill="url(#surface_center_inside_bottom_1_)" points="137.145,620.04 68.572,578.837 0,620.04 68.572,661.233"/>
<linearGradient id="surface_right_inside_side_1_" gradientUnits="userSpaceOnUse" x1="178.8889" y1="600.1787" x2="33.103" y2="517.9229">
<stop offset="0" style="stop-color:#FFFFFF"/>
<stop offset="0.9816" style="stop-color:#A3A5A8"/>
</linearGradient>
<polygon id="surface_right_inside_side_3_" fill="url(#surface_right_inside_side_1_)" points="136.526,620.374 68.359,578.501 68.572,493.837 137.358,535.37"/>
<linearGradient id="surface_right_inside_side_2_" gradientUnits="userSpaceOnUse" x1="126.2664" y1="563.249" x2="-28.4" y2="621.916">
<stop offset="0" style="stop-color:#FF0000"/>
<stop offset="0.6698" style="stop-color:#00FFFF"/>
<stop offset="1" style="stop-color:#FF0000"/>
</linearGradient>
<polygon id="surface_right_inside_side_5_" fill="url(#surface_right_inside_side_2_)" points="68.573,661.239 0,620.036 0,535.036 68.573,576.231"/>
<linearGradient id="surface_center_outside_top_1_" gradientUnits="userSpaceOnUse" x1="167.3728" y1="533.5059" x2="-47.9608" y2="536.8394">
<stop offset="0.0016" style="stop-color:#FF0000"/>
<stop offset="0.6735" style="stop-color:#00FFFF"/>
<stop offset="1" style="stop-color:#FF0000"/>
</linearGradient>
<polygon id="surface_center_outside_top_3_" fill="url(#surface_center_outside_top_1_)" points="137.145,535.041 68.572,493.837 0,535.041 68.572,576.233"/>
</g>
And here's the SVG code for the smaller one, which I need to expand:
<g>
<linearGradient id="surface_right_inside_side_4_" gradientUnits="userSpaceOnUse" x1="273.4377" y1="319.251" x2="78.0696" y2="209.0197">
<stop offset="0" style="stop-color:#FFFFFF"/>
<stop offset="0.9816" style="stop-color:#A3A5A8"/>
</linearGradient>
<polygon id="surface_right_inside_side_9_" fill="url(#surface_right_inside_side_4_)" points="205.112,366.797 136.945,324.924 137.157,156.261 205.731,197.464"/>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="247.2952" y1="408.1992" x2="-103.1108" y2="356.7538">
<stop offset="0" style="stop-color:#FFFFFF"/>
<stop offset="0.8528" style="stop-color:#CCCCCC"/>
<stop offset="0.9954" style="stop-color:#CCCCCC"/>
</linearGradient>
<polygon fill="url(#SVGID_1_)" points="205.731,366.465 137.157,325.262 0.021,407.536 68.592,448.729"/>
<linearGradient id="surface_right_inside_side_7_" gradientUnits="userSpaceOnUse" x1="160.3313" y1="296.623" x2="-52.0119" y2="377.1676">
<stop offset="0" style="stop-color:#FF0000"/>
<stop offset="0.6698" style="stop-color:#00FFFF"/>
<stop offset="1" style="stop-color:#FF0000"/>
</linearGradient>
<polygon id="surface_right_inside_side_6_" fill="url(#surface_right_inside_side_7_)" points="68.532,448.767 0,407.497 0.021,238.536 68.592,279.729"/>
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="248.4749" y1="215.7417" x2="-75.5139" y2="220.7572">
<stop offset="0.0016" style="stop-color:#FF0000"/>
<stop offset="0.6735" style="stop-color:#00FFFF"/>
<stop offset="1" style="stop-color:#FF0000"/>
</linearGradient>
<polygon fill="url(#SVGID_2_)" points="205.731,197.464 137.157,156.261 68.592,197.333 0.021,238.536 68.592,279.729"/>
</g>
@dignifiedquire take on this problem
I've implemented @dignifiedquire suggested algo in a test site: Here's the test link. I did the absolute to relative conversion on my own and it just shows the same result I would have normally adding the same polygon x and y change values to the gradient x and y. That is the main issue - how to translate those values into such value, that transform the gradient as in my examples above?
More help is needed.