Skip to content

Commit a374add

Browse files
committed
Add 2D text.
add text geometry (and PEP8 autofomat) add set_text() method and supporting logics split the texture from the rest of the object_json streamline material initialization demo WIP WIP add transparency parameter to differentiate scene and object texts fix transparent args and disentangle font size with face (for resizing) remove alphas finalizing
1 parent 030479c commit a374add

File tree

5 files changed

+216
-25
lines changed

5 files changed

+216
-25
lines changed

Diff for: demo.ipynb

+50
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,56 @@
153153
"vis.delete()"
154154
]
155155
},
156+
{
157+
"cell_type": "markdown",
158+
"metadata": {},
159+
"source": [
160+
"MeshCat supports simple 2d texts rendering. For example, to write 2d texts onto a geometry or object:"
161+
]
162+
},
163+
{
164+
"cell_type": "code",
165+
"execution_count": null,
166+
"metadata": {},
167+
"outputs": [],
168+
"source": [
169+
"vis.set_text('Hello, world!',g.Box([1, 1, 1]),font_size=20)"
170+
]
171+
},
172+
{
173+
"cell_type": "markdown",
174+
"metadata": {},
175+
"source": [
176+
"It is also possible to simple write 'floating' texts onto a scene without attached to an object (e.g., for scene discription):"
177+
]
178+
},
179+
{
180+
"cell_type": "code",
181+
"execution_count": null,
182+
"metadata": {},
183+
"outputs": [],
184+
"source": [
185+
"vis.set_text('Hello, world!')"
186+
]
187+
},
188+
{
189+
"cell_type": "markdown",
190+
"metadata": {},
191+
"source": [
192+
"Under the hood, the 'floating' texts are written onto a `g.Plane()` geometry, so to change the texts size:"
193+
]
194+
},
195+
{
196+
"cell_type": "code",
197+
"execution_count": null,
198+
"metadata": {},
199+
"outputs": [],
200+
"source": [
201+
"for i in np.linspace(10,2,20):\n",
202+
" vis.set_text('Hello, world!', plane_width=2*i,plane_height=i,font_size=200)\n",
203+
" time.sleep(0.05)"
204+
]
205+
},
156206
{
157207
"cell_type": "markdown",
158208
"metadata": {},

Diff for: src/meshcat/commands.py

+44-2
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,17 @@
44
if sys.version_info >= (3, 0):
55
unicode = str
66

7-
from .geometry import Geometry, Object, Mesh, MeshPhongMaterial, PointsMaterial, Points
7+
from .geometry import (Geometry, Plane, Object, Mesh,
8+
MeshPhongMaterial, PointsMaterial, Points, TextTexture)
89

910
class SetObject:
1011
__slots__ = ["object", "path"]
12+
1113
def __init__(self, geometry_or_object, material=None, path=[]):
1214
if isinstance(geometry_or_object, Object):
1315
if material is not None:
14-
raise(ArgumentError("Please supply either an Object OR a Geometry and a Material"))
16+
raise(ArgumentError(
17+
"Please supply either an Object OR a Geometry and a Material"))
1518
self.object = geometry_or_object
1619
else:
1720
if material is None:
@@ -30,8 +33,46 @@ def lower(self):
3033
}
3134

3235

36+
class SetText:
37+
__slots__ = ["object", "path"]
38+
39+
def __init__(self, text, geometry_or_object, plane_width=10,
40+
plane_height=5, material=None, path=[], **kwargs):
41+
self.text_texture = TextTexture(text, **kwargs)
42+
if isinstance(geometry_or_object, Object):
43+
if material is not None:
44+
raise(ArgumentError(
45+
"Please supply either an Object OR a Geometry and a Material"))
46+
self.object = geometry_or_object
47+
else:
48+
if geometry_or_object is None:
49+
geometry_or_object = Plane(width=plane_width, height=plane_height)
50+
# if writing onto the scene, default material is transparent
51+
material = MeshPhongMaterial(map=self.text_texture,
52+
needsUpdate=True, transparent=True)
53+
if material is None:
54+
material = MeshPhongMaterial(map=self.text_texture,
55+
needsUpdate=True)
56+
if isinstance(material, PointsMaterial):
57+
raise(ArgumentError(
58+
"Cannot write text onto points; please supply a mesh material"))
59+
else:
60+
self.object = Mesh(geometry_or_object, material)
61+
self.path = path
62+
63+
def lower(self):
64+
data = {
65+
u"type": u"set_text",
66+
u"object": self.object.lower(),
67+
u"path": self.path.lower()
68+
}
69+
self.text_texture.lower_in_object(data)
70+
return data
71+
72+
3373
class SetTransform:
3474
__slots__ = ["matrix", "path"]
75+
3576
def __init__(self, matrix, path=[]):
3677
self.matrix = matrix
3778
self.path = path
@@ -46,6 +87,7 @@ def lower(self):
4687

