草庐IT

php - 在它填充的多边形改变尺寸后计算新的渐变位置

coder 2023-06-13 原文

我想做什么?

此问题的更新:2012 年 7 月 10 日 - “gradientTransform 不完全” 受 Duopixel 启发 2012 年 7 月 11 日-“示例中的 SVG 代码”2012 年 7 月 16 日-“@dignifiedquire 解决这个问题”

我正在尝试创建一个让用户动态调整多边形大小的工具。大多数多边形都用渐变填充fill="url(#top_surface_1_gradient)"。我这样做的方法是一个简单的 JavaScript 脚本:

  1. 在某个多边形上查找 mousemove 和 click 事件
  2. 测量移动量
  3. 改变多边形一半的坐标(为了有拉伸(stretch)的效果)使用这个算法定义新坐标:x = x_movement, y = x_movement * Math.tan ( 31 * (Math.PI/180) )
  4. 用单一颜色填充的多边形是可以的
  5. 用渐变填充的多边形不是,让我演示一下:

视觉上

所以这是第一步,用户没有进行任何拉伸(stretch)。

这就是问题发生的地方。因为我不知道应该如何更改渐变的 x1, y1x2, y2 坐标,所以它只是保持在原来的位置,而多边形已经拉伸(stretch)。结果是一个无法维持深度错觉的形状。

我正在寻找的最终结果。请记住,渐变可能从一开始就具有完全随机的 Angular 。在我正在寻找的最终结果中,渐变的 x1, y1x2, y2 坐标都已更改。 应该使用什么算法来计算这些位置?我正在寻找一种完全看不到渐变 Angular 解决方案

以下是用于生成这些示例的所有适当坐标的 SVG:

使用 SVG 代码

第一步:

<!-- 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 建议的解决方案所实现的目标。

这是使用上述解决方案无法实现的用例。我更改了颜色以明显放大 Angular 和渐变停止。

示例中的 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 解决这个问题

我已经在一个测试站点中实现了@dignifiedquire 建议算法:Here's the test link。我自己进行了绝对到相对的转换,它只显示了与通常将相同的多边形 x 和 y 更改值添加到渐变 x 和 y 相同的结果。这是主要问题 - 如何将这些值转换为这样的值,如我上面的示例中那样转换渐变?

需要更多帮助。

最佳答案

更新 3 个备选想法

另一种解决方案是根据梯度的两个端点计算百分比值。
在此图片中,您可以看到原始多边形 abcd , 它的边界框 a'b'c'd'和梯度 g1g2 .现在的目标是首先计算两个点 g1g2在绝对值中,然后计算这两者的相对值。

