我想做什么?
此问题的更新:7/10/2012 - “gradientTransform not quite”灵感来自Duopixel 7/11/2012 - “示例中的SVG代码”2012年7月16日 - “@dignifiedquire采取此问题”
我正在尝试创建一个让用户动态调整多边形大小的工具。大多数多边形都填充了渐变fill="url(#top_surface_1_gradient)"
。我这样做的方法是一个简单的JavaScript脚本:
x = x_movement
,y = x_movement * Math.tan( 31 * (Math.PI/180) )
目视
所以这是第一步,用户没有进行任何拉伸。
这就是问题发生的地方。由于我不知道如何更改渐变的x1, y1
和x2, y2
坐标,因此在拉伸多边形时它只会保持在旧位置。结果是一种无法维持深度幻觉的形状。
我正在寻找的最终结果。并且记住,渐变可能具有完全随机的角度。在最终结果中,我正在寻找,渐变的x1, y1
和x2, y2
坐标都已更改。 应该使用什么算法来计算这些位置?我正在寻找一种完全忽视渐变角度的解决方案。
下面是SVG,其中包含用于生成这些示例的所有适当坐标:
使用SVG代码
步骤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"/>
第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"/>
第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"/>
我花了无数个小时为这个问题开发解决方案,我无法理解它。任何帮助将不胜感激。
更新:gradientTransform不太
使用gradientTransform属性而没有x1,y1;对于渐变的x2,y2坐标,我们以几乎所需的方式实现填充多边形的结果(此解决方案可在此处找到:http://jsfiddle.net/hqXx2/)。解决方案中断的唯一地方是多边形填充了一个从多边形外部开始和/或在外部/内部某处结束的渐变。让我来说明一下:
这是Duopixel建议的解决方案所取得的成果。
这是使用上述解决方案无法实现的使用情况。我改变了颜色,以便可视地放大角度和渐变停止。
示例中的SVG代码
这是更大,正确扩展的多边形组的代码:
<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>
这是较小的SVG代码,我需要扩展它:
<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采取此问题
我在测试网站{@ 3}}中实施了@dignifiedquire建议算法。我自己进行了绝对到相对转换,它只显示了相同的结果,我通常会将相同的多边形x和y更改值添加到渐变x和y。这是主要问题 - 如何将这些值转换为这样的值,如上面的示例中那样转换渐变?
需要更多帮助。
答案 0 :(得分:7)
更新3个替代想法
另一种解决方案是根据梯度的两个终点计算百分比值
在此图片中,您会看到原始多边形abcd
,其边界框a'b'c'd'
和渐变g1g2
。现在的目的是首先计算绝对值中的两个点g1
和g2
,然后计算这两个点的相对值。
我已经推导出一种能够完成我所描述的大部分工作的算法,但它没有计算渐变和边界框之间的交集。我对如何解决这个问题有了一个想法,但现在没有时间来实现它,所以我列出了相应的步骤
基本思想是测试渐变是否与通过边界框的角(a'b'
,b'c'
,c'd'
,d'a'
)的其中一条线相交,然后测试如果交叉点位于相关边缘。现在有两个需要处理的特殊情况。
1.梯度是垂直的,这意味着它的斜率是无穷大
2.有问题的一侧是垂直的,这意味着它的斜率是无限的
所有其他情况都可以通过基本数学(线的两点形式,两条线的交点)轻松解决。
我的算法
_ = 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);
}
更新2
问题:如果多边形作为一个整体在空间中移动而未拉伸,那么渐变坐标应该如何变化呢?
答案:您计算在x和y中移动多边形的一个点的数量,并将渐变的点移动完全相同的数量。
我现在已经将算法更改为基于多边形一侧缩放的绝对单位数。 我还创建了一个图像来解释算法的作用
更新时间:15.7.2012 我已经根据我提出的使用变换矩阵进行变换的想法推导出一种算法。我没有时间测试它,但代码在node.js下运行,并且如果在文档中包含underscore.js和sylvester(矩阵操作),则应该在浏览器中运行。
设置
/* underscore for some helper methods
* http:*http:*underscorejs.org
*/
_ = require("underscore");
/* matrix operations
* http:*sylvester.jcoglan.com
*/
require("sylvester");
输入
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"];
算法
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];
结果
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
}
}
旧答案
The image is here因为我无法将图片上传到stackoverflow(声誉很低)
我抽象出多边形的边,所以我们可以专注于那个。左图是缩放前的图片。现在我绘制了“整体”渐变来显示需要缩放的内容。为了找出所需的坐标,我们只是按照与多边形一侧相同的比例缩放渐变的平方。
我知道这张图片没有旋转,但是这个方法可以扩展到也包含这个。
我可以为这些东西推导出一种算法,但还没有时间这么做。所以,如果这是你想要的,请告诉我,我明天就会知道。
答案 1 :(得分:4)
您可以将变换应用于渐变,这意味着您可以执行gradientTransform="rotate(45)
之类的操作。这解决了您的轮换问题。
您应该使用相对单位并将用户空间设置为objectBoundingBox
,以便x
和y
值对应于多边形的尺寸。你的svg看起来像这样。
<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>
您可以在此处测试它在不同大小的多边形上的工作方式:http://jsfiddle.net/hqXx2/