Вычислить центр пути после применения матриц преобразования

Я сделал логотип в 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) после трансформационного преобразования?

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

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


person Daniel Andersson    schedule 12.04.2011    source источник
comment
Вероятно, это выглядело бы немного чище, если бы вы установили Inkscape для применения преобразований к значениям пути. Попробуйте Inkscape Prefs > Transforms > Store transformation: Optimized.   -  person Erik Dahlström    schedule 12.04.2011


Ответы (1)


Я думаю, что преобразования будут применяться от самого внутреннего наружу, поэтому 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().

person Peter Collingridge    schedule 17.06.2011
comment
Спасибо, мне нравится, как решается проблема. Это касается элементов SVG, с которыми у меня нет опыта, но это прекрасно, так как я сделал это, чтобы научиться :-) Это проект для свободного времени, а свободного времени в данный момент мало, поэтому я не буду реализовывать его для какое-то время. Я вернусь сюда, если найду что добавить к вопросу/ответу после того, как попробую этот подход. - person Daniel Andersson; 18.06.2011
comment
Я понимаю, что это может показаться слишком сложным способом получить центр элемента, но я не знаю более простого способа. Я действительно рекомендую поэкспериментировать с EMCAScript, когда у вас будет немного времени. Это позволяет вам делать произвольно сложные анимации в SVG. - person Peter Collingridge; 18.06.2011
comment
Я создал пример некоторых поворотных механизмов: petercollingridge.co.uk /sites/files/peter/4gears.svg - person Peter Collingridge; 18.06.2011
comment
Приветствуйте ответчика @Peter Collingridge за отличный пост в блоге на эту тему: petercollingridge.co.uk/blog/svg-animation-rotating-elements Петр I пришел к этому в поисках решения для автоматически сгенерированных svg, созданных с помощью d3js. Попытка портировать ваше решение сейчас - person Reed Spool; 17.07.2014