Как я могу использовать матрицу преобразования элемента SVG для вычисления координат пункта назначения?

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

Пример диаграммы объясняет это лучше всего:


Круг с точками A, B и центром вращения. A является опорной/начальной точкой. B похож на точку A, но с вращением, примененным вокруг центра вращения. (Все вышеперечисленное находится в содержащем SVG,  который намного больше во всех направлениях. Нельзя сделать никаких предположений, что левая ограничивающая рамка круга совпадает с ограничивающей рамкой корня SVG.) A: Я знаю расстояние от центра вращения, а также точные координаты x и y. B:  Я могу получить матрицу преобразования с помощью B.getCTM(), но ее координаты x и y остаются такими же, как у A. Используя матрицу преобразования из B, мне нужно определить координаты x и y после преобразования. Другими словами, я  нужна возможность создать элемент, который появляется в том же месте, что и B, но без применения преобразований».  /></а></p>
<hr> 
<p>Боюсь, я не пошел математическим путем в программирование.  Я могу как бы следить за поверхностными деталями того, что делает матрица преобразования, но мое понимание похоже на способность читать ноты по одной ноте за раз, и только очень медленно;  Я не совсем понимаю мелодию на более высоком уровне, поэтому я не совсем понимаю звучание целой музыкальной фразы, не говоря уже о мелодии.</p>
<p>Точно так же я не понимаю, как работают матрицы преобразования.  Я пробовал искать объяснения матриц преобразования grok, но все, что я находил, было наполнено <em>более</em> математическим жаргоном, которого я не понимаю.  Я просто знаю, что они работают как функция, и что это невероятно гибкие инструменты, но это все.</p>
<p>Из всех методов, доступных для <code>SVGMatrix</code> (предположительно устарел в пользу <code>DOMMatrix</code>, но Firefox Dev. ed. по-прежнему использует <code>SVGMatrix</code>), я понятия не имею, что мне нужно, <code>.inverse()</code> или <code>.multiply()</code>, и не знаю, как уговорить простой набор  <code>x</code> и <code>y</code> координаты вне этой матрицы.</p>
<p><strong>Примечание.</strong></p>
<ul>
<li>Меня не интересует здесь перевод координат экрана в SVG.</li>
<li>Меня интересуют только координаты SVG (пространство пользователя).</li>
</ul>
    </div>

    <br>

    <div class=

person Zearin    schedule 03.04.2021    source источник


Ответы (2)


Вы можете использовать простое тригонометрическое преобразование:

const rotatePoint = (point, center, rotateAngle) => {
  const dx = point.x - center.x;
  const dy = point.y - center.y;
  const distance = Math.hypot(dx, dy);
  const currentAngle = Math.atan(dy / dx);
  const nextAngle = currentAngle - rotateAngle;
  const nextDX = distance * Math.cos(nextAngle);
  const nextDY = distance * Math.sin(nextAngle);
  return {x: center.x + nextDX, y: center.y + nextDY};
};

Фрагмент отображает вращение синей точки вокруг красной (30/90/123 градуса против часовой стрелки)

const rotatePoint = (point, center, angle) => {
    const dx = point.x - center.x;
  const dy = point.y - center.y;
  const distance = Math.hypot(dx, dy);
  const current = Math.atan(dy / dx);
  const next = current - angle;
  const nextDX = distance * Math.cos(next);
  const nextDY = distance * Math.sin(next);
  return {x: center.x + nextDX, y: center.y + nextDY};
};

const center = {x: 150, y: 150};
const start = {x: 200, y: 30};
const svg = d3.select('svg');

svg.append('circle')
    .attr('cx', center.x)
  .attr('cy', center.y)
  .attr('r', 5)
  .style('fill', 'red');
svg.append('circle')
    .attr('cx', start.x)
  .attr('cy', start.y)
  .attr('r', 5)
  .style('fill', 'blue');  

// Rotate 30 deg
const p30 = rotatePoint(start, center, Math.PI * 30 / 180); 
svg.append('circle')
    .attr('cx', p30.x)
  .attr('cy', p30.y)
  .attr('r', 5)
  .style('fill', 'green');  
// Rotate 90 deg
const p90 = rotatePoint(start, center, Math.PI * 90 / 180); 
svg.append('circle')
    .attr('cx', p90.x)
  .attr('cy', p90.y)
  .attr('r', 5)
  .style('fill', 'orange');
// Rotate 123 deg
const p123 = rotatePoint(start, center, Math.PI * 123 / 180); 
svg.append('circle')
    .attr('cx', p123.x)
  .attr('cy', p123.y)
  .attr('r', 5)
  .style('fill', 'yellow');  
  
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width="250" height="200"></svg>

person Michael Rovinsky    schedule 03.04.2021
comment
Это хорошо. Благодарю вас! Единственная причина, по которой я не могу пометить это как ответ, заключается в следующем: я решил, что смогу найти выход из этого с помощью «Пифагора», но я бы предпочел решение, использующее существующие SVGMatrix или DOMMatrix API. Тем не менее, я попробую это как временное решение. Спасибо. ???????? - person Zearin; 03.04.2021
comment
@Zearin, возможно, вы найдете это полезным: stackoverflow.com/questions/14660796/, но я не уверен, что использование матриц проще, чем функциональный расчет, особенно когда речь идет о проблемах с производительностью. . - person Michael Rovinsky; 03.04.2021
comment
Ваш подход, безусловно, самое простое решение! И после моего первоначального комментария выше я попробовал это, и это работает довольно хорошо. Единственная причина, по которой я прошу решение, связанное с матрицей, заключается в том, что у меня будет что-то, что я могу (теоретически) использовать для любого преобразованного элемента SVG и вычислить точку (точки) назначения, если мне нужно к. - person Zearin; 03.04.2021
comment
В очередной раз благодарим за помощь. Я не смог найти решение, которое хотел, поэтому остановился на вашем. Я отмечу свой вопрос соответствующим образом. - person Zearin; 22.05.2021

Когда у вас есть круг SVG,

Вы можете рассчитать любую точку SVG на окружности с помощью стандартных функций getPointAtLength и getTotalLength.

Просто обратите внимание, где рисуется начинается круг (зеленая точка).

длина пути преобразована в градусы:

<style>
  svg{ height:300px }
  text {
    font-size: 3px;
    fill: white;
    text-anchor: middle;
    dominant-baseline: middle;
  }
</style>
<svg id="SVG" viewBox="0 0 100 100">
  <circle cx="50%" cy="50%" r="50%" fill="pink"></circle>
  <circle id="CIRCLE" cx="50%" cy="50%" r="40%" 
          fill="none" stroke="black" stroke-dasharray="2"></circle>
  <circle cx="90%" cy="50%" r="5%" fill="green"></circle>
</svg>
<script>
  let circle_length = CIRCLE.getTotalLength();
  for (let degree = 0; degree < 360; degree += 45) {
    let len = degree * ( circle_length / 360 );
    let point = CIRCLE.getPointAtLength(len);
    let circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
    circle.setAttribute("cx", point.x);
    circle.setAttribute("cy", point.y);
    circle.setAttribute("r", "3%");
    circle.setAttribute("fill", "blue");
    let text = document.createElementNS("http://www.w3.org/2000/svg", "text");
    text.setAttribute("x", point.x);
    text.setAttribute("y", point.y);
    text.innerHTML = degree;
    SVG.append(circle, text)
  }
</script>

person Danny '365CSI' Engelman    schedule 03.04.2021