2016-02-18 3 views
1

Я использую пару функций от Snap.SVG, в основном path2curve и функции вокруг него для создания плагина SVG morph.Вращение точек траектории SVG для лучшего морфинга

Я установил демо here on Codepen, чтобы лучше проиллюстрировать проблему. В основном морфинговые формы, простые в сложном и наоборот, работают правильно, как функциональность Javascript, однако визуальное изображение не очень приятное.

Первый вид формы выглядит ужасно, второй выглядит немного лучше, потому что я изменил/повернул его точки немного, но последний пример идеален.

Так что мне нужно либо лучше path2curve, либо функцию для подготовки строки пути до того, как другая функция построит массив кривых. Snap.SVG имеет функцию под названием getClosest, которая, по моему мнению, может быть полезна, но она не документирована.

В этой теме нет документации, поэтому я буду благодарен за любые предложения/предложения от разработчиков RaphaelJS/SnapSVG/d3.js/three/js.

+0

Обычно это ответственность дизайнера, чтобы убедиться, обе формы имеют одинаковое количество очков/сегментов и ориентированы соответствующим образом по отношению друг к другу. Алгоритм автоматического создания красивого векторного твинирования является нетривиальным. Даже Flash потребовал [некоторое взаимодействие с дизайнером для управления твиновским поведением] (http://help.adobe.com/en_US/flash/cs/using/WS58E1E1A4-9296-4b75-AB74-D9D545892556.html). –

+0

Я думал об одном и том же, но все же более сложные формы очень тяжело переносят любого, кто пытается их преобразовать. – thednp

+0

В любом случае, я собираюсь сорвать материал Snap SVG, это просто ненадежно. – thednp

ответ

3

Я представил приведенный ниже фрагмент кода ниже, который использует Snap.svg и, я считаю, демонстрирует одно решение вашей проблемы. Что касается поиска наилучшего способа превращения исходной формы в конечную форму, этот алгоритм существенно поворачивает точки начальной формы по одной позиции за раз, суммирует квадраты расстояний между соответствующими точками на (повернутом) пуске формы и (неизменной) окончательной формы, и находит минимум всех этих сумм. т. е. в основном подход наименьших квадратов. Минимальное значение определяет вращение, которое, как предполагается, будет обеспечивать «кратчайшие» морфологические траектории. Однако, несмотря на эти изменения координат, все «вращения» должны привести к визуально идентичным пусковым формам, если это необходимо.

Это, конечно, «слепой» математический подход, но он может помочь вам начать отправку, прежде чем делать ручной визуальный анализ. В качестве бонуса, даже если вам не нравится поворот, выбранный алгоритмом, он также предоставляет строки атрибутов пути «d» для всех других поворотов, поэтому некоторые из этих работ уже были выполнены для вас.

Вы можете отредактировать фрагмент, чтобы предоставить любую начальную и конечную фигуры, которые вы хотите. Ограничения заключаются в следующем:

  • Каждая форма должна иметь одинаковое число точек (хотя типы точек, например, «LineTo», «кубической кривой Безье», «горизонтальная LineTo», и т.д., могут полностью различаться)
  • Каждая форма должна быть закрыта, то есть заканчиваться на «Z»
  • Предпочитаемый морфинг должен включать только перевод. Если требуется масштабирование или вращение, они должны применяться после вычисления морфа, основанного только на переводе.

Кстати, в ответ на некоторые ваши комментарии, когда я нахожу Snap.svg интригующим, я также считаю, что его документации не хватает.

Обновление: Фрагмент кода ниже работает в Firefox (Mac или Windows) и Safari. Тем не менее, у Chrome, похоже, есть проблемы с доступом к библиотеке Snap.svg со своего внешнего веб-сайта, как написано (< script ... github ... >). У Opera и Internet Explorer также есть проблемы. Итак, попробуйте фрагмент в рабочих браузерах или попробуйте скопировать код фрагмента, а также код библиотеки Snap на ваш собственный компьютер. (Это проблема доступа к сторонним библиотекам из фрагмента кода и почему различия в браузере?)

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, i.e. 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>

+0

Спасибо за ваш ответ, однако этот сценарий не работает правильно. Аудитор XSS отказался выполнить сценарий в http://stacksnippets.net/js, потому что его исходный код был найден в запросе. Аудитор был включен, поскольку сервер не отправил ни заголовок «X-XSS-Protection», ни «Content-Security-Policy». – thednp

+0

После прочтения вашего комментария @thednp я проверил проблемы совместимости браузера. См. Обновление, добавленное к моему ответу. Простое решение: попробуйте в Firefox. –

+0

Большое вам спасибо. Кажется, это замечательное решение. Теперь я задаюсь вопросом, как я могу изменить функцию path2curve, чтобы лучше отображать точки кривой с этим решением. Как я уже сказал, я использую некоторые функции из Snap.SVG. Мне не нужен весь код. – thednp

Смежные вопросы