So berechnen Sie den SVG-Pfad für einen Bogen (eines Kreises)

Bei einem Kreis um (200,200), Radius 25, wie zeichne ich einen Bogen von 270 Grad bis 135 Grad und einen von 270 Grad bis 45 Grad?

0 Grad bedeutet, dass es rechts auf der x-Achse (die rechte Seite) ist (was bedeutet, dass es 3 Uhr Position ist) 270 Grad bedeutet, dass es 12 Uhr Position ist, und 90 bedeutet, dass es 6 Uhr Position ist

Allgemeiner, was ist ein Pfad für einen Bogen für einen Teil eines Kreises mit

x, y, r, d1, d2, direction 

Bedeutung

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

   

Erweiternd auf @wdebeaums große Antwort, hier ist eine Methode zum Erzeugen eines bogenförmigen Pfades:

 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; } 

benutzen

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

und in deinem HTML

  

Live-Demo

Sie möchten den elliptischen A rc -Befehl verwenden . Leider müssen Sie dazu die kartesischen Koordinaten (x, y) der Anfangs- und Endpunkte angeben und nicht die Polarkoordinaten (Radius, angular), die Sie haben. Sie müssen also etwas rechnen. Hier ist eine JavaScript-function, die funktionieren sollte (obwohl ich es nicht getestet habe), und die ich hoffe, ist ziemlich selbsterklärend:

 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]; } 

Welche angular welchen Uhrpositionen entsprechen, hängt vom Koordinatensystem ab; Tausche und / oder negiere die sin / cos-Terme wie nötig.

Der Befehl arc hat folgende Parameter:

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

Für dein erstes Beispiel:

rx = ry = 25 und x-axis-rotation = 0, da Sie einen Kreis und keine Ellipse wollen. Mit der obigen function können Sie sowohl die Startkoordinaten (die Sie bearbeiten sollten) als auch die Endkoordinaten (x, y) berechnen, wobei Sie (200, 175) bzw. ungefähr (182.322, 217.678) erhalten. Angesichts dieser Einschränkungen gibt es tatsächlich vier Bögen, die gezeichnet werden können, so dass die zwei Flags eines auswählen. Ich nehme an, Sie möchten wahrscheinlich einen kleinen Bogen zeichnen (dh large-arc-flag = 0), in Richtung abnehmender angular (also sweep-flag = 0). Insgesamt ist der SVG-Pfad:

 M 200 175 A 25 25 0 0 0 182.322 217.678 

Für das zweite Beispiel (vorausgesetzt, Sie meinen, in dieselbe Richtung zu gehen und somit einen großen Bogen), lautet der SVG-Pfad:

 M 200 175 A 25 25 0 1 0 217.678 217.678 

Auch hier habe ich diese nicht getestet.

(edit 2016-06-01) Wenn Sie sich wie @clocksmith fragen, warum sie diese API gewählt haben, schauen Sie sich die Implementierungshinweise an . Sie beschreiben zwei mögliche Bogenparametrisierungen, die “Endpunktparametrierung” (diejenige, die sie gewählt haben) und die “Centerparametrisierung” (die ähnlich wie die Frage ist). In der Beschreibung der “Endpunktparametrierung” sagen sie:

Einer der Vorteile der Endpunktparametrierung ist, dass sie eine konsistente Pfadsyntax erlaubt, bei der alle Pfadbefehle in den Koordinaten des neuen “aktuellen Punktes” enden.

Im Grunde ist es ein Nebeneffekt von Bögen, die als Teil eines größeren Pfades und nicht als ihr eigenes separates Objekt betrachtet werden. Ich nehme an, dass, wenn Ihr SVG-Renderer unvollständig ist, er einfach alle Pfadkomponenten überspringen kann, die er nicht zu rendern weiß, solange er weiß, wie viele Argumente er nimmt. Oder vielleicht ermöglicht es das parallele Rendern verschiedener Teile eines Pfades mit vielen Komponenten. Oder vielleicht haben sie es getan, um sicherzustellen, dass sich Rundungserrors nicht entlang der Länge eines komplexen Pfades bilden.

