Bir yay için SVG Yolu nasıl hesaplanır (bir dairenin)


191

(200,200), yarıçap 25 merkezli bir daire göz önüne alındığında, 270 dereceden 135 dereceye ve 270 dereceden 45 dereceye kadar olan bir yayı nasıl çizebilirim?

0 derece, x ekseninde (sağ taraf) doğru olduğu anlamına gelir (yani saatin 3 pozisyonu demektir) 270 derece, saatin 12 pozisyonu ve 90, saatin 6 pozisyonu olduğu anlamına gelir.

Daha genel olarak, bir dairenin bir kısmı için bir yayın yolu nedir

x, y, r, d1, d2, direction

anlamı

center (x,y), radius r, degree_start, degree_end, direction

Yanıtlar:


381

@ Wdebeaum'un harika cevabına genişleyerek, kavisli bir yol oluşturmak için bir yöntem var:

function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
  var angleInRadians = (angleInDegrees-90) * Math.PI / 180.0;

  return {
    x: centerX + (radius * Math.cos(angleInRadians)),
    y: centerY + (radius * Math.sin(angleInRadians))
  };
}

function describeArc(x, y, radius, startAngle, endAngle){

    var start = polarToCartesian(x, y, radius, endAngle);
    var end = polarToCartesian(x, y, radius, startAngle);

    var largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1";

    var d = [
        "M", start.x, start.y, 
        "A", radius, radius, 0, largeArcFlag, 0, end.x, end.y
    ].join(" ");

    return d;       
}

kullanmak

document.getElementById("arc1").setAttribute("d", describeArc(200, 400, 100, 0, 180));

ve html'nizde

<path id="arc1" fill="none" stroke="#446688" stroke-width="20" />

Canlı demo


9
Bu süper! arcSweepDeğişkenin aslında large-arc-flagsvg A parametresini kontrol ettiğini unutmayın . Yukarıdaki kodda, sweep-flagparametre değeri her zaman sıfırdır. arcSweepmuhtemelen yeniden adlandırılmalıdır longArc.
Steven Grosmark

Teşekkürler @PocketLogic, (nihayet) önerinize göre güncellendi.
opsb

2
Gerçekten yararlı, teşekkürler. Bulduğum tek şey, negatif açıları kullanırsanız largeArc mantığının çalışmadığıdır. Bu -360 ila +360 arasında çalışır: jsbin.com/kopisonewi/2/edit?html,js,output
Xcodo

Standardın neden yayları aynı şekilde tanımlamaması noktasını kaçırıyorum.
polkovnikov.ph

4
ve az miktarda yay uzunluğunu kesmeyi unutmayın: endAngle - 0.0001değilse, tam yay oluşturulmaz.
saike

128

Eliptik Arc komutunu kullanmak istiyorsunuz . Ne yazık ki, bu sizin sahip olduğunuz kutupsal koordinatlar (yarıçap, açı) yerine başlangıç ​​ve bitiş noktalarının Kartezyen koordinatlarını (x, y) belirtmenizi gerektirir, bu yüzden biraz matematik yapmanız gerekir. İşte (ben test etmedim rağmen) çalışması gereken ve oldukça açıklayıcı umuyoruz bir JavaScript işlevi:

function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
  var angleInRadians = angleInDegrees * Math.PI / 180.0;
  var x = centerX + radius * Math.cos(angleInRadians);
  var y = centerY + radius * Math.sin(angleInRadians);
  return [x,y];
}

Hangi açıların hangi saat konumlarının koordinat sistemine bağlı olacağı; sin / cos terimlerini gerektiği gibi değiştirin ve / veya reddedin.

Arc komutu şu parametrelere sahiptir:

rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y

İlk örneğiniz için:

