Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

There exists at least two options to control thickness for lines and curves in 3D: thickness and radius. If the latter is given, lines are rendered as tubes. Both options work for functions like line3d or parametric_plot3d. However, although these functions are used in the implementation of bezier3d, the radius option has no effect. I haven’t found a simple workaround. But one can always adapt the code of bezier3d. Consider, for example, the following code:

from sage.misc.decorators import options, rename_keyword
@options(opacity=1, color="blue", aspect_ratio=[1,1,1], thickness=2, radius=None, plot_points='automatic')
def my_bezier3d(path, **options):
    from sage.plot.plot3d import parametric_plot3d as P3D
    from sage.modules.free_module_element import vector
    from sage.symbolic.ring import SR

    plot_options = {'color': options['color'], 'aspect_ratio': options['aspect_ratio'],
                   'thickness': options['thickness'], 'opacity': options['opacity']}
    line_options = {'color': options['color'], 'thickness': options['thickness'], 
                    'opacity': options['opacity']}
    if options['radius'] != None: 
        plot_options.update({'radius': options['radius']})
        line_options.update({'radius': options['radius']})
    if options['plot_points']=='automatic': plot_options.update({'plot_points': 75})
    p0 = vector(path[0][-1])
    t = SR.var('t')
    if len(path[0]) > 2:
        B = (1-t)**3*vector(path[0][0])+3*t*(1-t)**2*vector(path[0][1])+3*t**2*(1-t)*vector(path[0][-2])+t**3*p0
        G = P3D.parametric_plot3d(list(B), (0, 1), **plot_options)
        G = line3d([path[0][0], p0], **line_options)

    for curve in path[1:]:
        if len(curve) > 1:
            p1 = vector(curve[0])
            p2 = vector(curve[-2])
            p3 = vector(curve[-1])
            B = (1-t)**3*p0+3*t*(1-t)**2*p1+3*t**2*(1-t)*p2+t**3*p3
            G += P3D.parametric_plot3d(list(B), (0, 1), **plot_options)
            G += line3d([p0,curve[0]], **line_options)
        p0 = curve[-1]
    return G

This code defines my_bezier3d, which has exactly the same syntax as bezier3d, but it admits two additional keywords: radius and plot_points. So, we can now use it:

path_1 = [[(0,0,0),(1,0,0),(1,1,0)], [(0,1,0),(0,0,1),(1,1,1)], [(0,1,1)]]
path_2 = [[(1,0,1),(0.1,0.9,0.7),(0.1,0.9,0.3),(1,0,0)]]
path_3 = [[(0,1,0),(0.7,0.9,0.2),(1,0.8,0.4),(0.6,0.4,0.6),(0.5,0,1)], [(0,0,1),(0,0,0.5),(0.5,0.2,0)]]
curve_1 = my_bezier3d(path_1)
curve_2 = my_bezier3d(path_2, color='green', radius=0.03)
curve_3 = my_bezier3d(path_3, color='red', radius=0.01, plot_points=25)
show(curve_1+curve_2+curve_3, viewer='threejs')

See the resulting graphic in this SageMath Cell.