Отображение SVG ARCTO на HTML Canvas ARCTO - PullRequest
4 голосов
/ 18 июля 2011

ARCTO в SVG спецификация сильно отличается от той, что у нас в Canvas .У меня есть сценарий использования, в котором у меня будут данные в соответствии со спецификацией SVG, но мне нужно нарисовать их на Canvas.

Я пробовал это, но я думаю, что моя геометрия слабаяМожете ли вы помочь?

Ответы [ 4 ]

2 голосов
/ 18 июля 2011

Разница между svg ellipse и canvas arc заключается в том, что у вас есть 2 радиуса в svg и только один в arcTo. Затем вам также нужно повернуть дугу на определенный угол на холсте. Чтобы эмулировать 2 радиуса, необходимо создать дугу с заданными координатами, имеющими наименьший радиус. Затем вам нужно масштабировать эту дугу в определенном направлении с коэффициентом (rx / ry). И теперь вам нужно только вращаться. Но в этом подходе действительно трудно определить, какую часть эллипса вы хотите показать, потому что это зависит от флага большой дуги и флага развертки в спецификации svg. Другая проблема - ограничить вашу дугу конечными координатами (из спецификации SVG). Так что с помощью arcTo вы можете построить максимум половину эллипса, я думаю.

Вы также можете использовать bezierCurveTo (x0, y0, x1, y1, x2, y2), чтобы нарисовать часть эллипса, если у вас есть координаты 3 контрольных точек на вашем эллипсе. При таком подходе вы можете построить любой сегмент эллипса. Конечно, для сегментов больше чем PI вам понадобятся как минимум две кривые

Из имеющейся у вас спецификации SVG (rx или x-ось-вращение, флаг большой дуги, флаг-развертка x y). Таким образом, примерный путь будет таким:

  M100,100 a25,50 -30 0,1 50,-25

Здесь вы можете найти, как следует рисовать кривые Безье.

Теперь у вас есть контекстная точка (что составляет 100 100) и конечная точка (что составляет 100 + 50 100-25) Вам необходимо рассчитать контрольные точки до поворота на -30 градусов.

Вот пример, который работает для меня:

$(document).ready(function(){
        var startX = 100;
        var startY = 100;
        var dX = 50;
        var dY = -25;
        var angle = -30;
        var rx = 25;
        var ry = 50;
        var svg = Raphael($('#svg')[0], 200, 200);

        var path = "M" +startX + "," + startY + " a" + rx + "," + ry + " " + angle + " 0,1" + " " + dX + "," +dY;
        svg.path(path).attr({"stroke-width" : 2, "stroke" : "#FFFFFF"});

        var kappa = .5522848,
        ox = rx*kappa,
        oy = ry*kappa,
        xm = startX + rx,       // x-middle
        ym = startY + ry;       // y-middle
        var canvas = document.getElementById("canvas");
        var ctx = canvas.getContext("2d");
        ctx.moveTo(startX,startY);
        ctx.bezierCurveTo(startX, startY - oy, startX + ox, startY - ry, startX + rx, startY - ry);
        ctx.bezierCurveTo(startX + rx + ox, startY - ry, startX + 2*rx, startY - oy, startX + dX, startY + dY);
        ctx.stroke();
    });

разметка это просто:

<div id="svg" style="border: 1px solid black;position : absolute;top : 50px;left : 50px;"></div>
<canvas id="canvas" width="200px" height="200px" style="border: 1px solid black;position : absolute;top : 300px;left : 50px;"></canvas>

кривые не похожи, потому что я не поворачивал контрольные точки до -30 градусов. Но я считаю, что это единственное, что вам нужно сделать. Потому что если вы поставите угол = 0. Они будут похожи Вы можете использовать эту статью, чтобы получить математику для вращения.

PS: я взял некоторые части кода из этого ответа

0 голосов
/ 22 марта 2019

У меня была такая же проблема, поэтому я наткнулся на этот пост.В Приложении к требованиям реализации определения SVG W3C рассказывается, как именно преобразовать параметризацию конечной точки формы (они называют это) в параметризацию центра и обратно:

Дуга SVG (параметризация конечной точки)описывается следующим образом:

  • x 1 / y 1 : начальная позиция (позиция последней команды пути)
  • x 2 / y 2 : конечная позиция дуги (значения x и y этой команды пути)
  • r x / r y : радиус x и y
  • φ: угол поворота
  • f A : флаг большой дуги (1 или0, использовать ли большую или маленькую дугу)
  • f S : флаг развертки (по часовой стрелке или против часовой стрелки)

