Circle drawing with SVG's arc path

158,876

Solution 1

Same for XAML's arc. Just close the 99.99% arc with a Z and you've got a circle!

Solution 2

I know it's a bit late in the game, but I remembered this question from when it was new and I had a similar dillemma, and I accidently found the "right" solution, if anyone is still looking for one:

<path 
    d="
    M cx cy
    m -r, 0
    a r,r 0 1,0 (r * 2),0
    a r,r 0 1,0 -(r * 2),0
    "
/>

In other words, this:

<circle cx="100" cy="100" r="75" />

can be achieved as a path with this:

  <path 
        d="
        M 100, 100
        m -75, 0
        a 75,75 0 1,0 150,0
        a 75,75 0 1,0 -150,0
        "
  />

The trick is to have two arcs, the second one picking up where the first left off and using the negative diameter to get back to the original arc start point.

The reason it can't be done as a full circle in one arc (and I'm just speculating) is because you would be telling it to draw an arc from itself (let's say 150,150) to itself (150,150), which it renders as "oh, I'm already there, no arc necessary!".

The benefits of the solution I'm offering are:

  1. it's easy to translate from a circle directly to a path, and
  2. there is no overlap in the two arc lines (which may cause issues if you are using markers or patterns, etc). It's a clean continuous line, albeit drawn in two pieces.

None of this would matter if they would just allow textpaths to accept shapes. But I think they are avoiding that solution since shape elements like circle don't technically have a "start" point.

jsfiddle demo: http://jsfiddle.net/crazytonyi/mNt2g/

Update:

If you are using the path for a textPath reference and you are wanting the text to render on the outer edge of the arc, you would use the exact same method but change the sweep-flag from 0 to 1 so that it treats the outside of the path as the surface instead of the inside (think of 1,0 as someone sitting at the center and drawing a circle around themselves, while 1,1 as someone walking around the center at radius distance and dragging their chalk beside them, if that's any help). Here is the code as above but with the change:

<path 
    d="
    M cx cy
    m -r, 0
    a r,r 0 1,1 (r * 2),0
    a r,r 0 1,1 -(r * 2),0
    "
/>

Solution 3

In reference to Anthony’s solution, here is a function to get the path:

function circlePath(cx, cy, r){
    return 'M '+cx+' '+cy+' m -'+r+', 0 a '+r+','+r+' 0 1,0 '+(r*2)+',0 a '+r+','+r+' 0 1,0 -'+(r*2)+',0';
}

Solution 4

A totally different approach:

Instead of fiddling with paths to specify an arc in svg, you can also take a circle element and specify a stroke-dasharray, in pseudo code:

with $score between 0..1, and pi = 3.141592653589793238

$length = $score * 2 * pi * $r
$max = 7 * $r  (i.e. well above 2*pi*r)

<circle r="$r" stroke-dasharray="$length $max" />

Its simplicity is the main advantage over the multiple-arc-path method (e.g. when scripting you only plug in one value and you're done for any arc length)

The arc starts at the rightmost point, and can be shifted around using a rotate transform.

Note: Firefox has an odd bug where rotations over 90 degrees or more are ignored. So to start the arc from the top, use:

<circle r="$r" transform="rotate(-89.9)" stroke-dasharray="$length $max" />

Solution 5

For those like me who were looking for an ellipse attributes to path conversion:

const ellipseAttrsToPath = (rx,cx,ry,cy) =>
`M${cx-rx},${cy}a${rx},${ry} 0 1,0 ${rx*2},0a${rx},${ry} 0 1,0 -${rx*2},0 Z`
Share:
158,876

Related videos on Youtube

nonopolarity
Author by

nonopolarity

I started with Apple Basic and 6502 machine code and Assembly, then went onto Fortran, Pascal, C, Lisp (Scheme), microcode, Perl, Java, JavaScript, Python, Ruby, PHP, and Objective-C. Originally, I was going to go with an Atari... but it was a big expense for my family... and after months of me nagging, my dad agreed to buy an Apple ][. At that time, the Pineapple was also available. The few months in childhood seem to last forever. A few months nowadays seem to pass like days. Those days, a computer had 16kb or 48kb of RAM. Today, the computer has 16GB. So it is in fact a million times. If you know what D5 AA 96 means, we belong to the same era.

Updated on November 15, 2021

Comments

  • nonopolarity
    nonopolarity over 2 years

    Short question: using SVG path, we can draw 99.99% of a circle and it shows up, but when it is 99.99999999% of a circle, then the circle won't show up. How can it be fixed?

    The following SVG path can draw 99.99% of a circle: (try it on http://jsfiddle.net/DFhUF/1381/ and see if you see 4 arcs or only 2 arcs, but note that if it is IE, it is rendered in VML, not SVG, but have the similar issue)

    M 100 100 a 50 50 0 1 0 0.00001 0
    

    But when it is 99.99999999% of a circle, then nothing will show at all?

    M 100 100 a 50 50 0 1 0 0.00000001 0    
    

    And that's the same with 100% of a circle (it is still an arc, isn't it, just a very complete arc)

    M 100 100 a 50 50 0 1 0 0 0 
    

    How can that be fixed? The reason is I use a function to draw a percentage of an arc, and if I need to "special case" a 99.9999% or 100% arc to use the circle function, that'd be kind of silly.

    Again, a test case on jsfiddle using RaphaelJS is at http://jsfiddle.net/DFhUF/1381/
    (and if it is VML on IE 8, even the second circle won't show... you have to change it to 0.01)


    Update:

    This is because I am rendering an arc for a score in our system, so 3.3 points get 1/3 of a circle. 0.5 gets half a circle, and 9.9 points get 99% of a circle. But what if there are scores that are 9.99 in our system? Do I have to check whether it is close to 99.999% of a circle, and use an arc function or a circle function accordingly? Then what about a score of 9.9987? Which one to use? It is ridiculous to need to know what kind of scores will map to a "too complete circle" and switch to a circle function, and when it is "a certain 99.9%" of a circle or a 9.9987 score, then use the arc function.

    • nonopolarity
      nonopolarity about 13 years
      @minitech of course it works... it is then 99% of a circle (just roughly speaking). The case is that it can draw 98%, 99%, 99.99% of a circle, but not 99.9999999% or 100%
    • csakii
      csakii about 13 years
      Both of those links go to the same thing, and it works fine in Safari.
    • nonopolarity
      nonopolarity about 13 years
      right, same link, i just want people to see the test case earlier so I add the link at the beginning of the question. Right safari will do it, how nice... Chrome and Firefox won't... kind of strange coz Safari and Chrome are both Webkit... but does SVG engine depend on Webkit?
    • nonopolarity
      nonopolarity about 13 years
      @Marcin looks fine how? do you see 4 arcs or 2 arcs? did you even look at the code?
    • Marcin
      Marcin about 13 years
      No, I didn't. Did you mention there were four circles, or make them different colors, so people who are helping you can do it more easily?
    • nonopolarity
      nonopolarity almost 13 years
      @Marcin if you scan the code briefly, you can tell it is trying to draw 4 arcs... or, don't scan the code, and it is other people's fault, as usual
    • Marcin
      Marcin almost 13 years
      @動靜能量: You realise it's not my job to fix your code?
    • ericsoco
      ericsoco almost 11 years
      an updated jsfiddle with Raphael included as a library, to get around cross-origin error loading raphael.js: jsfiddle.net/DFhUF/1381
    • davidcondrey
      davidcondrey over 9 years
  • nonopolarity
    nonopolarity about 13 years
    so can you close the third case in the jsfiddle sample and make it work?
  • Todd Main
    Todd Main about 13 years
    with lowering the value to 0.0001, yes. the issue sounds related to the various browser's implementation of WebKit, not SVG itself. But that is how you make a circle in SVG/XAMLs Arc component.
  • nonopolarity
    nonopolarity about 13 years
    "You can create a circle with two elliptical arc commands"? what do you mean? Can't you just use one? At least by making to go from (0,0) to (0.01, 0) so that it renders something. For using circle, please see the Update in the question instead.
  • Phrogz
    Phrogz about 13 years
    No, I don't think you can use just one. You've shown that you can't, and you have a similar problem with HTML5 Canvas arcs.
  • nonopolarity
    nonopolarity about 13 years
    you mean, using arc to create a full circle by using the left arc and the right arc? So arc cannot draw a complete circle... to do so two arc paths are needed
  • Phrogz
    Phrogz about 13 years
    @動靜能量 Correct, two arc paths are needed (or the ugly hack of one arc going most of the way and then a closepath command to straight-line to the end).
  • dhara tcrails
    dhara tcrails over 11 years
    +1, Thanks for the formulas. Of course, the first two commands could be consolidated.
  • Ashith
    Ashith almost 11 years
    A major limitation of svg is that shape elements can't be used for text paths. This is likely the primary motivation for everyone who visits this question.
  • David John Welsh
    David John Welsh almost 11 years
    +1 for the clarity and practicality of the solution in general, but especially for 'shape elements like circle don't technically have a "start" point' - such a clear way to express the problem.
  • qbolec
    qbolec over 10 years
    I think the problem here is not that "oh, I'm already there, no arc necessary!", as by providing 1 as large-arc-flag you explicitly tell the engine that you want it to go the further way. The real problem is that there are infinitely many circles which fullfil constraints of passing trough your point. This explains why when you want 360* arc, then engine does not know what to do. The reason it does not work for 359.999 is that this is numerically unstable computation, and while there technically is exactly one circle which matches the request, it would probably not be the one you wanted anyway
  • Ashith
    Ashith over 10 years
    @qbolec - You're right, I was thinking in terms of the large-arc and sweep being off and the arc having no destination. Or at the very least that even with large-arc and sweep enabled that there was some fundamental design that defined an arc as the curve between two points (so that with one point used twice, it saw it as a collapsed single point.) Your vision of the arc potentially drawing 360 circles around the one point makes sense, though it would collapse to one circle if a center is defined, which raises the question of could a single arc make a full circle if a center did exist?
  • adius
    adius over 9 years
    Illustrator sucks. You can't make a circle with bezier curves. Only something close to a circle, but still not a circle.
  • Phrogz
    Phrogz over 9 years
    @adius actually, if you create a circle with Illustrator and save as SVG it creates a <circle> element even though illustrator shows a four-segment Bézier. Only if you adjust the handles or anchors does it export as a <path> approximation of a circle.
  • mxfh
    mxfh over 8 years
    be aware that this solution is only applicable for cases where you a not using any type of fill and you only need an arc segment with no deviations. The moment you want to draw sectors you even need a new svg path node, which could otherwise be handled in a single path tag.
  • mvds
    mvds about 8 years
    @mxfh not really, if you take stroke-width twice the value of r you get perfect sectors.
  • stenci
    stenci about 8 years
    With stroke-dasharray="0 10cm 5cm 1000cm" it is possible to avoid the transformation
  • mvds
    mvds about 8 years
    @stenci yes, but the downside is that you cannot have one parameter in the dasharray to scale from 0% to 100% fill (which was one of my objectives)
  • SimonR
    SimonR about 8 years
    Ahhh, cheating, always the best solution. Still need to handle the 100% case, but just by "rounding down" to 99.999% rather than with a whole separate code branch.
  • Medet Tleukabiluly
    Medet Tleukabiluly almost 8 years
    Any demo? This answer doesn't fulfill
  • TWiStErRob
    TWiStErRob almost 8 years
    @MedetTleukabiluly you have the arc above in the question, add a z at the end and done. You may have to remove zeroes, for example in latest Chrome I had to write 0.0001 to make it work. If you're looking for where to put the path string, look at Paths on MDN.
  • Mark
    Mark almost 8 years
    Anyone looking to JavaScript function this: function circPath(r) { return "m " + (-r) + ", 0 a " + r + "," + r + " 0 1,0 " + (r * 2) + ",0 a " + r + "," + r + " 0 1,0" + (-(r * 2)) + ",0"; }
  • Paul LeBeau
    Paul LeBeau over 7 years
    "shape elements like circle don't technically have a "start" point". This is not actually true. The SVG spec absolutely specifies where the start point of all shapes is. It has to do so in order for dash arrays to work on shapes.
  • Anton
    Anton about 7 years
    This is quite a hack plus you will not get a perfect circle. Rather see the solution below provided by Anthony (two semicircles will make it up for you).
  • loominade
    loominade almost 7 years
    This is a great answer! Just a small addition: This path is drawn East, North, West, South. If you want the circle to behave exactly like a <circle>, you have to change the direction to East, South, West, North: "M" + cx + "," + cy + "m" + (r) + ",0" + "a" + r + "," + r + " 0 1,1 " + (-r * 2) + ",0" + "a" + r + "," + r + " 0 1,1 " + (r * 2) + ",0" The advantage is that stroke-dashoffset and startOffset now work the same way.
  • Jason Foglia
    Jason Foglia about 5 years
    Here is the path cubic bezier link w3.org/TR/SVG/paths.html#PathDataCubicBezierCommands.
  • nonopolarity
    nonopolarity almost 5 years
    @ToddMain can you make jsfiddle.net/DFhUF/1381 work? I tried adding a space and then a z or Z to the end of the path, as in jsfiddle.net/m3uyxrL7/1 and it still won't show the 3rd or 4th circle
  • nonopolarity
    nonopolarity almost 5 years
    hm... so it requires 2 arcs? It would seem like it is a bit of a hack... because theoretically, there should be a clean and simple method to be able to show a 99.99 complete circle and a 99.999999999% complete circle. If you can, can you use Raphael to do it (and SVG XML as an addition) because the original requirement was using RaphaelJS (unless SVG XML should and would give identical results). But your 2 arc solution seems quite simple... can it work with: given a p% complete circle, show it, when p can be 0, 3, 10, 25, 33, 50, 70, 75, 90... any percentage at all?
  • Robert Longson
    Robert Longson almost 5 years
    @Anthony they can now. Firefox supports textPath against any shape. I suspect Chrome does too.
  • Ashith
    Ashith almost 5 years
    @RobertLongson - Can you provide an example or links to an example? Curious how it decides where the text path starts and whether it is on the outside or inside (etc) when it is drawn without a traditional starting point.
  • Ashith
    Ashith almost 5 years
    How is drawing two arcs a hack? A circle is two arcs. And drawing 99.99% -- to me -- is the hack. Hence the need for the discussion in the first place.
  • Ashith
    Ashith almost 5 years
    @太極者無極而生 - Also, no, I think as far as I have tested, it fails pretty spectacularly when you try to make a partial circle. At least when all you do is change one obvious parameter. But I've always had trouble with achieving what you are describing with paths anyway. Like wanting to throw my computer across the room frustration.
  • Robert Longson
    Robert Longson almost 5 years
    @Anthony See the SVG 2 specification e.g. w3.org/TR/SVG2/shapes.html#CircleElement, also the new textPath side attribute
  • RoboKozo
    RoboKozo over 4 years
    This is exactly what I needed. I was using the greensock morph plugin to morph a circle path to a rectangular path and needed a slight rotation to get it to look just right.
  • Måns Dahlström
    Måns Dahlström about 4 years
    This was exactly what i was looking for. Best answer here! upvote this guy
  • Soley
    Soley over 3 years
    You saved my life :) Thank you for this wonderful answer! Looking for this the entire day!
  • J. Barca
    J. Barca almost 3 years
    The bezier approximation of a circle is described at length here: spencermortensen.com/articles/bezier-circle
  • ashleedawg
    ashleedawg over 2 years
    This, functionified & compressed: function circPath(x,y,r){return['M',x,y,'m',-r,'0a',r,r,0,1,0,r*2,'0a‌​',r,r,0,1,0,-r*2,0].‌​join(' ')}