Я пытался понять arc svg, так как кажется, что я нуждаюсь в них в замысле - моя цель - построить пересечение круга.
Моя оригинальная идея была примерно такой:
для каждого пересечения, чтобы найти начальную и конечную координаты, а также высоту - но я не очень уверен, куда идти отсюда. Кажется, мне не хватает параметров вращения и большого дугового флага /Sweep, и я не уверен, как я собираюсь их восстановить. Если бы кто-нибудь мог указать мне в правильном направлении здесь, это было бы здорово!
Не знаю много о SVG arcTo. MDN дает "A rx,ry xAxisRotate LargeArcFlag,SweepFlag x,y"
. как дугу в элементе пути. Что такое rx и ry? Я бы предположил радиус для x, y.
Я предполагаю, что вы будете использовать его как
// x,y start position
// rx,ry radius x and y
// x1,y1 end position
<path d="M x,y A rx, ry, 0 1 1 x1, y1"/>
Ниже проблема решена как javascript. Я прокомментировал часть, которая вам нужна для SVG. Две конечные точки (перехваты)
Существует много избыточности, но неясно, чего вы хотите, поэтому код обеспечивает, как найти другие части двух пересекающихся кругов.
Математика для решения проблемы называется законом косинусов, который используется для решения треугольников.
В этом случае треугольник создается из 3-х длин. Каждый из радиуса окружности и один - это расстояние между центрами окружности. Изображение дает более подробную информацию
С углом c вы можете найти длины GE, DE и EF. Если вам нужен угол для другой стороны в точке f, просто замените B и C.
Переместите мышь, чтобы проверить перехват.
const ctx = canvas.getContext("2d");
const m = {
x: 0,
y: 0
};
document.addEventListener("mousemove", e => {
var b = canvas.getBoundingClientRect();
m.x = e.pageX - b.left - scrollX;
m.y = e.pageY - b.top - scrollY;
});
const PI = Math.PI;
const PI2 = Math.PI * 2;
const circles = [];
function circle(x, y, r, col, f = 0, t = PI2, w = 2) {
var c;
circles.push(c = { x, y,r, col, f, t, w});
return c;
};
function drawCircle(A) {
ctx.strokeStyle = A.col;
ctx.lineWidth = A.w;
ctx.beginPath();
ctx.arc(A.x, A.y, A.r, A.f, A.t);
ctx.stroke();
}
function mark(x, y, r, c) {
ctx.strokeStyle = c;
ctx.lineWidth = 2;
ctx.beginPath();
ctx.arc(x, y, r, 0, PI2);
ctx.stroke();
}
function line(A, B, c) {
ctx.strokeStyle = c;
ctx.lineWidth = 2;
ctx.beginPath();
ctx.lineTo(A.x, A.y);
ctx.lineTo(B.x, B.y);
ctx.stroke();
}
// note I am sharing calc results between function
function circleIntercept(A, B) {
var vx, vy, dist, c, d, x, y, x1, y1, x2, y2, dir, a1, a2;
// Vec from A to B
vx = B.x - A.x;
vy = B.y - A.y;
// Distance between
dist = Math.sqrt(vx * vx + vy * vy);
// Are the intercepting
if (dist < A.r + B.r && dist > B.r - A.r) {
c = (B.r * B.r - (dist * dist + A.r * A.r)) / (-2 * dist);
// Find mid point on cord
x = A.x + vx * (c / dist);
y = A.y + vy * (c / dist);
mark(x, y, 5, "blue");
// Find circumference intercepts
//#################################################################
//=================================================================
// SVG path
// Use x1,y1 and x2,y2 as the start and end angles of the ArcTo SVG
d = Math.sqrt(A.r * A.r - c * c);
x1 = x - vy * (d / dist);
y1 = y + vx * (d / dist);
x2 = x + vy * (d / dist);
y2 = y - vx * (d / dist);
// SVG path from above coords
// d = 'M ${x1}, ${y1} A ${A.r}, ${A,r1} 0, 1, 1, ${x2}, ${y2}';
//=================================================================
// draw the chord
line({x: x1,y: y1}, {x: x2,y: y2}, "red");
// mark the intercepts
mark(x1, y1, 5, "Green");
mark(x2, y2, 5, "Orange");
// Get direction from A to B
dir = Math.atan2(vy, vx);
// Get half inside sweep
a1 = Math.acos(c / A.r);
// Draw arc for A
A.col = "black";
A.w = 4;
A.f = dir - a1;
A.t = dir + a1;
drawCircle(A);
A.col = "#aaa";
A.w = 2;
A.f = 0;
A.t = PI2;
// inside sweep for B
a2 = Math.asin(d / B.r);
// Draw arc for B
B.col = "black";
B.w = 4;
if (dist < c) {
B.t = dir - a2;
B.f = dir + a2;
} else {
B.f = dir + PI - a2;
B.t = dir + PI + a2;
}
drawCircle(B);
B.col = "#aaa";
B.w = 2;
B.f = 0;
B.t = PI2;
}
}
var w = canvas.width;
var h = canvas.height;
var cw = w / 2; // center
var ch = h / 2;
var C1 = circle(cw, ch, ch * 0.5, "#aaa");
var C2 = circle(cw, ch, ch * 0.8, "#aaa");
function update(timer) {
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.globalAlpha = 1;
if (w !== innerWidth || h !== innerHeight) {
cw = (w = canvas.width = innerWidth) / 2;
ch = (h = canvas.height = innerHeight) / 2;
C1.x = cw;
C1.y = ch;
C1.r = ch * 0.5;
ctx.lineCap = "round";
}
C2.x = m.x;
C2.y = m.y;
ctx.clearRect(0, 0, w, h);
drawCircle(C1);
drawCircle(C2);
circleIntercept(C1, C2);
requestAnimationFrame(update);
}
requestAnimationFrame(update);
canvas {
position: absolute;
top: 0px;
left: 0px;
}
<canvas id="canvas"></canvas>
Начнем с некоторой терминологии, чтобы прояснить, что такое по часовой стрелке (помните, что ось y SVG идет вниз): первый круг имеет радиус r1
, второй r2
.
cy1 > cy2
), то укажите точку пересечения с меньшей координатой x (x1, y1)
, а другую (x2, y2)
.cy1 < cy2
), то укажите точку пересечения с большей координатой x (x1, y1)
, а другая (x2, y2)
.(x1, y1)
, а другую (x2, y2)
.Теперь мы проведем дугу от первой до второй точки пересечения с радиусом первого круга. Первыми двумя параметрами дуги являются горизонтальный и вертикальный радиус. Поскольку мы рисуем круг, оба они одинаковы. По этой же причине вращение радиусов не имеет смысла, а третий параметр равен 0.
Пересечение двух окружностей всегда использует малую дугу (большая дуга будет использоваться для объединения), поэтому большой флаг дуги равен 0. Мы рисуем дугу по часовой стрелке, поэтому флаг развертки равен 1.
Все еще неясно? Спектр использует это изображение для объяснения флагов:
Вторая дуга идет от второй к первой точке пересечения с радиусом второго круга. Флаги остаются неизменными.
Результат выглядит следующим образом:
M x1, y1 A r1 r1 0 0 1 x2, y2 A r2 r2 0 0 1 x1, y1 Z
acos((C*C-(A*A+B*B))/(-2*A*B))
- угол от линии соединения до точки пересечения.