4788
class Delete:
4889
__slots__ = ["path"]
90+
4991
def __init__(self, path):
5092
self.path = path
5193

Diff for: src/meshcat/geometry.py

+91-8
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@
1414

1515

1616
class SceneElement(object):
17+
1718
def __init__(self):
1819
self.uuid = unicode(uuid.uuid1())
1920

2021

2122
class ReferenceSceneElement(SceneElement):
23+
2224
def lower_in_object(self, object_data):
2325
object_data.setdefault(self.field, []).append(self.lower(object_data))
2426
return self.uuid
@@ -44,6 +46,7 @@ class Image(ReferenceSceneElement):
4446

4547

4648
class Box(Geometry):
49+
4750
def __init__(self, lengths):
4851
super(Box, self).__init__()
4952
self.lengths = lengths
@@ -59,6 +62,7 @@ def lower(self, object_data):
5962

6063

6164
class Sphere(Geometry):
65+
6266
def __init__(self, radius):
6367
super(Sphere, self).__init__()
6468
self.radius = radius
@@ -68,8 +72,8 @@ def lower(self, object_data):
6872
u"uuid": self.uuid,
6973
u"type": u"SphereGeometry",
7074
u"radius": self.radius,
71-
u"widthSegments" : 20,
72-
u"heightSegments" : 20
75+
u"widthSegments": 20,
76+
u"heightSegments": 20
7377
}
7478

7579

