Ask Your Question

Concatenate bezier paths?

asked 2020-12-05 16:46:12 +0200

John L gravatar image

updated 2020-12-05 16:53:00 +0200

I have two bezier paths path1 and path2 (constructed using arc(...)). They have the same beginning point and end point, and I'd like to join them into a single path and fill the interior. What's the right way to do this? If I just try to concatenate the lists path1.path and path2.path and then call bezier_curve() and the result, it generates an error when I try to display it.

Here is a simple failed attempt.

arc1 = arc((0,1),sqrt(2),sector=(5*pi/4,7*pi/4))
arc2 = arc((0,-1),sqrt(2),sector=(pi/4,3*pi/4))

path1 = arc1[0].bezier_path()
path2 = arc2[0].bezier_path()

combo = bezier_path(path1[0].path+path2[0].path)
edit retag flag offensive close merge delete


This question is strongly connected with this other one: Filling a hyperbolic polygon. Read my answer in this related question to see a method to concatenate Bézier paths.

Juanjo gravatar imageJuanjo ( 2020-12-11 20:58:03 +0200 )edit

1 Answer

Sort by » oldest newest most voted

answered 2020-12-06 00:41:54 +0200

Juanjo gravatar image

updated 2020-12-10 01:57:33 +0200

You can't concatenate Bézier paths by simply concatenating the corresponding lists. For example, let B1 and B2 be the Bézier paths generated by the lists L1=[[p1,c1,p2], [c2,p3]] and L2=[[p3,c3,p4]], and let C be the Bézier path associated to L1+L2=[[p1,c1,p2], [c2,p3], [p3,c3,p4]]. The concatenation of B1and B2 is formed by three quadratic Bézier curves. However, C is composed by two quadratic Bézier curves and a cubic one. Note that [p3,c3,p4] in L1+L2 is interpreted as a curve starting at p3 and ending at p4 which has two control points p3 and c3, so it is a cubic Bézier curve. This situation can be clearly seen in the plot generated by the following code, where B1, B2 and C are colored in green, blue and red respectively:

L1 = [[(0,0),(1,1),(2,-1)],[(3,-3),(5,0)]]
L2 = [[(5,0),(7,3),(2,1)]]
B1 = bezier_path(L1,rgbcolor="lightgreen",thickness=3)
B2 = bezier_path(L2,rgbcolor="cyan",thickness=3)
C = bezier_path(L1+L2,rgbcolor="red",linestyle="-.")

image description

In your case there is the additional problem that, due to rounding errors, both paths have different endpoints:

sage: path1[0]
Bezier path from (-1.0000000000000004, 3.3306690738754696e-16) to (1.0, -2.220446049250313e-16)
sage: path2[0]
Bezier path from (1.0000000000000002, 0.0) to (-1.0, 2.220446049250313e-16)

It seems that you just want to fill the region limited by both paths. You can achieve this by changing some graphic options as follows:

arc1 = arc((0,1),sqrt(2),sector=(5*pi/4,7*pi/4))
arc2 = arc((0,-1),sqrt(2),sector=(pi/4,3*pi/4))

path1 = arc1[0].bezier_path()
path2 = arc2[0].bezier_path()

opts = path1[0].options()
opts.update({"fill": True, "rgbcolor": "red"})

opts = path2[0].options()
opts.update({"fill": True, "rgbcolor": "green"})

show(path1 + path2, frame=True)

This is the output:

image description

Hope this helps.

Edited (10/12/2020): An alternative route to get similar plots could be to define a function like the following one:

def circular_arc(center, radius, sector=(0,pi), segment=True, 
                 plot_points=50, **kwargs):
    step = (sector[1]-sector[0])/(plot_points-1)
    pts = [(center[0]+radius*cos(t), center[1]+radius*sin(t)) 
           for t in [sector[0]..sector[1],step=step]]
    if(not segment): pts.append(center)
    return polygon(pts,**kwargs)

This function has a syntax similar to that of arc. It plots the circular segment determined by the arc if segment=True, or the corresponding circular sector if segment=False. With this function at hand, the following code yields almost exactly the same output as above:

arc1 = circular_arc((0,1), sqrt(2), sector=(5*pi/4,7*pi/4), color="red")
arc2 = circular_arc((0,-1), sqrt(2), sector=(pi/4,3*pi/4), color="green")
show(arc1 + arc2, frame=True, aspect_ratio=golden_ratio)

Just for the purpose of comparison, let us show what happens with segment=False:

arc1 = circular_arc((0,1), sqrt(2), sector=(5*pi/4,7*pi/4), segment=False,
                    color="orange", edgecolor="red", thickness=3)
arc2 = circular_arc((0,-1), sqrt(2), sector=(pi/4,3*pi/4), segment=False, 
                    color="lightgreen", edgecolor="darkgreen",
                    thickness=3, alpha=0.5)
show(arc1 + arc2, frame=True)

image description

edit flag offensive delete link more


Nice! I think the idea is to draw hyperbolic polygons in the disc model. See

So you would want to fill a region that is bounded by several circle arcs but in a concave rather than convex way. For instance:

arc((1, -1), 1, sector((pi/2, pi))
arc((-1, -1), 1, sector((0, pi/2))
arc((-1, 1), 1, sector((-pi/2, 0))
arc((1, 1), 1, sector((-pi, -pi/2))
slelievre gravatar imageslelievre ( 2020-12-07 07:13:29 +0200 )edit

The link doesn't work. Did you mean this question? I see. Perhaps it would worthy to add fill as an allowed option for the bezier_path method, or, even better, for the arc graphics primitive.

Juanjo gravatar imageJuanjo ( 2020-12-07 12:50:20 +0200 )edit

Sorry there was a typo in the link I gave (fixed now). You correctly figured out what page I meant.

Yes, a fill option for Bezier paths would be nice! Would you open a ticket and contribute that?

slelievre gravatar imageslelievre ( 2020-12-07 16:36:09 +0200 )edit

Thinking more carefully about this question, perhaps it is not such a good idea to add the fill option. It is ambiguous what should be filled: the circular sector or the circular segment determined by the arc? There is also alternative simple ways to get the same results. See the edit to my answer.

Juanjo gravatar imageJuanjo ( 2020-12-10 01:59:58 +0200 )edit

Filling a path that closes up should make sense.

That operation is available in PostScript, SVG, TikZ, ...

Possibly in matplotlib too?

For paths that do not close up, add a final segment to close it up and fill.

slelievre gravatar imageslelievre ( 2020-12-10 02:15:38 +0200 )edit

Your Answer

Please start posting anonymously - your entry will be published after you log in or create a new account.

Add Answer

Question Tools


Asked: 2020-12-05 16:46:12 +0200

Seen: 663 times

Last updated: Dec 10 '20