rx= ry= 25 ve x-axis-rotation= 0, çünkü elips değil, bir daire istiyorsunuz. MYukarıdaki işlevi kullanarak (sırasıyla, 200, 175) ve yaklaşık (182.322, 217.678), hem başlangıç ​​koordinatlarını (oveya gitmelisiniz) hem de bitiş koordinatlarını (x, y) hesaplayabilirsiniz. Şimdiye kadar bu kısıtlamalar göz önüne alındığında, aslında çizilebilecek dört yay vardır, bu yüzden iki bayrak bunlardan birini seçer. Sanırım muhtemelen large-arc-flagazalan açı yönünde (anlamı = 0) küçük bir yay çizmek istiyorsunuz (anlamı sweep-flag= 0). Hep birlikte, SVG yolu:

M 200 175 A 25 25 0 0 0 182.322 217.678

İkinci örnek için (aynı yöne ve dolayısıyla büyük bir yaya gitmek istediğinizi varsayarsak), SVG yolu:

M 200 175 A 25 25 0 1 0 217.678 217.678

Yine, bunları test etmedim.

(edit 2016-06-01) @clocksmith gibi bu API'yı neden seçtiklerini merak ediyorsanız uygulama notlarına bir göz atın . İki olası ark parametrelemesini, "uç nokta parametrelemesini" (seçtikleri) ve "merkez parametrelemesini" (sorunun kullandığı gibi) tanımlarlar. "Uç nokta parametrelendirmesi" tanımında şöyle derler:

Uç nokta parametrelemesinin avantajlarından biri, tüm yol komutlarının yeni "geçerli nokta" nın koordinatlarında bittiği tutarlı bir yol sözdizimine izin vermesidir.

Temel olarak, bu, yayların kendi ayrı nesnelerinden ziyade daha büyük bir yolun parçası olarak kabul edilmesinin bir yan etkisidir. SVG oluşturucunuz eksikse, kaç argüman aldıklarını bildiği sürece, nasıl oluşturulacağını bilmediği herhangi bir yol bileşenini atlayabileceğini düşünüyorum. Ya da belki bir yolun farklı parçalarının birçok bileşenle paralel oluşturulmasını sağlar. Ya da belki de yuvarlama hatalarının karmaşık bir yol boyunca birikmediğinden emin olmak için yaptılar.

Uygulama notları orijinal soru için de yararlıdır, çünkü iki parametrelendirme arasında dönüştürme için daha matematiksel sözde kodları vardır (bu cevabı ilk yazdığımda fark etmedim).


1
iyi görünüyor, sonra deneyeceğim. ark "daha fazla" ise (ark dairenin yarısından fazla olduğunda) ve large-arc-flag0'dan 1'e geçiş yapılması gerektiğinde bazı hatalar ortaya çıkabilir.
nonopolarite

ugh, neden bu svg yayları için api?
saatçi

17

Opsb'nin cevabını biraz değiştirdim ve daire sektörü için destek dolumu yaptım. http://codepen.io/anon/pen/AkoGx

JS

function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
  var angleInRadians = (angleInDegrees-90) * Math.PI / 180.0;

  return {
    x: centerX + (radius * Math.cos(angleInRadians)),
    y: centerY + (radius * Math.sin(angleInRadians))
  };
}

function describeArc(x, y, radius, startAngle, endAngle){

    var start = polarToCartesian(x, y, radius, endAngle);
    var end = polarToCartesian(x, y, radius, startAngle);

    var arcSweep = endAngle - startAngle <= 180 ? "0" : "1";

    var d = [
        "M", start.x, start.y, 
        "A", radius, radius, 0, arcSweep, 0, end.x, end.y,
        "L", x,y,
        "L", start.x, start.y
    ].join(" ");

    return d;       
}

document.getElementById("arc1").setAttribute("d", describeArc(200, 400, 100, 0, 220));

HTML

<svg>
  <path id="arc1" fill="orange" stroke="#446688" stroke-width="0" />
</svg>

5
Kodlama bağlantısı benim için çalışmıyor (Chrome)
Ray Hulha

Ark, SVG sınırlarının dışına çekilir. SVG en yüksekliğini ve değişim arttırın centerX, centerYörneğin 100.
A.Akram

Üst svg öğesinde açıkça bir görünüm kutusu ayarlayabilirsiniz. ÖrneğinviewBox="0 0 500 500"
Håken Lid

7

