2014-02-16 5 views
3

В svg Я хочу построить функцию, которая возвращает все параметры для «элемента дуги» в атрибуте path-d. Учитывая начальную точку, конечную точку и сквозную точку. (3 точки на нечеткой линии соответствуют определению по кругу). Меня интересуют только круг-дуги (rx == ry).svg arc, как определить флаги развертки и больших дуг, заданных начальным концом & через точку

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

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

ответ

3

Да, вы можете определить большой-дугу и подметать флаги сегмента дуги SVG пути, глядя на углах, образованных в начале (S), с помощью (V) и конец (Е) точек, что дуга.

Для большой дуги флаг:

  • Если | ∠ SVE | > π/2 then flag = 0
  • If | ∠ SVE | < π/2, то флаг = 1
    • Вы определяете, является ли острым или тупым углом с центром на V.
    • Если этот угол «заострен» (т. Е. Острый), то вы пройдете длинный путь вокруг круга.

Для флага развертки:

  • If ∠ ESV> 0, то флаг = 0
  • Если ∠ ESV < 0, то флаг = 1
    • Вы определяете, с какой стороны линии S-to-E находится точка V.
    • Когда вы смотрите от S к E, если V находится справа, то вы будете двигаться против часовой стрелки по кругу.

Обратите внимание на следующее:

  • Порядок точек, образующих углы вопросы: S-V-E по сравнению с E-S-V для большого флага дуги и флаг развертки соответственно.
  • Все углы должны быть между - π и π, то есть между -180 ° и 180 °.
  • Абсолютное значение используется для определения большого флага дуги, но не флага развертки.

Демо-код в нижней части этого ответа демонстрирует эти расчеты. Единственный код важно для прямого ответа на вопрос, что ОП являются первые две строки:

const lgArcFl = (S,V,E) => Math.abs(angle(S,V,E)) > pi/2 ? 0 : 1; 
const sweepFl = (S,V,E) =>   angle(E,S,V) > 0 ? 0 : 1; 

Чтобы использовать демо, нажмите на кнопку «Выполнить фрагмент кода», а затем нажмите 3 раза на прямоугольнике в положение старт (S) , через (V) и конец (E) в этом порядке. Демо будет вычислять радиус и затем нарисовать все 4 дуги с различными комбинациями двух флагов, то есть 0,0, 0,1, 1,0 и 1,1. Большая и малая дуги будут соответственно синими и красными, в то время как дуги по часовой стрелке и против часовой стрелки будут сплошными и пунктирными соответственно. Одна правильная дуга будет выделена желтым цветом. После рисования дуг вы можете повторно щелкнуть 3 раза, чтобы повторить и т. Д. Обратите внимание, что если дважды щелкнуть в одном месте или щелкнуть 3х по прямой линии, то не получится никаких дуг, как и ожидалось бы геометрически.

Обратите внимание, что с момента моего первоначального опубликования этого ответа (5 ноября 2016 года) демонстрация работает в Chrome, Opera и Safari, но не в Firefox. (Я не проверял Explorer или Edge, ни мобильные браузеры.) Я подозреваю, что это может быть потому, что я использовал код ES6/ES2015. Однако нажатие кнопки «Использовать BabelJS/ES2015» в редакторе кода делает код незаменимым по причинам, которые я не понимаю. Итак, если у вас возникли проблемы с работой демо, возможно, попробуйте другой браузер.

const lgArcFl = (S,V,E) => Math.abs(angle(S,V,E)) > pi/2 ? 0 : 1; 
 
const sweepFl = (S,V,E) =>   angle(E,S,V) > 0 ? 0 : 1; 
 

 
const angle = ([a,b],[c,d],[e,f]) => (Math.atan2(f-d,e-c)-Math.atan2(b-d,a-c)+3*pi)%(2*pi)-pi; 
 
const qs = sel => document.querySelector(sel), pi = Math.PI, pts = []; 
 
const radius = ([a,b],[c,d],[e,f]) => { 
 
    const g=c-a,h=2*(c-e)/g,i=d-b,j=c*c+d*d,k=j-a*a-b*b,l=(j-e*e-f*f-h*k/2)/(2*(d-f)-h*i); 
 
    return Math.hypot(a+(i*l-k/2)/g,b-l); 
 
}; 
 