我推导出了一种算法,它可以完成我所描述的大部分内容,但它无法计算梯度和边界框之间的交集。我有一个关于如何解决这个问题的想法,但现在不是实现它的时候,所以我列出了解决这个问题的步骤。
基本思想是测试渐变是否与通过边界框 Angular 的其中一条线相交(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

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.

我现在已将算法更改为基于在多边形的一侧按单位的绝对数量进行缩放。 我还创建了一个图像来解释算法的作用

  1. 原始多边形
  2. 按由输入确定的比例因子缩放多边形
  3. 将多边形移回原处

更新于 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(声誉太低)

我抽象了多边形的边,这样我们就可以专注于它。左图是缩放前。现在我绘制了“整体”渐变以显示需要缩放的部分。为了找出所需的坐标,只需按与多边形边相同的比例缩放渐变的平方。

我知道这张图片没有旋转,但是这个方法可以扩展到也包含这个。

我可以为这些东西推导出一个算法,但还没有时间这样做。因此,如果这是您想要的,请告诉我,我明天会处理。

关于php - 在它填充的多边形改变尺寸后计算新的渐变位置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11399009/

有关php - 在它填充的多边形改变尺寸后计算新的渐变位置的更多相关文章

  1. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  2. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  3. ruby - 匹配大写字母并用后续字母填充,直到一定的字符串长度 - 2

    我有一个驼峰式字符串,例如:JustAString。我想按照以下规则形成长度为4的字符串:抓取所有大写字母;如果超过4个大写字母,只保留前4个;如果少于4个大写字母,则将最后大写字母后的字母大写并添加字母,直到长度变为4。以下是可能发生的3种情况:ThisIsMyString将产生TIMS(大写字母);ThisIsOneVeryLongString将产生TIOV(前4个大写字母);MyString将生成MSTR(大写字母+tr大写)。我设法用这个片段解决了前两种情况:str.scan(/[A-Z]/).first(4).join但是,我不太确定如何最好地修改上面的代码片段以处理最后一种

  4. ruby-on-rails - 如何在发布新的 Ruby 或 Rails 版本时收到通知? - 2

    有人知道在发布新版本的Ruby和Rails时收到电子邮件的方法吗?他们有邮件列表,RubyonRails有一个推特,但我不想听到那些随之而来的喧嚣,我只想知道什么时候发布新版本,尤其是那些有安全修复的版本。 最佳答案 从therailsblog获取提要.http://weblog.rubyonrails.org/feed/atom.xml 关于ruby-on-rails-如何在发布新的Ruby或Rails版本时收到通知?,我们在StackOverflow上找到一个类似的问题:

  5. ruby - 改变替换的大小写 - 2

    我有以下内容:text.gsub(/(lower)(upper)/,'\1\2')我可以将\2替换为大写吗?类似于:sed-e's/\(abc\)/\U\1/'这在Ruby中可行吗? 最佳答案 查看gsub文档:str.gsub(模式){|匹配|block}→new_str在block形式中,当前匹配字符串作为参数传入,$1、$2、$`、$&、$'等变量将被适当设置。block返回的值将替换为每次调用的匹配项。"alowerupperb".gsub(/(lower)(upper)/){|s|$1+""+$2.upcase}

  6. ruby - 需要重构为新的 Ruby 1.9 哈希语法 - 2

    这个问题在这里已经有了答案:HashsyntaxinRuby[duplicate](1个回答)关闭5年前。我有一个Recipe,其中包含以下未通过lint测试的代码:service'apache'dosupports:status=>true,:restart=>true,:reload=>trueend失败并出现错误:UsethenewRuby1.9hashsyntax.supports:status=>true,:restart=>true,:reload=>true不确定新语法是什么样的...有人可以帮忙吗?

  7. ruby - 为什么我不能从 ruby​​ 中的选定键创建新的散列? - 2

    这个问题困扰了我一段时间。这不是一件困难的事情,但我不知道为什么没有简单的方法来做到这一点,我敢打赌有但我没有看到。我只想取一个散列,像这样:cars={:bob=>'Pontiac',:fred=>'Chrysler',:lisa=>'Cadillac',:mary=>'Jaguar'}然后做类似的事情cars[:bob,:lisa]得到{:bob=>'Pontiac',:lisa=>'Cadillac'}我这样做了,效果很好:classHashdefpick(*keys)Hash[select{|k,v|keys.include?(k)}]endendruby-1.8.7-p249

  8. ruby - 如何将新的 rvm 安装与现有的 ruby​​ 版本相关联? - 2

    我遇到了RVM的问题,所以我卸载并重新安装了它。事实是我实际上尝试过rbenv,但这对我来说没有用,所以我试图让rvm重新启动并运行-而不必安装重复版本的Ruby。我至少安装了1个现有版本的Ruby:ruby--versionruby1.8.7(2011-12-28patchlevel357)[universal-darwin11.0]但是当我执行rvmlist时,我得到一个空白列表:bash-3.2$rvmlistrvmrubies#Defaultrubynotset.Try'rvmaliascreatedefault'.#=>-current#=*-current&&default

  9. ruby - 如何用递增的值填充数组 Ruby - 2

    我正在尝试解决http://projecteuler.net/problem=1.我想创建一个方法,它接受一个整数,然后创建一个包含它前面的所有整数的数组,并将整数本身作为数组中的值。以下是我目前所拥有的。代码不起作用。defmake_array(num)numbers=Array.newnumcount=1numbers.eachdo|number|numbers 最佳答案 (1..num).to_a是您在Ruby中需要做的全部。1..num将创建一个Range对象,以1开始并以任意值num结束是。Range对象有to_a方法通过

  10. ruby-on-rails - RoR中是否有任何内置方法可以为整数填充零? - 2

    如果我想要“00001”而不是“1”,除了我自己写填零方法之外,有没有内置的方法可以帮助我为整数填零? 最佳答案 puts"%05d"%1#00001参见:String::%,Kernel::sprintf这是正在发生的事情。%左侧的"%05d"是C风格的格式说明符。%右边的变量就是要格式化的东西。格式说明符可以像这样解码:%-格式说明符的开头0-用前导零填充5-长度为5个字符d-被格式化的是一个整数如果你要格式化多个东西,你会把它们放在一个数组中:"%d-%s"%[1,"One"]#=>1-one

随机推荐