холстовая дугаиспользует (параметризация центральной точки):

  • c x / c y : центральная точка эллипса
  • r x / r y : радиус x и y
  • φ: угол поворота
  • θ 1 : начальный угол эллипса (до поворота)
  • Δθ: угловое расстояние до использования эллипса (направление зависит от флага развертки f S , вы также можете рассчитать конечную точку θ 2 , что может быть даже лучше)

Конвертировать из SVG в Canvas

Это означает, что для преобразования из SVG в canvas можно использовать следующие уравнения (взятые непосредственно из заданного URL-адреса из W3C):

  1. Compute (x1′, y1′) (Уравнение F.6.5.1)

    Equation F.6.5.1

  2. Вычислить (cx′, cy′) (Уравнение F.6.5.2)

    enter image description here

    где знак + выбран, если f A ≠ f S , а знак - выбран, если f A = f S .

  3. Вычислить (cx, cy) из (cx′, cy′) (уравнение F.6.5.3)

    enter image description here

  4. Вычислить θ 1 и Δθ (уравнения F.6.5.5 и F.6.5.6)

    Редактировать: я сейчас использую другие уравнения, взгляните внизу

    enter image description here

    enter image description here

    , где θ 1 фиксируется в диапазоне −360 ° <Δθ <360 ° таким образом: </p>

    , если f S = 0, тогда Δθ <0, </p>

    , в противном случае, если f S = 1, тогда Δθ> 0.

    Другими словами, если f S = 0 и правая часть (F.6.5.6) больше 0, затем вычесть 360 °, тогда как если f S = 1 и правая часть (F.6.5.6)меньше 0, затем добавьте 360 °.Во всех остальных случаях оставьте все как есть.

Copyright © 16 августа 2011 г., World Wide Web Consortium, (MIT, ERCIM, Keio, Beihang).http://www.w3.org/Consortium/Legal/2015/doc-license

Редактировать: модифицированные уравнения для шага 4.

Теперь я использую следующие уравнения для определения θ 1 и Δθ:

enter image description here

enter image description here

Это просто векторы между начальной и конечной точкой дуги и центральной точкой.Φ вычитается, потому что угол вычисляется до поворота.Вы можете просто оставить это, если нужно.

Я получил неправильные результаты данных уравнений, но это также может быть ошибкой в ​​моей реализации.Пытаясь найти ошибку, я думал о том, что W3C делает здесь.Я искал, как рассчитать углы, и это было первое, о чем я подумал.Это приводит к правильным результатам для меня.

Конвертировать из Canvas в SVG

Я также столкнулся с проблемамипри использовании уравнений W3C при обратном преобразовании. Это может быть из-за изменения углов. Для перехода из Canvas в SVG необходимо преобразовать начальный и конечный углы (θ 1 и θ 2 = θ 1 + Δθ) вместе с центральной точкой до пересечений дуги. Это начальная и конечная точки дуги SVG.

  1. Вычислить (x1', y1') и (x2', y2')

    enter image description here

    Это вычисление пересечения для линии, которая определяется заданным углом θ 1 / θ 2 в повернутой системе координат. Для координаты x следует выбирать знак +, когда -π / 2 ≤ θ ≤ π / 2. Знак + для координаты y следует выбирать, когда 0 ≤ θ ≤ π.

  2. Вычислить (x1, y1) и (x2, y2)

    enter image description here

    Координаты x и y начальной и конечной точек можно затем рассчитать, повернув угол поворота φ назад и переместив вектор в центр эллипса.

  3. Найти флаги

    Флаги можно легко определить: f A равно 1, если Δθ больше 180 °, fS равно 1, если Δθ больше 0 °.

0 голосов
/ 26 ноября 2017