const mkArc = (arc, [sx, sy], [ex, ey], r, lg, sw) => arc.setAttribute('d', 
 
    `M ${sx} ${sy} A ${r} ${r} 0 ${lg} ${sw} ${ex} ${ey}`); 
 
const calcArcs = (S,V,E) => { 
 
    const args = [S, E, radius(S,V,E)]; 
 
    [[0,0],[0,1],[1,0],[1,1]].forEach(([lg,sw]) => mkArc(qs(`#arc${lg}${sw}`), ...args, lg, sw)); 
 
    mkArc(qs(`#arc`), ...args, lgArcFl(S,V,E), sweepFl(S,V,E)); 
 
}; 
 
let ptNum = 0; 
 
qs('svg').addEventListener('click', evt => { 
 
    const x = evt.x - 10, y = evt.y - 10; 
 
    pts[ptNum] = [x, y]; 
 
    qs('#pt' + ptNum).setAttribute('transform', `translate(${x},${y})`); 
 
    if (ptNum++ === 2) { 
 
    calcArcs(...pts); 
 
    ptNum = 0; 
 
    } 
 
});
text { 
 
    font-family: courier; 
 
    font-size: 18px; 
 
    fill: black; 
 
    stroke: none; 
 
    transform: translate(-5px,5px); 
 
} 
 
#arc00, #arc01 { 
 
    stroke: red; 
 
} 
 
#arc10, #arc11 { 
 
    stroke: blue; 
 
} 
 
#arc00, #arc10 { 
 
    stroke-width: 4; 
 
    stroke-dasharray: 5,5; 
 
} 
 
#arc01, #arc11 { 
 
    stroke-width: 2; 
 
} 
 
rect { 
 
    fill: none; 
 
    stroke: black; 
 
    height: 200px; 
 
    width: 600px; 
 
}
<svg height="200" width="600"> 
 
    <defs> 
 
    <path id="arc" /> 
 
    <circle id="circ" cx="0" cy="0" r="10" /> 
 
    </defs> 
 
    <g fill="none"> 
 
    <use xlink:href="#arc" stroke="black" stroke-width="12" /> 
 
    <use xlink:href="#arc" stroke="#ff8" stroke-width="10" /> 
 
    <path id="arc00" /> 
 
    <path id="arc01" /> 
 
    <path id="arc10" /> 
 
    <path id="arc11" /> 
 
    </g> 
 
    <g fill="#ff8" stroke="black"> 
 
    <g id="pt0" transform="translate(-99,0)"><use xlink:href="#circ" /><text>S</text></g> 
 
    <g id="pt1" transform="translate(-99,0)"><use xlink:href="#circ" /><text>V</text></g> 
 
    <g id="pt2" transform="translate(-99,0)"><use xlink:href="#circ" /><text>E</text></g> 
 
    </g> 
 
    <rect /> 
 
</svg>

2

Недавно я немного поработал над круговыми дугами SVG и использовал следующие значения для дуговой развертки и значений d. Это может быть полезно.

//---x1,y1 and x2,y2 are the two end points--- 
    function polarToCartesian(centerX, centerY,radius, angleInDegrees) 
    { 
     var angleInRadians = (angleInDegrees) * Math.PI/180.0; 

     return { 
     x: centerX + (radius * Math.cos(angleInRadians)), 
     y: centerY + (radius * Math.sin(angleInRadians)) 
     }; 
    } 
    var startAngle = 180/Math.PI*Math.atan2(y1-cy, x1-cx); 
    var endAngle = 180/Math.PI*Math.atan2(y2-cy, x2-cx); 

    StartPnt = polarToCartesian(cx, cy, radius, startAngle); 
    EndPnt = polarToCartesian(cx, cy, radius, endAngle); 
    ArcSweep = endAngle - startAngle <= 180 ? "0" : "1"; 

    var d = [ 
    "M", StartPnt.x, StartPnt.y, 
    "A", radius, radius, 0, ArcSweep, 0, EndPnt.x, EndPnt.y 
    ].join(" "); 
+1

Это не правильно. Дуга SVG представляет собой «радиус радиуса xrot-deg largearc sweep endx endy». То, что вы называете ArcSweep, на самом деле является флагом largeArc, который равен «1», если угол равен> PI. Флаг развертки определяет, нарисован ли он по часовой стрелке или CCW, и имеет эффект отражения дуги относительно линии между началом/концом. – Marvin

 Смежные вопросы

  • Нет связанных вопросов^_^