Bu eski bir sorudur ama kullanışlı kod buldum ve :) düşünme üç dakika bana küçük genişleme ekliyorum Yani kaydedilen @opsb s' cevabını .

Bu yayı bir dilime dönüştürmek istiyorsanız (doldurmaya izin vermek için) kodu biraz değiştirebiliriz:

function describeArc(x, y, radius, spread, startAngle, endAngle){
    var innerStart = polarToCartesian(x, y, radius, endAngle);
  	var innerEnd = polarToCartesian(x, y, radius, startAngle);
    var outerStart = polarToCartesian(x, y, radius + spread, endAngle);
    var outerEnd = polarToCartesian(x, y, radius + spread, startAngle);

    var largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1";

    var d = [
        "M", outerStart.x, outerStart.y,
        "A", radius + spread, radius + spread, 0, largeArcFlag, 0, outerEnd.x, outerEnd.y,
        "L", innerEnd.x, innerEnd.y, 
        "A", radius, radius, 0, largeArcFlag, 1, innerStart.x, innerStart.y, 
        "L", outerStart.x, outerStart.y, "Z"
    ].join(" ");

    return d;
}

function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
  var angleInRadians = (angleInDegrees-90) * Math.PI / 180.0;

  return {
    x: centerX + (radius * Math.cos(angleInRadians)),
    y: centerY + (radius * Math.sin(angleInRadians))
  };
}

var path = describeArc(150, 150, 50, 30, 0, 50)
document.getElementById("p").innerHTML = path
document.getElementById("path").setAttribute('d',path)
<p id="p">
</p>
<svg width="300" height="300" style="border:1px gray solid">
  <path id="path" fill="blue" stroke="cyan"></path>
</svg>

ve işte gidiyorsunuz!


5

@ opsb'nin cevapları düzgün, ancak merkez noktası doğru değil, ayrıca @Jithin belirttiği gibi, açı 360 ise, o zaman hiçbir şey çizilmez.

@ 360 sorunu düzeltti, ancak 360 dereceden az seçtiyseniz, ark döngüsünü kapatan bir satır alırsınız, bu gerekli değildir.

Bunu düzelttim ve aşağıdaki kodda bazı animasyonlar ekledim:

function myArc(cx, cy, radius, max){       
       var circle = document.getElementById("arc");
        var e = circle.getAttribute("d");
        var d = " M "+ (cx + radius) + " " + cy;
        var angle=0;
        window.timer = window.setInterval(
        function() {
            var radians= angle * (Math.PI / 180);  // convert degree to radians
            var x = cx + Math.cos(radians) * radius;  
            var y = cy + Math.sin(radians) * radius;
           
            d += " L "+x + " " + y;
            circle.setAttribute("d", d)
            if(angle==max)window.clearInterval(window.timer);
            angle++;
        }
      ,5)
 }     

  myArc(110, 110, 100, 360);
    
<svg xmlns="http://www.w3.org/2000/svg" style="width:220; height:220;"> 
    <path d="" id="arc" fill="none" stroke="red" stroke-width="2" />
</svg>


4

@Ahtenus yanıtı, özellikle Ray Hulha yorum üzerinde kodun herhangi bir yay göstermediğini söyleyerek yorum yapmak istedim, ancak itibarım yeterince yüksek değil.

Bu kod alanının çalışmamasının nedeni, html'sinin kontur genişliği sıfır ile hatalı olmasıdır.

Düzeltip buraya ikinci bir örnek ekledim: http://codepen.io/AnotherLinuxUser/pen/QEJmkN .

Html:

<svg>
    <path id="theSvgArc"/>
    <path id="theSvgArc2"/>
</svg>

İlgili CSS:

svg {
    width  : 500px;
    height : 500px;
}

path {
    stroke-width : 5;
    stroke       : lime;
    fill         : #151515;
}

Javascript:

document.getElementById("theSvgArc").setAttribute("d", describeArc(150, 150, 100, 0, 180));
document.getElementById("theSvgArc2").setAttribute("d", describeArc(300, 150, 100, 45, 190));

3