Следующий фрагмент кода был извлечен из соответствующего раздела всеобъемлющего пакета CANVG Гейба Лернера (см. https://github.com/canvg/canvg) для любого из вас, кто, как я, может не захотеть целых девять ярдов пакета Гейба. В отличие отболее ранние решения - это не приближение, это точный эквивалент элемента пути дуги SVG, за который я хотел бы выразить огромную благодарность Гейбу.

Еще один момент заключается в том, что если вы уже применили некоторое масштабирование и /или перевод на холст перед построением пути, вам нужно будет учесть это в параметрах двух вызовов Context.translate, а также в параметре radius вызова Context.arc

function drawSVGarcOnCanvas (Context,lastX,lastY,rx,ry,xAxisRotation,largeArcFlag,sweepFlag,x,y)
{
    //--------------------
    // rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y
    // are the 6 data items in the SVG path declaration following the A
    //
    // lastX and lastY are the previous point on the path before the arc
    //--------------------
    // useful functions
    var m   = function (   v) {return Math.sqrt (Math.pow (v[0],2) + Math.pow (v[1],2))};
    var r   = function (u, v) {return ( u[0]*v[0] + u[1]*v[1]) / (m(u) * m(v))};
    var ang = function (u, v) {return ((u[0]*v[1] < u[1]*v[0])? -1 : 1) * Math.acos (r (u,v))};
    //--------------------

var currpX =  Math.cos (xAxisRotation) * (lastX - x) / 2.0 + Math.sin (xAxisRotation) * (lastY - y) / 2.0 ;
var currpY = -Math.sin (xAxisRotation) * (lastX - x) / 2.0 + Math.cos (xAxisRotation) * (lastY - y) / 2.0 ;

var l = Math.pow (currpX,2) / Math.pow (rx,2) + Math.pow (currpY,2) / Math.pow (ry,2);
if (l > 1) {rx *= Math.sqrt (l); ry *= Math.sqrt (l)};
var s = ((largeArcFlag == sweepFlag)? -1 : 1) * Math.sqrt 
      (( (Math.pow (rx,2) * Math.pow (ry    ,2)) - (Math.pow (rx,2) * Math.pow (currpY,2)) - (Math.pow (ry,2) * Math.pow (currpX,2))) 
       / (Math.pow (rx,2) * Math.pow (currpY,2) +   Math.pow (ry,2) * Math.pow (currpX,2)));
if (isNaN (s)) s = 0 ;

var cppX = s *  rx * currpY / ry ;
var cppY = s * -ry * currpX / rx ;
var centpX = (lastX + x) / 2.0 + Math.cos (xAxisRotation) * cppX - Math.sin (xAxisRotation) * cppY ;
var centpY = (lastY + y) / 2.0 + Math.sin (xAxisRotation) * cppX + Math.cos (xAxisRotation) * cppY ;

var ang1 = ang ([1,0], [(currpX-cppX)/rx,(currpY-cppY)/ry]);
var a = [(  currpX-cppX)/rx,(currpY-cppY)/ry];
var b = [(-currpX-cppX)/rx,(-currpY-cppY)/ry];
var angd = ang (a,b);
if (r (a,b) <= -1) angd = Math.PI;
if (r (a,b) >=  1) angd = 0;

var rad = (rx > ry)? rx : ry;
var sx  = (rx > ry)? 1 : rx / ry;
var sy  = (rx > ry)? ry / rx : 1;

Context.translate (centpX,centpY);
Context.rotate (xAxisRotation);
Context.scale (sx, sy);
Context.arc (0, 0, rad, ang1, ang1 + angd, 1 - sweepFlag);
Context.scale (1/sx, 1/sy);
Context.rotate (-xAxisRotation);
Context.translate (-centpX, -centpY);
};      
0 голосов
/ 15 марта 2013

При попытке отобразить «M100,100 a25,50 -30 0,1 50, -25» на холст, используйте мою функцию.Конечно, я написал это с учетом дуг окружности.

эллипс (100,100,50, -25,50, ложь);

function ellipse(x1, y1, x2, y2, radius, clockwise) {

var cBx = (x1 + x2) / 2;    //get point between xy1 and xy2
var cBy = (y1 + y2) / 2;
var aB = Math.atan2(y1 - y2, x1 - x2);  //get angle to bulge point in radians
if (clockwise) { aB += (90 * (Math.PI / 180)); }
else { aB -= (90 * (Math.PI / 180)); }
var op_side = Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)) / 2;
var adj_side = Math.sqrt(Math.pow(radius, 2) - Math.pow(op_side, 2));

if (isNaN(adj_side)) {
    adj_side = Math.sqrt(Math.pow(op_side, 2) - Math.pow(radius, 2));
}

var Cx = cBx + (adj_side * Math.cos(aB));            
var Cy = cBy + (adj_side * Math.sin(aB));
var startA = Math.atan2(y1 - Cy, x1 - Cx);       //get start/end angles in radians
var endA = Math.atan2(y2 - Cy, x2 - Cx);
var mid = (startA + endA) / 2;
var Mx = Cx + (radius * Math.cos(mid));
var My = Cy + (radius * Math.sin(mid));
context.arc(Cx, Cy, radius, startA, endA, clockwise);
}
...