Opened 14 months ago

Last modified 7 months ago

#26410 new defect

Make threejs / three.js viewer respect thickness in parametric_plot3d

Reported by: slelievre Owned by:
Priority: major Milestone: sage-wishlist
Component: graphics Keywords: threejs, three.js, parametric_plot3d, thickness, graphic options
Cc: slelievre Merged in:
Authors: Reviewers:
Report Upstream: N/A Work issues:
Branch: Commit:
Dependencies: Stopgaps:

Description (last modified by slelievre)

On Windows and macOS, WebGL line thickness support is absent or limited, so that the three.js / threejs viewer appears not to respect the thickness optional parameter in parametric_plot3d, while other viewers such as Jmol and Tachyon do.

For example, in Sage 8.3 or 8.7 on macOS 10.10.5, the thickness is not taken into account in the following -- whether launching it from the Sage REPL, the Jupyter notebook, or even SageCell online:

sage: pi = RDF.pi()
sage: tau = 2*pi
sage: a = (lambda t: cos(t), lambda t: sin(t), lambda t: 2*t/tau)
sage: b = (lambda t: cos(t), lambda t: sin(t), lambda t: 2*t/tau + 1)
sage: aa = parametric_plot(a, (-tau, tau), color='blue', thickness=1)
sage: bb = parametric_plot(b, (-3*pi, pi), color='red', thickness=10)
sage: p = aa + bb
sage: p.show(viewer='threejs')
Launched html viewer for Graphics3d Object
sage: p.show(viewer='jmol')
Launched jmol viewer for Graphics3d Object
sage: p.show(viewer='tachyon')
Launched png viewer for Graphics3d Object

Suggested workarounds:

  • use radius instead of thickness.
  • use THREE.Meshline

Change History (8)

comment:1 Changed 14 months ago by slelievre

  • Description modified (diff)
  • Keywords threejs three.js parametric_plot3d thickness graphic options added

comment:2 Changed 8 months ago by paulmasson

Samuel, I don't see what the problem is: the red line looks thicker to me than the blue in the Three.js viewer. What are your expecting?

comment:3 Changed 8 months ago by slelievre

Paul, thanks a lot for having a look. For me the red line looks thicker in jmol and tachyon, but not in threejs. Not only on my computer, but also on sagecell.

comment:4 Changed 8 months ago by paulmasson

That's probably because you're using a Windows machine. WebGL line thickness isn't supported at all in Windows, and support for it in macOS has limitations. It looks like line thickness will be removed from future versions of WebGL entirely.

The way to visualize thick lines is to use the radius parameter listed in the documentation, which creates a cylinder along the path:

pi = RDF.pi()
tau = 2*pi
a = (lambda t: cos(t), lambda t: sin(t), lambda t: 2*t/tau)
b = (lambda t: cos(t), lambda t: sin(t), lambda t: 2*t/tau + 1)
aa = parametric_plot(a, (-tau, tau), color='blue', thickness=1)
bb = parametric_plot(b, (-3*pi, pi), color='red', radius=.1)
p = aa + bb
p.show(viewer='threejs')

Three.js has its own workaround for thick lines, but it's only in the examples, not the main library. I wouldn't implement it here until it's an official part of the main library.

comment:5 Changed 8 months ago by slelievre

Thanks for explaining and for the workaround.

This was under macOS 10.10.5, using either Firefox 66.0.2 (64-bit), Opera 58.0.3135.118, Safari 10.1.2 (10603.3.8), so surely illustrating the limitations you refer to.

Using radius as you suggest does the trick. A small enough radius works well. If the curve is long and the radius gets somewhat large it is worth increasing the number of plot points so that the (piecewise linear) cylinder still looks more curved than piecewise linear.

The following works well:

pi = RDF.pi()
tau = 2*pi
a = (lambda t: cos(t), lambda t: sin(t), lambda t: 2*t/tau)
b = (lambda t: cos(t), lambda t: sin(t), lambda t: 2*t/tau + 1)
aa = parametric_plot(a, (-tau, tau), color='blue', radius=.02, plot_points=400)
bb = parametric_plot(b, (-3*pi, pi), color='red', radius=.04, plot_points=400)
p = aa + bb
p.show(viewer='threejs')

I also notice that the cylinders have a polygonal base, with a number of sides depending on the radius; it seems to start 5-sided for small radius, and then the number of sides grows with the radius. Is there a way to control that number of sides?

The following example shows the number of sides starting at 5 then increasing to 6, 7, 8, 8, 9, 10, 11 (makes one want to guess the fomula for number_of_sides as a function of radius):

pp = parametric_plot
cyl = lambda x, y: (lambda t: x, lambda t: y, lambda t: t)
pcyl = lambda x, y, r, h: pp(cyl(x, y), (0, 1), radius=r, rgbcolor=hue(h))
param = (.03, .04, .05, .06, .07, .08, .09, .10, .11, .12, .13, .14, .15, .16)
np = RDF(len(param))
cylparam = lambda i, r: (0, 2.05*sum(param[:i])+r, r, (i+1)/(np+1))
p = sum((pcyl(*cylparam(i, r)) for i, r in enumerate(param)), Graphics())
p.show(frame=False, viewer='threejs')

comment:6 Changed 8 months ago by embray

I have encountered this problem myself, about a year ago, with my own threejs experiments outside of Sage. It's really too bad this is still a problem. IIRC the best workaround I found was to use the THREE.MeshLine plugin: https://github.com/spite/THREE.MeshLine

In particular, check out this cool demo: https://www.clicktorelease.com/code/THREE.MeshLine/demo/index.html

Perhaps we could work that into Sage for use in parametric plots?

comment:7 Changed 7 months ago by slelievre

  • Description modified (diff)
  • Milestone changed from sage-8.4 to sage-wishlist

comment:8 Changed 7 months ago by slelievre

Thanks! Very cool demo indeed -- it would be very nice to work that into Sage.

Note: See TracTickets for help on using tickets.