Die Implementierungshinweise sind auch für die ursprüngliche Frage nützlich, da sie einen mathematischeren Pseudocode zum Konvertieren zwischen den beiden Parametrisierungen haben (was ich beim ersten Schreiben dieser Antwort nicht bemerkt habe).

Ich änderte die Antwort von opsb leicht und machte Unterstützung für den Kreissektor. 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

    

Ich wollte die @ Ahtenus Antwort kommentieren, speziell auf Ray Hulha Kommentar, der sagt, dass der Codepen keinen Bogen zeigt, aber mein Ruf ist nicht hoch genug.

Der Grund dafür, dass dieser Codepen nicht funktioniert, ist, dass sein HTML mit einer Strichbreite von Null errorshaft ist.

Ich habe es behoben und ein zweites Beispiel hier hinzugefügt: http://codepen.io/AnotherLinuxUser/pen/QEJmkN .

Der html:

     

Das relevante CSS:

 svg { width : 500px; height : 500px; } path { stroke-width : 5; stroke : lime; fill : #151515; } 

Das Javascript:

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

@ opsbs Antworten sind ordentlich, aber der Mittelpunkt ist nicht genau, außerdem, wie @Jithin bemerkte, wenn der angular 360 ist, dann wird überhaupt nichts gezeichnet.

@Jithin hat das 360-Problem behoben, aber wenn Sie weniger als 360 Grad gewählt haben, erhalten Sie eine Linie, die die Bogenschleife schließt, was nicht erforderlich ist.

Ich habe das behoben und eine Animation im folgenden Code hinzugefügt:

 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); 
    

Die ursprüngliche polarToCartesian function von wdebeaum ist korrekt:

 var angleInRadians = angleInDegrees * Math.PI / 180.0; 

Umkehren von Start- und Endpunkten mit:

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

Ist verwirrend (für mich), weil dies die Sweep-Flagge umkehren wird. Verwenden:

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

Mit dem Sweep-Flag = “0” werden “normale” gegen den Uhrzeigersinn gerichtete Lichtbögen gezogen, was meiner Ansicht nach einfacher ist. Siehe https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths

Eine kleine Änderung an @ opsbs Antwort. Mit dieser Methode können wir keinen vollen Kreis zeichnen. Dh wenn wir (0, 360) geben, wird es überhaupt nichts zeichnen. Also eine kleine Änderung gemacht, um dies zu beheben. Es könnte nützlich sein, Noten anzuzeigen, die manchmal 100% erreichen.

 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)); 

Dies ist eine alte Frage, aber ich fand den Code nützlich und ersparte mir drei Minuten des Nachdenkens 🙂 Also füge ich @opsbs Antwort eine kleine Erweiterung hinzu .

Wenn Sie diesen Bogen in einen Slice umwandeln möchten (um die Füllung zu ermöglichen), können wir den Code leicht modifizieren:

 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) 
 

Sie können den JSFiddle-Code verwenden, den ich oben für die Antwort erstellt habe:

https://jsfiddle.net/tyw6nfee/

Alles, was Sie tun müssen, ist letzte Zeile console.log Code ändern und geben Sie Ihren eigenen Parameter:

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

Ein Bild und etwas Python

Nur um besser zu klären und eine andere Lösung anzubieten. Arc Befehl Arc [ A ] verwendet die aktuelle Position als Ausgangspunkt, daher müssen Sie zuerst den Befehl Moveto [M] verwenden.

Dann sind die Parameter von Arc wie folgt:

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

Wenn wir zum Beispiel die folgende SVG-Datei definieren:

  

Der folgende Code gibt Ihnen dieses Ergebnis:

    

Bildbeschreibung hier eingeben

Sie setzen den Startpunkt mit M den Endpunkt mit den Parametern xf und yf von A

Wir suchen nach Kreisen, also setzen wir rx gleich wie ry , also versucht es jetzt im Grunde, den ganzen Kreis des Radius rx , der den Anfangs- und Endpunkt schneidet.

 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"""""" ) else: with open(output,'a') as f: f.write(r"""""" %(xcenter,ycenter,r)) 

Sie können eine ausführlichere Erklärung in diesem Post haben , den ich schrieb.

ReactJS-Komponente basierend auf der ausgewählten Antwort:

 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) =>     ; export default Arc; 

ES6-Version:

 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; };