Вычислить центр пути после применения матриц преобразования - PullRequest
3 голосов
/ 12 апреля 2011

Я сделал логотип в Inkscape.Для обучения я хотел, чтобы форма колеса в логотипе вращалась с помощью поддержки анимации в SVG.

Вращение было легко реализовать, но мне было трудно определить правильную ось вращения.,Форма была зубчатым колесом, и я хотел, чтобы оно вращалось вокруг его центра.Метод проб и ошибок показал, что xy-координата (47.1275, 1004.17) (чьи компоненты странно асимметричны, но я предполагаю, что это связано с матрицами преобразования, применяемыми Inkscape) была хорошим приближением (см. Тег animateTransform ниже), но как бы яполучить это из первых принципов?

<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 321.281 150.799" xmlns:dc="http://purl.org/dc/elements/1.1/">
    <g transform="translate(-9.9178912,-891.57237)">
        <g transform="matrix(1.9522781,0,0,1.9522781,4.6434311,-1008.1558)">
            <animateTransform attributeType="xml" attributeName="transform" type="rotate" from="0 47.1275 1004.17" to="45 47.1275 1004.17" dur="2s" fill="freeze" additive="sum" repeatCount="indefinite" />
            <g transform="matrix(0.65043772,0,0,0.65043772,-143.67477,980.4256)" stroke="#666" stroke-miterlimit="4" stroke-dasharray="none" stroke-width="7.68713093" fill="none">
                <path stroke-linejoin="miter" d="m293.404-3.51576c-2.73916,0-5.41514,0.287192-8,0.8125v6.1875c-3.47484,0.838872-6.7198,2.18462-9.6875,4l-4.375-4.375c-2.24264,1.48612-4.29226,3.22977-6.1875,5.125s-3.63888,3.94486-5.125,6.1875l4.375,4.375c-1.81538,2.9677-3.16112,6.21265-4,9.6875h-6.1875c-0.5253,2.58486-0.8125,5.26083-0.8125,8s0.2872,5.41515,0.8125,8h6.1875c0.83888,3.47485,2.18462,6.7198,4,9.6875l-4.375,4.375c1.48612,2.24264,3.22976,4.29227,5.125,6.1875s3.94486,3.63888,6.1875,5.125l4.375-4.375c2.9677,1.81538,6.21266,3.16113,9.6875,4v6.1875c2.58486,0.525308,5.26082,0.8125,8,0.8125,2.73916,0,5.41514-0.287192,8-0.8125v-6.1875c3.47484-0.838872,6.7198-2.18462,9.6875-4l4.375,4.375c2.24264-1.48612,4.29226-3.22977,6.1875-5.125s3.63888-3.94486,5.125-6.1875l-4.375-4.375c1.81538-2.9677,3.16112-6.21266,4-9.6875h6.1875c0.5253-2.58485,0.8125-5.26083,0.8125-8s-0.2872-5.41515-0.8125-8h-6.1875c-0.83888-3.47485-2.18462-6.7198-4-9.6875l4.375-4.375c-1.48612-2.24264-3.22976-4.29227-5.125-6.1875s-3.94486-3.63888-6.1875-5.125l-4.375,4.375c-2.9677-1.81538-6.21266-3.16113-9.6875-4v-6.1875c-2.58486-0.525308-5.26084-0.8125-8-0.8125z" stroke-dashoffset="162" stroke="#666" stroke-linecap="butt" stroke-miterlimit="4" stroke-dasharray="none" stroke-width="7.68713093" fill="none"/>
            </g>
        </g>
    </g>
</svg>

Из того, что я прочитал в спецификации, я бы сказал, что применяются матрицы преобразования

1.9522781  0              4.6434311
0          1.9522781  -1008.1558
0          0              1

и

0.65043772  0          -143.67477
0           0.65043772  980.4256
0           0             1

Применяются ли они к xyz-координате (-9.9178912,-891.57237,0) после преобразования преобразования?

Я полагаю, что при правильном анализе, приведенном выше, я получу верхнюю левую точку описанного пути или, возможно, координату для первого дескриптора.После этого нужно ли анализировать путь, чтобы определить ограничивающую рамку и, таким образом, центр (так как он касается несколько кругового объекта) пути?

Является ли это уроком того, чтобы не пытаться делать вручнуюанимация на свободно создаваемых фигурах?

1 Ответ

6 голосов
/ 17 июня 2011

Я думаю, что преобразования будут применяться изнутри наружу, поэтому transform="translate(-9.9178912,-891.57237)" будет выполнено последним. Но вы можете игнорировать другие преобразования, если поместите анимацию в самую внутреннюю область, то есть в сам путь:

   <g transform1>
     <g transform2>
       <g transform3>
         <path d="coordinates">
           <animateTransform your transformation here>
         </path>
       </g>
     </g>
   </g>

Тогда вам просто нужно найти центр вашего пути, что легко сделать в Inkscape, но сложно сделать на лету (связанный вопрос здесь: программно Как получить ширину фигуры в SVG-документе Java ).

Лично я бы использовал скрипт в svg, чтобы вы могли использовать getBBox, чтобы найти ограничивающую рамку вашей фигуры. Если вы добавите следующий элемент в ваш SVG, вы можете сделать любой элемент с id = "cog" поворотом вокруг его центра:

<script type="text/ecmascript"><![CDATA[
  var svgNS = "http://www.w3.org/2000/svg";

  function init(evt)
  {
    if ( window.svgDocument == null )
    {
        svgDocument = evt.target.ownerDocument;
    }
    addRotateTransform('cog');
  }

  function addRotateTransform(target_id)
  {
    var element_to_rotate = svgDocument.getElementById(target_id);
    var my_transform = svgDocument.createElementNS(svgNS, "animateTransform");

    var bb = element_to_rotate.getBBox();
    var cx = bb.x + bb.width/2;
    var cy = bb.y + bb.height/2;

    my_transform.setAttributeNS(null, "attributeName", "transform");
    my_transform.setAttributeNS(null, "attributeType", "XML");
    my_transform.setAttributeNS(null, "type", "rotate");
    my_transform.setAttributeNS(null, "dur", "4s");
    my_transform.setAttributeNS(null, "repeatCount", "indefinite");
    my_transform.setAttributeNS(null, "from", "0 "+cx+" "+cy);
    my_transform.setAttributeNS(null, "to", "360 "+cx+" "+cy);

    element_to_rotate.appendChild(my_transform);
    my_transform.beginElement();
  }
]]></script>

Вам также необходимо добавить onload = "init (evt)" в качестве атрибута к тегу SVG. например,

<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
     xmlns="http://www.w3.org/2000/svg"
     version="1.1"
     xmlns:cc="http://creativecommons.org/ns#"
     xmlns:xlink="http://www.w3.org/1999/xlink"
     viewBox="0 0 321.281 150.799"
     xmlns:dc="http://purl.org/dc/elements/1.1/"
     onload="init(evt)">

Это вызовет функцию init () при первой загрузке SVG. Функция init () вызывает метод addRotateTransform (), который находит элемент с заданным идентификатором. Затем он находит центр этого объекта с помощью getBBox () и добавляет метод animateTransform с соответствующими центрами. Вы можете изменить атрибут dur, который определяет скорость полного вращения.

Может показаться, что кода много, но я думаю, что это самый простой способ определить центр пути. Это также означает, что вы можете легко добавить другие вращающиеся элементы, добавив addRotateTransform('whatever-id'); в функцию init ().

...