Bir görüntü ve biraz Python

Sadece daha iyi netleştirmek ve başka bir çözüm sunmak için. Arc[ A] komutu geçerli konumu başlangıç ​​noktası olarak kullanır, bu nedenle Movetoönce [M] komutunu kullanmanız gerekir .

Sonra parametreleri Arcşunlardır:

rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, xf, yf

Örneğin aşağıdaki svg dosyasını tanımlarsak:

<svg viewBox="0 0 500 500">
    <path fill="red" d="
    M 250 250
    A 100 100 0 0 0 450 250
    Z"/> 
</svg>

resim açıklamasını buraya girin

Sen ile başlayan noktayı koyacaktır Mparametreleri ile biten noktası xfve yfbir A.

Daireler arıyoruz, bu yüzden buna rxeşit olarak ayarladık, ryşimdi rxbaşlangıç ​​ve bitiş noktasını kesen yarıçap dairesini bulmaya çalışacağız .

import numpy as np

def write_svgarc(xcenter,ycenter,r,startangle,endangle,output='arc.svg'):
    if startangle > endangle: 
        raise ValueError("startangle must be smaller than endangle")

    if endangle - startangle < 360:
        large_arc_flag = 0
        radiansconversion = np.pi/180.
        xstartpoint = xcenter + r*np.cos(startangle*radiansconversion)
        ystartpoint = ycenter - r*np.sin(startangle*radiansconversion)
        xendpoint = xcenter + r*np.cos(endangle*radiansconversion)
        yendpoint = ycenter - r*np.sin(endangle*radiansconversion)
        #If we want to plot angles larger than 180 degrees we need this
        if endangle - startangle > 180: large_arc_flag = 1
        with open(output,'a') as f:
            f.write(r"""<path d=" """)
            f.write("M %s %s" %(xstartpoint,ystartpoint))
            f.write("A %s %s 0 %s 0 %s %s" 
                    %(r,r,large_arc_flag,xendpoint,yendpoint))
            f.write("L %s %s" %(xcenter,ycenter))
            f.write(r"""Z"/>""" )

    else:
        with open(output,'a') as f:
            f.write(r"""<circle cx="%s" cy="%s" r="%s"/>"""
                    %(xcenter,ycenter,r))

Yazdığım bu yayında daha ayrıntılı bir açıklama olabilir .


3

Cevap arayanlar için not (kim olduğum da) - ark kullanmak zorunlu değilse , kısmi bir daire çizmek için çok daha basit bir çözüm stroke-dasharraySVG kullanmaktır .<circle> .

Kısa çizgi dizisini iki öğeye ayırın ve aralıklarını istenen açıya ölçeklendirin. Başlangıç ​​açısı kullanılarak ayarlanabilirstroke-dashoffset .

Görünürde tek bir kosinüs değil.

Açıklamalı tam örnek: https://codepen.io/mjurczyk/pen/wvBKOvP


2

Wdebeaum'un orjinal polarToCartesian işlevi doğrudur:

var angleInRadians = angleInDegrees * Math.PI / 180.0;

Başlangıç ​​ve bitiş noktalarını aşağıdakileri kullanarak tersine çevirme:

var start = polarToCartesian(x, y, radius, endAngle);
var end = polarToCartesian(x, y, radius, startAngle);

Kafa karıştırıcı (benim için) çünkü bu süpürme bayrağını tersine çevirecek. Kullanımı:

var start = polarToCartesian(x, y, radius, startAngle);
var end = polarToCartesian(x, y, radius, endAngle);

sweep-flag = "0" ile "normal" saat yönünün tersine yaylar çizilir, ki bu daha basittir. Bkz. Https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths


2

@ Opsb'nin cevabında küçük bir değişiklik yapıldı. Bu yöntemle tam bir daire çizemiyoruz. yani (0, 360) verirsek hiçbir şey çizmez. Yani bunu düzeltmek için küçük bir değişiklik yapıldı. Bazen% 100'e ulaşan puanları görüntülemek yararlı olabilir.

