简体   繁体   English

旋转SVG路径点以获得更好的变形效果

[英]Rotating SVG path points for better morphing

I am using a couple of functions from Snap.SVG, mainly path2curve and the functions around it to build a SVG morph plugin. 我正在使用Snap.SVG中的一些函数,主要是path2curve和它周围的函数来构建一个SVG变形插件。

I've setup a demo here on Codepen to better illustrate the issue. 在Codepen上设置了一个演示来更好地说明问题。 Basically morphing shapes simple to complex and the other way around is working properly as of Javascript functionality, however, the visual isn't very pleasing. 基本上变形形状简单到复杂,反之亦然,Javascript功能正常工作,但是,视觉效果不是很令人愉悦。

The first shape morph looks awful, the second looks a little better because I changed/rotated it's points a bit, but the last example is perfect. 第一个形状变形看起来很糟糕,第二个看起来好一点,因为我稍微改变/旋转它的点,但最后一个例子是完美的。

So I need either a better path2curve or a function to prepare the path string before the other function builds the curves array. 所以我需要一个更好的path2curve或一个函数来准备路径字符串,然后另一个函数构建curve数组。 Snap.SVG has a function called getClosest that I think may be useful but it's not documented. Snap.SVG有一个名为getClosest的函数,我认为它可能很有用但是没有记录。

There isn't any documentation available on this topic so I would appreciate any suggestion/input from RaphaelJS / SnapSVG / d3.js / three/js developers. 没有关于此主题的任何文档,所以我将不胜感激RaphaelJS / SnapSVG / d3.js / three / js开发人员提出的任何建议/意见。

I've provided a runnable code snippet below that uses Snap.svg and that I believe demonstrates one solution to your problem. 我在下面提供了一个使用Snap.svg的可运行代码片段,我相信它可以为您的问题提供一个解决方案。 With respect to trying to find the best way to morph a starting shape into an ending shape, this algorithm essentially rotates the points of the starting shape one position at a time, sums the squares of the distances between corresponding points on the (rotated) starting shape and the (unchanged) ending shape, and finds the minimum of all those sums. 关于试图找到将起始形状变形为结束形状的最佳方法,该算法基本上将起始形状的点一次旋转一个位置,对(旋转)起点上的相应点之间的距离的平方求和形状和(未更改的)结束形状​​,并找到所有这些总和的最小值。 ie It's basically a least squares approach. 即它基本上是最小二乘法。 The minimum value identifies the rotation that, as a first guess, will provide the "shortest" morph trajectories. 最小值标识旋转,作为第一个猜测,将提供“最短”变形轨迹。 In spite of these coordinate reassignments, however, all 'rotations' should result in visually identical starting shapes, as required. 然而,尽管进行了这些坐标重新分配,但所有“旋转”应根据需要产生视觉上相同的起始形状。

This is, of course, a "blind" mathematical approach, but it might help provide you with a starting point before doing manual visual analysis. 当然,这是一种“盲目”的数学方法,但它可能有助于在进行手动视觉分析之前为您提供一个起点。 As a bonus, even if you don't like the rotation that the algorithm chose, it also provides the path 'd' attribute strings for all the other rotations, so some of that work has already been done for you. 作为奖励,即使您不喜欢算法选择的旋转,它也为所有其他旋转提供路径'd'属性字符串,因此已经为您完成了一些工作。

You can modify the snippet to provide any starting and ending shapes you want. 您可以修改代码段以提供所需的任何起始和结束形状。 The limitations are as follows: 限制如下:

  • Each shape should have the same number of points (although the point types, eg 'lineto', 'cubic bezier curve', 'horizontal lineto', etc., can completely vary) 每个形状应该具有相同数量的点(尽管点类型,例如'lineto','cubic bezier curve','horizo​​ntal lineto'等,可以完全变化)
  • Each shape should be closed, ie end with "Z" 每个形状都应该是封闭的,即以“Z”结尾
  • The morph desired should involve only translation. 所需的变形应仅涉及翻译。 If scaling or rotation is desired, those should be applied after calculating the morph based only on translation. 如果需要缩放或旋转,则应在仅基于平移计算变形之后应用这些缩放或旋转。

By the way, in response to some of your comments, while I find Snap.svg intriguing, I also find its documentation to be somewhat lacking. 顺便说一下,回应你的一些评论,虽然我发现Snap.svg很有趣,但我发现它的文档有点缺乏。

