Skip to content

Commit

Permalink
Rename font options to be consistent with 2D options
Browse files Browse the repository at this point in the history
Also:
- Support integer and keyword weights and keyword sizes
- Make sure font* options carry over from 2D text via plot3d
  • Loading branch information
Joshua Campbell committed Sep 23, 2020
1 parent 8873e14 commit 6a6d33e
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 31 deletions.
11 changes: 5 additions & 6 deletions src/sage/ext_data/threejs/threejs_template.html
Original file line number Diff line number Diff line change
Expand Up @@ -109,15 +109,13 @@
}

function addLabel( text, x, y, z, color='black', fontSize=14, fontFamily='monospace',
italic=false, bold=false, opacity=1 ) {
fontStyle='normal', fontWeight='normal', opacity=1 ) {

var canvas = document.createElement( 'canvas' );
var context = canvas.getContext( '2d' );
var pixelRatio = Math.round( window.devicePixelRatio );

var font = fontSize + 'px ' + fontFamily;
if ( italic ) font = 'italic ' + font;
if ( bold ) font = 'bold ' + font;
var font = [fontStyle, fontWeight, fontSize + 'px', fontFamily].join(' ');

context.font = font;
var width = context.measureText( text ).width;
Expand Down Expand Up @@ -258,12 +256,13 @@
for ( var i=0 ; i < texts.length ; i++ ) addText( texts[i] );

function addText( json ) {
json.font = json.font.map( function( f ) {
json.fontFamily = json.fontFamily.map( function( f ) {
// Need to put quotes around fonts that have whitespace in their names.
return /\s/.test( f ) ? '"' + f + '"' : f;
}).join(', ');
var sprite = addLabel( json.text, a[0]*json.x, a[1]*json.y, a[2]*json.z, json.color,
json.size, json.font, json.italic, json.bold, json.opacity );
json.fontSize, json.fontFamily, json.fontStyle, json.fontWeight,
json.opacity );
sprite.userData = json;
}

Expand Down
82 changes: 65 additions & 17 deletions src/sage/plot/plot3d/shapes.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1169,16 +1169,17 @@ class Text(PrimitiveObject):
EXAMPLES::
sage: T = text3d("Hi", (1, 2, 3), color='red', font='serif', bold=True,
....: italic=True, size=20, opacity=0.5)
sage: T = text3d("Hi", (1, 2, 3), color='red', fontfamily='serif',
....: fontweight='bold', fontstyle='italic', fontsize=20,
....: opacity=0.5)
sage: T.threejs_repr(T.default_render_params())
[('text',
{'bold': True,
'color': '#ff0000',
'font': ['serif'],
'italic': True,
{'color': '#ff0000',
'fontFamily': ['serif'],
'fontSize': 20.0,
'fontStyle': 'italic',
'fontWeight': 'bold',
'opacity': 0.5,
'size': 20,
'text': 'Hi',
'x': 1.0,
'y': 2.0,
Expand All @@ -1193,12 +1194,12 @@ class Text(PrimitiveObject):
sage: T = Text("Hi")
sage: T.threejs_repr(T.default_render_params())
[('text',
{'bold': False,
'color': '#6666ff',
'font': ['monospace'],
'italic': False,
{'color': '#6666ff',
'fontFamily': ['monospace'],
'fontSize': 14.0,
'fontStyle': 'normal',
'fontWeight': 'normal',
'opacity': 1.0,
'size': 14,
'text': 'Hi',
'x': 0.0,
'y': 0.0,
Expand All @@ -1208,18 +1209,65 @@ class Text(PrimitiveObject):
center = (float(0), float(0), float(0))
if render_params.transform is not None:
center = render_params.transform.transform_point(center)

color = '#' + str(self.texture.hex_rgb())
string = str(self.string)
size = int(self._extra_kwds.get('size', 14))
font = self._extra_kwds.get('font', ['monospace'])

default_size = 14.0
size = self._extra_kwds.get('fontsize', default_size)
try:
size = float(size)
except (TypeError, ValueError):
scale = str(size).lower()
if scale.endswith('%'):
try:
scale = float(scale[:-1]) / 100.0
size = default_size * scale
except ValueError:
import warnings
warnings.warn(f"invalid fontsize: {size}, using: {default_size}")
size = default_size
else:
from matplotlib.font_manager import font_scalings
try:
size = default_size * font_scalings[scale]
except KeyError:
import warnings
warnings.warn(f"unknown fontsize: {size}, using: {default_size}")
size = default_size

font = self._extra_kwds.get('fontfamily', ['monospace'])
if isinstance(font, str):
font = font.split(',')
font = [str(f).strip() for f in font]
italic = bool(self._extra_kwds.get('italic'))
bold = bool(self._extra_kwds.get('bold'))

default_style = 'normal'
style = str(self._extra_kwds.get('fontstyle', default_style))
if style not in ['normal', 'italic'] and not style.startswith('oblique'): # ex: oblique 30deg
import warnings
warnings.warn(f"unknown style: {style}, using: {default_style}")
style = default_style

default_weight = 'normal'
weight = self._extra_kwds.get('fontweight', default_weight)
if weight not in ['normal', 'bold']:
try:
weight = int(weight)
except:
from matplotlib.font_manager import weight_dict
try:
weight = weight_dict[weight]
except KeyError:
import warnings
warnings.warn(f"unknown fontweight: {weight}, using: {default_weight}")
weight = default_weight

opacity = float(self._extra_kwds.get('opacity', 1.0))

text = dict(text=string, x=center[0], y=center[1], z=center[2], color=color,
size=size, font=font, italic=italic, bold=bold, opacity=opacity)
fontSize=size, fontFamily=font, fontStyle=style, fontWeight=weight,
opacity=opacity)

return [('text', text)]

def bounding_box(self):
Expand Down
19 changes: 12 additions & 7 deletions src/sage/plot/plot3d/shapes2.py
Original file line number Diff line number Diff line change
Expand Up @@ -693,13 +693,18 @@ def text3d(txt, x_y_z, **kwds):
sage: text3d("Sage is...",(2,12,1), color=(1,0,0)) + text3d("quite powerful!!",(4,10,0), color=(0,0,1))
Graphics3d Object
Adjust the font size, family, and style (Three.js viewer only)::
sage: t1 = text3d("Hello", (1, 2, 3), size=25, font='serif', italic=True)
sage: t2 = text3d("World", (3, 2, 1), size=10, font='sans-serif', bold=True)
sage: t3 = text3d("Font fallbacks", (0, 1, 0), font=['Consolas', 'Lucida Console', 'monospace'])
sage: t4 = text3d("Another way", (0, -1, 0), font='Consolas, Lucida Console, monospace')
sage: show(t1 + t2 + t3 + t4, viewer='threejs')
Adjust the font size, family, style, and weight (Three.js viewer only)::
sage: t0 = text3d("Pixel size", (0, 0, 0), fontsize=20)
sage: t1 = text3d("Percentage size", (0, 0, 1), fontsize='300%')
sage: t2 = text3d("Keyword size", (0, 0, 2), fontsize='x-small')
sage: t3 = text3d("Single family", (0, 0, 3), fontfamily='serif')
sage: t4 = text3d("Family fallback", (0, 0, 4), fontfamily=['Consolas', 'Lucida Console', 'monospace'])
sage: t5 = text3d("Another way", (0, 0, 5), fontfamily='Consolas, Lucida Console, monospace')
sage: t6 = text3d("Style", (0, 0, 6), fontstyle='italic')
sage: t7 = text3d("Keyword weight", (0, 0, 7), fontweight='bold')
sage: t8 = text3d("Integer weight (1-1000)", (0, 0, 8), fontweight=800) # 'extra bold'
sage: sum([t0, t1, t2, t3, t4, t5, t6, t7, t8]).show(viewer='threejs', frame=False)
Adjust the text's opacity (Three.js viewer only)::
Expand Down
5 changes: 4 additions & 1 deletion src/sage/plot/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,11 @@ def _plot3d_options(self, options=None):
if options is None:
options = dict(self.options())
options_3d = {}
for s in ['fontfamily', 'fontsize', 'fontstyle', 'fontweight']:
if s in options:
options_3d[s] = options.pop(s)
# TODO: figure out how to implement rather than ignore
for s in ['axis_coords', 'clip', 'fontsize', 'horizontal_alignment',
for s in ['axis_coords', 'clip', 'horizontal_alignment',
'rotation', 'vertical_alignment']:
if s in options:
del options[s]
Expand Down

0 comments on commit 6a6d33e

Please sign in to comment.