2

I'm trying to draw a bezier curve surrounding an ellipse with a given margin :

Bezier path surrounding an ellipse

I want to achieve this programmatically, so if I changes the ellipse size, the curve will follow it.

At the moment I've made this function :

function bezierPathTopRounded(ellipse, margin) {
    var box = ellipse.paper.getBBox();

    var leftX = box.x - margin; 

    var rightX = box.x + margin + box.width;

    var y = box.y + box.height/2 - margin; 

    var p = "M "+ leftX  + ", "+ y
    + " C " //could be relative too
    + ( box.x - margin + (box.width/15)  )  + ", " + ( box.y + margin - (box.height/4.5)  ) + " "
    + ( box.x + margin + box.width - (box.width/15)  )  + ", " + ( box.y + margin - (box.height/4.5) ) + " "
    + rightX +", " + y;

    return p;   
}

But I can't figure out how to calculate this direction points values so that it will work with any ellipse :

  • box.width/15
  • box.height/4.5

There is a fiddle with this example.

I've read this stackoverflow question and I tried the same on my example, but still can't figure out a simple solution, it remains random...

Edit

Now I'm trying with an elliptical Arc, the result is worser than with a Bezier Path :

Elliptical Arc test

There is the function I'm using. If I remove the margin it follows exactly my ellipse... Finally this is the matter is how may I follow the ellipse with a margin ?

function borderPath(ellipse, margin, flag) {
    var flag = flag == undefined ? 1 : 0;

    var box = ellipse.paper.getBBox();

    var leftX = box.x - margin;

    var rightX = box.x + margin + box.width;

    var y = box.y + box.height/2;
    y += (flag == 1) ? -margin : margin;

    var rx = box.width/2 + margin;
    var ry = box.height/2;

    var p = "M "+ leftX + ", "+ y
    + " A "
    + rx + " " + ry
    + " 0 0 "+ flag +" "
    + rightX +", " + y;

    return p;
}

See the updated fiddle here.

Really sorry for the awful colors, those are for example purpose.

Community
  • 1
  • 1
soyuka
  • 8,839
  • 3
  • 39
  • 54
  • Why Bezier? Why not draw another partial ellipse of appropriate size/position? – Beetroot-Beetroot Apr 19 '13 at 08:16
  • You meen with an elliptical arc ? I tried this one before, but It wasn't possible because of the margin it's not a perfect ellipse anymore. And I need it to be a path so I can draw this specific line. – soyuka Apr 19 '13 at 08:28
  • Yes I mean an elliptical arc, though I see your point that concentric ellipses may not give the effect you seek. Having said that I think a Bezier curve will only ever be an approximation. How did you derive `box.width/15` and `box.height/4.5`? Is it not a question of generalizing the Math? – Beetroot-Beetroot Apr 19 '13 at 09:10
  • I tried a lot before having this both numbers. It might be a question of generalizing the Math, dunno... I'll try again with elliptical arc and let you know. – soyuka Apr 19 '13 at 09:14
  • I've updated with an elliptical arc, but the result is still wrong... – soyuka Apr 19 '13 at 16:59

3 Answers3

2

If you want to do this with Bezier curves, you'll have to decide how "wrong" you want it to look. Bezier curves cannot represent circular (and by extension, elliptical) curves, they can only get really close, in the same way a polygon can, just with higher precision using fewer sections.

I describe both circle-approximation and curve offsetting using Bezier curves in my primer on Bezier curves, http://pomax.github.io/bezierinfo/#circles_cubic and http://pomax.github.io/bezierinfo/#offsetting respectively, but if you're coding this from scratch particularly the offsetting will be overkill if you only need it for what you describe in your example.

Instead, I'd recommend firing up Inkscape or Illustrator, turning on the grid overlay, and drawing a bezier curve around your ellipse. Make it look good, then check what the coordinates are, and use that as reliable-enough information for implementing in your canvas program. You probably don't need mathematically rigidly correct, as long as people don't go "that looks wrong", you should be just fine.

Mike 'Pomax' Kamermans
  • 49,297
  • 16
  • 112
  • 153
  • Nice articles you got there ! I agree with the fact that they can't be perfectly right, but on my example it looks not so "wrong", didn't it ? I can't do them with Inkscape etc. because I want to animate them according to the ellipse. My solution so far is having 2 different functions for the path (beginning, ending). Now if I wanted to have "perfect" curves, should I use elliptical arcs ? — which won't be right too because it's cut by the margin – soyuka Apr 19 '13 at 12:18
  • Using an elliptical arc would probably be good enough for you, as long as you offset both radii by the same amount. So if your ellipse is (x,y,r1,r2) and you want an offset over half the ellipse of, say, 10px, you'd use arc(x,y,r1+10,r2+10, pi,2*pi), with the last two indicating the start and end angle for your arc. The problem here is that if you're using Canvas2D, the only arc available at the moment is the circular arc. The elliptical arc command ("ellipse") is finally in the updated draft (I'm one of the people who requested it, very happy it has been added now) but is not supported yet – Mike 'Pomax' Kamermans Apr 19 '13 at 15:17
  • I'm using Raphael (svg no canvas so the `A` path - `rx ry x-axis-rotation large-arc-flag sweep-flag x y`) but I'll try finding a way with this info thanks. – soyuka Apr 19 '13 at 16:15
  • 1
    What appears to be required here is to plot a thin line at the outer edge of a margin of constant width around the ellipse. If so, then the margin can be calculated as the locus of a constant projection of the normal to the ellipse - [equation here](http://www.codecogs.com/reference/maths/geometry/coordinate/ellipse.php). I doubt that such a locus is an ellipse; for an ellipse of any eccentricity, it will tend to a circle as the margin width increases. – Beetroot-Beetroot Apr 20 '13 at 08:23
  • If you can make it work, then the result should be good but there's quite a lot of work to code up the equation, calculate the projection, and plot the locus. As the equation is Cartesian, not polar, you may well have to treat the top/bottom and left/right arcs of the ellipse separately - otherwise the iterative increments will tend to infinitesimal. – Beetroot-Beetroot Apr 20 '13 at 08:24
1

I've manage to make an elliptical arc according to the ellipse and its margin. Than i'm simply hiding the part I don't want with a rectangle.

Here is the function :

function borderPath(ellipse, flag) {
    var flag = flag == undefined ? 1 : flag;

    var box = ellipse.paper.getBBox();

    var leftX = box.x;

    var rightX = box.x + box.width;

    var y = box.y + box.height/2;

    var rx = box.width/2;
    var ry = box.height/2;

    var p = "M "+ leftX + ", "+ y
    + " A "
    + rx + " " + ry
    + " 0 0 "+ flag +" "
    + rightX +", " + y;

    return p;
}
soyuka
  • 8,839
  • 3
  • 39
  • 54
0

Using bezier curves to draw elliptical path may cause you headaches. As you said in a comment, you are using path arc which works well with RaphaelJS.

Documentation about all the values it expects, especially the flags, can be found at http://www.svgbasics.com/arcs.html .

atondelier
  • 2,424
  • 15
  • 11