function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
  var angleInRadians = (angleInDegrees-90) * Math.PI / 180.0;

  return {
    x: centerX + (radius * Math.cos(angleInRadians)),
    y: centerY + (radius * Math.sin(angleInRadians))
  };
}

function describeArc(x, y, radius, startAngle, endAngle){

    var endAngleOriginal = endAngle;
    if(endAngleOriginal - startAngle === 360){
        endAngle = 359;
    }

    var start = polarToCartesian(x, y, radius, endAngle);
    var end = polarToCartesian(x, y, radius, startAngle);

    var arcSweep = endAngle - startAngle <= 180 ? "0" : "1";

    if(endAngleOriginal - startAngle === 360){
        var d = [
              "M", start.x, start.y, 
              "A", radius, radius, 0, arcSweep, 0, end.x, end.y, "z"
        ].join(" ");
    }
    else{
      var d = [
          "M", start.x, start.y, 
          "A", radius, radius, 0, arcSweep, 0, end.x, end.y
      ].join(" ");
    }

    return d;       
}

document.getElementById("arc1").setAttribute("d", describeArc(120, 120, 100, 0, 359));

2

ES6 sürümü:

const angleInRadians = angleInDegrees => (angleInDegrees - 90) * (Math.PI / 180.0);

const polarToCartesian = (centerX, centerY, radius, angleInDegrees) => {
    const a = angleInRadians(angleInDegrees);
    return {
        x: centerX + (radius * Math.cos(a)),
        y: centerY + (radius * Math.sin(a)),
    };
};

const arc = (x, y, radius, startAngle, endAngle) => {
    const fullCircle = endAngle - startAngle === 360;
    const start = polarToCartesian(x, y, radius, endAngle - 0.01);
    const end = polarToCartesian(x, y, radius, startAngle);
    const arcSweep = endAngle - startAngle <= 180 ? '0' : '1';

    const d = [
        'M', start.x, start.y,
        'A', radius, radius, 0, arcSweep, 0, end.x, end.y,
    ].join(' ');

    if (fullCircle) d.push('z');
    return d;
};

2
ES6 şablon değişmezlerinden yararlanarak örneği daha net hale getirebilirsiniz:const d = `M ${start.x} ${start.y} A ${radius} ${radius} 0 ${largeArc} 0 ${end.x} ${end.y}`
Roy Prins

0

Seçilen cevaba dayalı ReactJS bileşeni:

import React from 'react';

const polarToCartesian = (centerX, centerY, radius, angleInDegrees) => {
    const angleInRadians = (angleInDegrees - 90) * Math.PI / 180.0;

    return {
        x: centerX + (radius * Math.cos(angleInRadians)),
        y: centerY + (radius * Math.sin(angleInRadians))
    };
};

const describeSlice = (x, y, radius, startAngle, endAngle) => {

    const start = polarToCartesian(x, y, radius, endAngle);
    const end = polarToCartesian(x, y, radius, startAngle);

    const largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1";

    const d = [
        "M", 0, 0, start.x, start.y,
        "A", radius, radius, 0, largeArcFlag, 0, end.x, end.y
    ].join(" ");

    return d;
};

const path = (degrees = 90, radius = 10) => {
    return describeSlice(0, 0, radius, 0, degrees) + 'Z';
};

export const Arc = (props) => <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 300">
    <g transform="translate(150,150)" stroke="#000" strokeWidth="2">
        <path d={path(props.degrees, props.radius)} fill="#333"/>
    </g>

</svg>;

export default Arc;

0

yukarıdaki cevap için yaptığım JSFiddle kodunu kullanabilirsiniz:

https://jsfiddle.net/tyw6nfee/

tek yapmanız gereken son satır konsol.log kodunu değiştirmek ve kendi parametrenizi vermek:

  console.log(describeArc(255,255,220,30,180));
  console.log(describeArc(CenterX,CenterY,Radius,startAngle,EndAngle))

1
Snippet'inizde yalnızca görsel bir sonuç almak için bazı değişiklikler yaptım. Bir göz atın: jsfiddle.net/ed1nh0/96c203wj/3
ed1nh0
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.