php - terremoto - Calcule nuevas posiciones de degradado después de que el polígono que llena cambia las dimensiones
superluna y terremotos (2)
Puede aplicar transformaciones a degradados, esto significa que puede hacer cosas como gradientTransform="rotate(45)
. Esto resuelve su problema de rotación.
Debe usar unidades relativas y establecer el espacio de usuario en objectBoundingBox
para que los valores y
correspondan a las dimensiones de su polígono. Tu svg se vería así.
<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>
Puedes probar cómo funciona en diferentes tamaños de polígonos aquí: http://jsfiddle.net/hqXx2/
¿Lo que quiero hacer?
Actualizaciones a esta pregunta: 10/07/2012 - "gradientTransform not quite" Inspirado por Duopixel 11/07/2012 - "Código SVG del ejemplo" 16/07/2012 - "@dignifiedquire asumir este problema"
Intento crear una herramienta que permita al usuario cambiar el tamaño de los polígonos de forma dinámica. La mayoría de los polígonos están rellenos con degradados fill="url(#top_surface_1_gradient)"
. La forma en que hago esto es un simple script de JavaScript que:
- busca eventos mousemove y clic sobre un cierto polígono
- mide la cantidad de movimiento
- cambia la mitad de las coordenadas del polígono (para tener el efecto de estiramiento) usando este algoritmo para definir nuevas coordenadas:
x = x_movement
,y = x_movement * Math.tan( 31 * (Math.PI/180) )
- los polígonos que están rellenos con un solo color están bien
- los polígonos que están llenos con un degradado no lo son, permítanme demostrar:
Visualmente
Así que este es el primer paso, el usuario no ha hecho ningún estiramiento.
Aquí es donde ocurre el problema. Como no sé cómo debo cambiar las coordenadas x1, y1
y x2, y2
del degradado, simplemente permanece colgado en su posición anterior mientras el polígono se ha estirado. El resultado es una forma que no puede mantener la ilusión de profundidad.
El resultado final que estoy buscando Y sin olvidar que el gradiente podría tener un ángulo completamente aleatorio desde el principio. En este resultado final, que estoy buscando, se han cambiado las coordenadas x1, y1
y x2, y2
del degradado. ¿Qué algoritmo debería usarse para calcular estas posiciones? Estoy buscando una solución que sea completamente ciega al ángulo del gradiente .
A continuación se muestra el SVG con todas las coordenadas apropiadas que se utilizaron para generar estos ejemplos:
Usando el código SVG
Paso 1:
<!-- 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"/>
Paso 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"/>
Paso 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"/>
He pasado innumerables horas desarrollando soluciones para este problema y simplemente no podía entenderlo. Cualquier ayuda sería muy apreciada.
Actualización: gradientTransform no del todo
Usar el atributo gradientTransform y no x1, y1; Las coordenadas x2, y2 para el degradado, logramos resultados que llenan el polígono de una manera casi igual a la necesaria (esta solución se puede encontrar aquí: http://jsfiddle.net/hqXx2/ ). El único lugar donde se rompe la solución es cuando el polígono se llena con un degradado que comienza fuera del polígono y / o termina en algún lugar fuera / dentro. Déjame ilustrar:
Esto es lo que se logra con la solución que Duopixel sugiere.
Este es el caso de uso que es imposible de lograr con la solución mencionada anteriormente. Cambié el color para amplificar visiblemente el ángulo y el gradiente.
Código SVG del ejemplo
Aquí está el código para el grupo de polígonos más grande y correctamente expandido:
<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>
Y aquí está el código SVG para el más pequeño, que necesito expandir:
<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 asumir este problema
Implementé @dignifiedquire algo sugerido en un sitio de prueba: aquí está el enlace de prueba . Hice la conversión absoluta a relativa por mi cuenta y solo muestra el mismo resultado que normalmente tendría al agregar los mismos valores de cambio xey del polígono al gradiente xey. Ese es el problema principal: ¿cómo traducir esos valores en tal valor, que transforman el gradiente como en mis ejemplos anteriores?
Se necesita más ayuda.
Actualizar 3 Idea alternativa
Una solución alternativa podría ser calcular los valores porcentuales en función de los dos puntos finales del gradiente.
En esta imagen, verá el polígono original abcd
, su cuadro delimitador a''b''c''d''
y el gradiente g1g2
. El objetivo ahora es calcular primero los dos puntos g1
y g2
en valores absolutos y luego calcular los valores relativos de estos dos.
Obtuve un algoritmo que hace la mayor parte de lo que describí, pero no alcanza a calcular la intersección entre el gradiente y el cuadro delimitador. Tengo una idea sobre cómo resolver este problema, pero ahora no es el momento de implementarlo, así que enumero los pasos para eso.
La idea básica es comprobar si el gradiente se cruza con una de las líneas a través de las esquinas del cuadro delimitador ( a''b''
, b''c''
, c''d''
, d''a''
) y luego probar si la intersección es en el borde en cuestión. Ahora hay dos casos especiales que necesitan manejo. 1. el gradiente es vertical, esto significa que su pendiente es infinito 2. el lado en cuestión es vertical, otra vez esto significa que su pendiente es infinito Todos los otros casos son fácilmente solucionables con matemática básica (forma de dos puntos de una línea, intersección de dos líneas )
Mi algoritmo
_ = 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 so
function 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);
}
Actualización 2
Pregunta: ¿También cómo deberían cambiar las coordenadas del gradiente si el polígono se mueve en el espacio como un todo y no se estira?
Respuesta: Calcula la cantidad que mueve un punto de su polígono en xey y mueve los puntos del degradado exactamente la misma cantidad.
Ahora he cambiado el algoritmo para que esté basado en escalar en un lado del polígono por cantidad absoluta de unidades. También creé una imagen para explicar lo que hace el algoritmo
- polígono original
- polígono escalado por un factor de escala determinado por las entradas
- mover el polígono de vuelta al lugar original
Actualizado 15.7.2012 He derivado un algoritmo basado en la idea que propuse usar matrices de transformación para la transformación. No tuve tiempo de probarlo pero el código se está ejecutando bajo node.js y debería ejecutarse en el navegador si incluye underscore.js y sylvester (operaciones de matriz) en su documento.
La puesta en marcha
/* underscore for some helper methods
* http:*http:*underscorejs.org
*/
_ = require("underscore");
/* matrix operations
* http:*sylvester.jcoglan.com
*/
require("sylvester");
Las entradas
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 coordinates
var scaleAbsX = 100;
var scaleAbsY = 100 * Math.tan( 62/2 * (Math.PI/180) );
// this describes the side that is scaled
var side = ["a", "b"];
El algoritmo
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 algorithm
var newPolygon, newGradient = null;
var result = scalePolyWithGradient(polygon, gradient, scaleAbsX, scaleAbsY, side);
newPolygon = result[0];
newGradient = result[1];
El resultado
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
}
}
Vieja respuesta
La imagen está aquí porque no puedo subir imágenes a (la reputación es baja)
Abstraccioné el lado del polígono para poder enfocarnos en eso. La imagen de la izquierda está antes de escalar. Ahora he dibujado el degradado "completo" para mostrar lo que debe escalarse. Para descubrir las coordenadas necesarias, uno simplemente escala el cuadrado del degradado en la misma proporción que el lado del polígono.
Sé que esta imagen no tiene rotación, pero este método se puede ampliar para incorporarlo también.
Puedo derivar un algoritmo para esto, pero no he tenido el tiempo para hacerlo. Entonces, si esto es lo que quieres, házmelo saber y lo abordaré mañana.