@@ -78,6 +82,7 @@ class Ellipsoid(Sphere):
7882
An Ellipsoid is treated as a Sphere of unit radius, with an affine
7983
transformation applied to distort it into the ellipsoidal shape
8084
"""
85+
8186
def __init__(self, radii):
8287
super(Ellipsoid, self).__init__(1.0)
8388
self.radii = radii
@@ -86,11 +91,33 @@ def intrinsic_transform(self):
8691
return np.diag(np.hstack((self.radii, 1.0)))
8792

8893

94+
class Plane(Geometry):
95+
96+
def __init__(self, width=1, height=1, widthSegments=1, heightSegments=1):
97+
super(Plane, self).__init__()
98+
self.width = width
99+
self.height = height
100+
self.widthSegments = widthSegments
101+
self.heightSegments = heightSegments
102+
103+
def lower(self, object_data):
104+
return {
105+
u"uuid": self.uuid,
106+
u"type": u"PlaneGeometry",
107+
u"width": self.width,
108+
u"height": self.height,
109+
u"widthSegments": self.widthSegments,
110+
u"heightSegments": self.heightSegments,
111+
}
112+
89113
"""
90114
A cylinder of the given height and radius. By Three.js convention, the axis of
91115
rotational symmetry is aligned with the y-axis.
92116
"""
117+
118+
93119
class Cylinder(Geometry):
120+
94121
def __init__(self, height, radius=1.0, radiusTop=None, radiusBottom=None):
95122
super(Cylinder, self).__init__()
96123
if radiusTop is not None and radiusBottom is not None:
@@ -114,11 +141,14 @@ def lower(self, object_data):
114141

115142

116143
class MeshMaterial(Material):
117-
def __init__(self, color=0xffffff, reflectivity=0.5, map=None, **kwargs):
144+
145+
def __init__(self, color=0xffffff, reflectivity=0.5, map=None,
146+
transparent=False, **kwargs):
118147
super(MeshMaterial, self).__init__()
119148
self.color = color
120149
self.reflectivity = reflectivity
121150
self.map = map
151+
self.transparent = transparent
122152
self.properties = kwargs
123153

124154
def lower(self, object_data):
@@ -127,6 +157,7 @@ def lower(self, object_data):
127157
u"type": self._type,
128158
u"color": self.color,
129159
u"reflectivity": self.reflectivity,
160+
u"transparent": self.transparent
130161
}
131162
data.update(self.properties)
132163
if self.map is not None:
@@ -135,22 +166,23 @@ def lower(self, object_data):
135166

136167

137168
class MeshBasicMaterial(MeshMaterial):
138-
_type=u"MeshBasicMaterial"
169+
_type = u"MeshBasicMaterial"
139170

140171

141172
class MeshPhongMaterial(MeshMaterial):
142-
_type=u"MeshPhongMaterial"
173+
_type = u"MeshPhongMaterial"
143174

144175

145176
class MeshLambertMaterial(MeshMaterial):
146-
_type=u"MeshLambertMaterial"
177+
_type = u"MeshLambertMaterial"
147178

148179

149180
class MeshToonMaterial(MeshMaterial):
150-
_type=u"MeshToonMaterial"
181+
_type = u"MeshToonMaterial"
151182

152183

153184
class PngImage(Image):
185+
154186
def __init__(self, data):
155187
super(PngImage, self).__init__()
156188
self.data = data
@@ -167,7 +199,49 @@ def lower(self, object_data):
167199
}
168200

169201

202+
class CanvasImage(Image):
203+
204+
def __init__(self):
205+
super(CanvasImage, self).__init__()
206+
207+
def lower(self, object_data):
208+
return {
209+
u"uuid": self.uuid,
210+
u"url": ""
211+
}
212+
213+
214+
class TextTexture(Texture):
215+
216+
def __init__(self, text, font_size=96, font_face='sans-serif',
217+
width=200, height=100, position=[10, 10]):
218+
super(TextTexture, self).__init__()
219+
self.text = text
220+
# font_size will be passed to the JS side as is; however if the
221+
# text width exceeds canvas width, font_size will be reduced.
222+
self.font_size = font_size
223+
self.font_face = font_face
224+
self.width = width
225+
self.height = height
226+
self.position = position
227+
self.image = CanvasImage()
228+
229+
def lower(self, object_data):
230+
return {
231+
u"uuid": self.uuid,
232+
u"type": u"TextTexture",
233+
u"text": unicode(self.text),
234+
u"font_size": self.font_size,
235+
u"font_face": self.font_face,
236+
u"width": self.width,
237+
u"height": self.height,
238+
u"position": self.position,
239+
u"image": self.image.lower_in_object(object_data)
240+
}
241+
242+
170243
class GenericTexture(Texture):
244+
171245
def __init__(self, properties):
172246
super(GenericTexture, self).__init__()
173247
self.properties = properties
@@ -182,6 +256,7 @@ def lower(self, object_data):
182256

183257

184258
class ImageTexture(Texture):
259+
185260
def __init__(self, image, wrap=[1001, 1001], repeat=[1, 1], **kwargs):
186261
super(ImageTexture, self).__init__()
187262
self.image = image
@@ -201,6 +276,7 @@ def lower(self, object_data):
201276

202277

203278
class GenericMaterial(Material):
279+
204280
def __init__(self, properties):
205281
self.properties = properties
206282
self.uuid = str(uuid.uuid1())
@@ -215,6 +291,7 @@ def lower(self, object_data):
215291

216292

217293
class Object(SceneElement):
294+
218295
def __init__(self, geometry, material=MeshPhongMaterial()):
219296
super(Object, self).__init__()
220297
self.geometry = geometry
@@ -251,7 +328,8 @@ def item_size(array):
251328
elif array.ndim == 2:
252329
return array.shape[0]
253330
else:
254-
raise ValueError("I can only pack 1- or 2-dimensional numpy arrays, but this one has {:d} dimensions".format(array.ndim))
331+
raise ValueError(
332+
"I can only pack 1- or 2-dimensional numpy arrays, but this one has {:d} dimensions".format(array.ndim))
255333

256334

257335
def threejs_type(dtype):
@@ -280,6 +358,7 @@ def pack_numpy_array(x):
280358

281359

282360
class ObjMeshGeometry(Geometry):
361+
283362
def __init__(self, contents):
284363
super(ObjMeshGeometry, self).__init__()
285364
self.contents = contents
@@ -299,6 +378,7 @@ def from_file(fname):
299378

300379

301380
class PointsGeometry(Geometry):
381+
302382
def __init__(self, position, color=None):
303383
super(PointsGeometry, self).__init__()
304384
self.position = position
@@ -318,6 +398,7 @@ def lower(self, object_data):
318398

319399

320400
class PointsMaterial(Material):
401+
321402
def __init__(self, size=0.001, color=0xffffff):
322403
super(PointsMaterial, self).__init__()
323404
self.size = size
@@ -336,6 +417,8 @@ def lower(self, object_data):
336417
class Points(Object):
337418
_type = u"Points"
338419

420+
class Texts(Object):
421+
_type = u"_texttexture"
339422

340423
def PointCloud(position, color, **kwargs):
341424
return Points(

0 commit comments

Comments
 (0)