Update : The code snippet below works in Firefox (Mac or Windows) and Safari. 更新下面的代码片段适用于Firefox(Mac或Windows)和Safari。 However, Chrome seems to have trouble accessing the Snap.svg library from its external web site as written (<script...github...>). 但是,Chrome似乎无法通过其外部网站(<script ... github ...>)访问Snap.svg库。 Opera and Internet Explorer also have problems. Opera和Internet Explorer也存在问题。 So, try the snippet in the working browsers, or try copying the snippet code as well as the Snap library code to your own computer. 因此,请尝试使用工作浏览器中的代码段,或尝试将代码段代码和Snap库代码复制到您自己的计算机上。 (Is this an issue of accessing third party libraries from within the code snippet? And why browser differences? Insightful comments would be appreciated.) (这是从代码片段中访问第三方库的问题吗?为什么浏览器存在差异?有见地的评论会受到赞赏。)

 var s = Snap(), colors = ["red", "blue", "green", "orange"], // colour list can be any length staPath = s.path("M25,35 l-15,-25 C35,20 25,0 40,0 L80,40Z"), // create the "start" shape endPath = s.path("M10,110 h30 l30,20 C30,120 35,135 25,135Z"), // create the "end" shape staSegs = getSegs(staPath), // convert the paths to absolute values, using only cubic bezier endSegs = getSegs(endPath), // segments, & extract the pt coordinates & segment strings numSegs = staSegs.length, // note: the # of pts is one less than the # of path segments numPts = numSegs - 1, // b/c the path's initial 'moveto' pt is also the 'close' pt linePaths = [], minSumLensSqrd = Infinity, rotNumOfMin, rotNum = 0; document.querySelector('button').addEventListener('click', function() { if (rotNum < numPts) { linePaths.forEach(function(linePath) {linePath.remove();}); // erase any previous coloured lines var sumLensSqrd = 0; for (var ptNum = 0; ptNum < numPts; ptNum += 1) { // draw new lines, point-to-point var linePt1 = staSegs[(rotNum + ptNum) % numPts]; // the new line begins on the 'start' shape var linePt2 = endSegs[ ptNum % numPts]; // and finished on the 'end' shape var linePathStr = "M" + linePt1.x + "," + linePt1.y + "L" + linePt2.x + "," + linePt2.y; var linePath = s.path(linePathStr).attr({stroke: colors[ptNum % colors.length]}); // draw it var lineLen = Snap.path.getTotalLength(linePath); // calculate its length sumLensSqrd += lineLen * lineLen; // square the length, and add it to the accumulating total linePaths[ptNum] = linePath; // remember the path to facilitate erasing it later } if (sumLensSqrd < minSumLensSqrd) { // keep track of which rotation has the lowest value minSumLensSqrd = sumLensSqrd; // of the sum of lengths squared (the 'lsq sum') rotNumOfMin = rotNum; // as well as the corresponding rotation number } show("ROTATION OF POINTS #" + rotNum + ":"); // display info about this rotation var rotInfo = getRotInfo(rotNum); show("&nbsp;&nbsp;point coordinates: " + rotInfo.ptsStr); // show point coordinates show("&nbsp;&nbsp;path 'd' string: " + rotInfo.dStr); // show 'd' string needed to draw it show("&nbsp;&nbsp;sum of (coloured line lengths squared) = " + sumLensSqrd); // the 'lsq sum' rotNum += 1; // analyze the next rotation of points } else { // once all the rotations have been analyzed individually... linePaths.forEach(function(linePath) {linePath.remove();}); // erase any coloured lines show("&nbsp;"); show("BEST ROTATION, ie rotation with lowest sum of (lengths squared): #" + rotNumOfMin); // show which rotation to use show("Use the shape based on this rotation of points for morphing"); $("button").off("click"); } }); function getSegs(path) { var absCubDStr = Snap.path.toCubic(Snap.path.toAbsolute(path.attr("d"))); return Snap.parsePathString(absCubDStr).map(function(seg, segNum) { return {x: seg[segNum ? 5 : 1], y: seg[segNum ? 6 : 2], seg: seg.toString()}; }); } function getRotInfo(rotNum) { var ptsStr = ""; for (var segNum = 0; segNum < numSegs; segNum += 1) { var oldSegNum = rotNum + segNum; if (segNum === 0) { var dStr = "M" + staSegs[oldSegNum].x + "," + staSegs[oldSegNum].y; } else { if (oldSegNum >= numSegs) oldSegNum -= numPts; dStr += staSegs[oldSegNum].seg; } if (segNum !== (numSegs - 1)) { ptsStr += "(" + staSegs[oldSegNum].x + "," + staSegs[oldSegNum].y + "), "; } } ptsStr = ptsStr.slice(0, ptsStr.length - 2); return {ptsStr: ptsStr, dStr: dStr}; } function show(msg) { var m = document.createElement('pre'); m.innerHTML = msg; document.body.appendChild(m); } 
 pre { margin: 0; padding: 0; } 
 <script src="//cdn.jsdelivr.net/snap.svg/0.4.1/snap.svg-min.js"></script> <p>Best viewed on full page</p> <p>Coloured lines show morph trajectories for the points for that particular rotation of points. The algorithm seeks to optimize those trajectories, essentially trying to find the "shortest" cumulative routes.</p> <p>The order of points can be seen by following the colour of the lines: red, blue, green, orange (at least when this was originally written), repeating if there are more than 4 points.</p> <p><button>Click to show rotation of points on top shape</button></p> 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM