Calculate new gradient positions after the polygon it fills changes dimensions Calculate new gradient positions after the polygon it fills changes dimensions php php

Calculate new gradient positions after the polygon it fills changes dimensions


Update 3 Alternative Idea

An alternative solution could be to calculate the percentage values based on the two end points of the gradient.
In this Picture you see the original polygon abcd, its bounding box a'b'c'd' and the gradient g1g2. The aim is now to calculate first the two points g1 and g2 in absolute values and then calculate the relative values of these two.

bounding box

I've derived an algorithm that does most of what I described but it fails short of calculating the intersection between the gradient and the bounding box. I have an idea on how to solve this problem but right now not the time to implement it, so I list the steps for that.
The basic idea is to test if the gradient intersects with one of the lines through the corners of the bounding box (a'b',b'c',c'd',d'a') and then test if the intersection is in on the edge in question. There are now two special cases that need handling. 1. the gradient is vertical, this means its slope is infinity 2. the side in question is vertical, again this means its slope is infinityAll other cases are easily solvable with basic math (two point form of a line, intersection of two lines).

My algorithm

_ = require('underscore')    function intersectGradientWithPolygon(polygon, gradient){  var sides = [    ["a", "b"],    ["b", "c"],    ["c", "d"],    ["d", "a"]  ];  var boundingBox = calculateBoundingBox(polygon);  // intersect each side of the bounding box with the gradient  var intersections = _.map(sides, function(side){    var intersection = intersect(boundingBox[side[0]], boundingBox[side[1]], gradient.a, gradient.b);    if(intersection){      // calculate the percentages      console.log(JSON.stringify(intersection));      return calcPercentage(intersection, boundingBox);    }  });  return intersections;}function Point(x,y){  this.x = x;  this.y = y;}function calcPercentage(intersection, boundingBox){  var lengthX = (boundingBox.max.x - boundingBox.min.x),      lengthY = (boundingBox.max.y - boundingBox.min.y),      x = (intersection.x / lengthX) * 100,      y = (intersection.y / lengthY) * 100;}function calculateBoundingBox(polygon){  var xValues = _.pluck(polygon, 'x'),      yValues = _.pluck(polygon, 'y'),      maxX = _.max(xValues),      maxY = _.max(yValues),      minX = _.min(xValues),      minY = _.min(yValues);  return {    "a": new Point(minX, maxY),    "b": new Point(maxX, maxY),    "c": new Point(maxX, minY),    "d": new Point(minX, minY),    "max": new Point(maxX, maxY),    "min": new Point(minX, minY)  };}// tests if the two lines a1, b1 and a2, b2 intersect and // returns the point of intersection if the do sofunction intersect(a1, b1, a2, b2){  var s = new Point( );  // TODO  // This is the part that is missing  // one needs to implement what I described above at this point  //  if (isInIntervall(s.x, a1.x, b1.x) && isInIntervall(s.y, a2.y, b2.y)){    return s;  }  else {    return false;  }}// test if a point is in the intervall [a,b]function isInIntervall(point, a, b){  return (point >= a) && (point <=b);}

Update 2

Question: Also how should the gradient coordinates change if the polygon is moved in space as a whole and not stretched.?

Answer: You calculate the amount that you move one point of your polygon in x and y and move the points of the gradient the exact same amount.

I've now changed the algorithm to be based of scaling on one side of the polygon by absolute amount of units. I've also created an image to explain what the algorithm does

  1. original polygon
  2. scaled polygon by a scale factor determined by the inputs
  3. move the polygon back to the original place

explain

Updated 15.7.2012I've derived an algorithm based on the idea I proposed using transformation matrices for the transform. I hadn't time to test it but the code is running under node.js and should run in the browser if you include underscore.js and sylvester (matrix operations) in your document.

The Setup

/* underscore for some helper methods * http:*http:*underscorejs.org */_ = require("underscore");  /* matrix operations * http:*sylvester.jcoglan.com */require("sylvester");

The inputs

var gradient = {  "a":{    "x": 165.3425,    "y": 39.7002  },  "b":{    "x": -49.991,    "y": 43.0337   }};var polygon = {  "a": {    "x": 137.145,    "y": 41.204  },  "b": {    "x": 68.572,    "y": 0  },  "c": {    "x": 0,    "y": 41.204  },  "d": {    "x": 68.572,    "y": 82.396  }};// the scales are now absolute values in the same units as the coordinatesvar scaleAbsX = 100;var scaleAbsY = 100 * Math.tan( 62/2 * (Math.PI/180) );// this describes the side that is scaledvar side = ["a", "b"];

The Algorithm

scalePolyWithGradient = function(polygon, gradient, scaleAbsX, scaleAbsY, side){  // 1. Scale by factor: derive factor from input scaling and then translate into scaling matrix  // 2. Apply scale to the gradient  // 3. Translate both back   // create a scaling matrix based of the input scales  // get the two points of the scaled side  var point1 = polygon[side[0]],      point2 = polygon[side[1]];  // scale these points  var scaledPoint1 = { "x": point1.x + scaleAbsX,                       "y": point1.y + scaleAbsY },      scaledPoint2 = { "x": point2.x + scaleAbsX,                       "y": point2.y + scaleAbsY };  // calculate the relative scales  var scaleRelX = scaledPoint1.x / point1.x,      scaleRelY = scaledPoint1.y / point1.y;  // create the scale matrix  var scaleMatrix = $M([ [scaleRelX, 0],                     [0, scaleRelY] ]);  // scale both the polygon and the gradient  // we iterate so that the translation is done on every point  var scale = function(point){    // convert the point into a matrix    point = $M([[point.x],                 [point.y]]);    // scale    var newPoint = scaleMatrix.multiply(point);    return { "x": newPoint.elements[0][0],             "y": newPoint.elements[1][0]};  };  var newPolygon  = {},      newGradient = {};  _.each(polygon, function(point, key){    newPolygon[key] = scale(point);  });  _.each(gradient, function(point, key){    newGradient[key] = scale(point);  });  // calculate the translation to move them to the original position  // and move them back  // we know that the points to move to their original position are the  // ones not in the scale side  var sides = _.keys(polygon),                   // all possible sides      movePoints = _.difference(sides, side),    // the points not used in the scale      point = movePoints[0];                     // the first of these points  // we use these points to create the translation  var oldPoint = polygon[point],      newPoint = newPolygon[point];  var translateMatrix = $M([ [newPoint.x - oldPoint.x],                             [newPoint.y - oldPoint.y] ]);  var translate = function(point){    // convert the point into a matrix    point = $M([[point.x],                 [point.y]]);    // translate    var newPoint = point.add(translateMatrix);    return { "x": newPoint.elements[0][0],             "y": newPoint.elements[1][0]};  };  // apply the translation  _.each(newPolygon, function(point, key){     newPolygon[key] = translate(point);   });   _.each(newGradient, function(point, key){     newGradient[key] = translate(point);   });  // return the new coordinates  return [newPolygon, newGradient];};// apply the algorithmvar newPolygon, newGradient = null;var result = scalePolyWithGradient(polygon, gradient, scaleAbsX, scaleAbsY, side);newPolygon = result[0];newGradient = result[1];

The result

 newPolygon = { "a": {                   "x": 178.2885,                   "y":82.405                  },                 "b": {                   "x": 96.00089999999999,                   "y": 20.598999999999997                 },                 "c": {                   "x": 13.714500000000001,                   "y": 82.405                 },                 "d": {                    "x": 96.00089999999999,                   "y":144.19299999999998                 }               } newGradient = { "a": {                   "x": 212.12550000000005,                   "y":80.14930000000001                 },                 "b": {                     "x": -46.274699999999996,                     "y": 85.14955                 }               }

Old Answer

The image is here because I can't upload images to stackoverflow (reputation is to low)

I abstracted the side of the polygon so we can focus on that. The left picture is before scaling. Now I've drawn the "whole" gradient to show what needs to be scaled. In order to figure out the needed coordinates one just scales the square of the gradient in the same proportion as the side of the polygon.

I know this image is without rotation but this method can be expanded to also incorporate this.

I can derive an algorithm for this stuff but haven't had the time to do so. So if this is what you want let me know and I will get to it tomorrow.


You can apply transformations to gradients, this means you can do stuff such as gradientTransform="rotate(45). This solves your rotation issue.

You should use relative units and set the userspace to objectBoundingBox so that the x and y values correspond to the dimensions of your polygon. Your svg would look like this.

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">  <linearGradient id="top_surface_2_gradient" gradientUnits="objectBoundingBox" x1="0%" y1="0%" x2="100%" y2="100%" gradientTransform="rotate(0 0.5 0.5)">    <stop  offset="0" style="stop-color:#000"/>    <stop  offset="1" style="stop-color:#fff"/>  </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"/></svg>

You can test how it works on different sized polygons here: http://jsfiddle.net/hqXx2/