Skip to content

Commit

Permalink
Add 2D text.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
shensquared committed Feb 15, 2019
1 parent 030479c commit a374add
Show file tree
Hide file tree
Showing 5 changed files with 216 additions and 25 deletions.
50 changes: 50 additions & 0 deletions demo.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,56 @@
"vis.delete()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"MeshCat supports simple 2d texts rendering. For example, to write 2d texts onto a geometry or object:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"vis.set_text('Hello, world!',g.Box([1, 1, 1]),font_size=20)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"It is also possible to simple write 'floating' texts onto a scene without attached to an object (e.g., for scene discription):"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"vis.set_text('Hello, world!')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Under the hood, the 'floating' texts are written onto a `g.Plane()` geometry, so to change the texts size:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"for i in np.linspace(10,2,20):\n",
" vis.set_text('Hello, world!', plane_width=2*i,plane_height=i,font_size=200)\n",
" time.sleep(0.05)"
]
},
{
"cell_type": "markdown",
"metadata": {},
Expand Down
46 changes: 44 additions & 2 deletions src/meshcat/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@
if sys.version_info >= (3, 0):
unicode = str

from .geometry import Geometry, Object, Mesh, MeshPhongMaterial, PointsMaterial, Points
from .geometry import (Geometry, Plane, Object, Mesh,
MeshPhongMaterial, PointsMaterial, Points, TextTexture)

class SetObject:
__slots__ = ["object", "path"]

def __init__(self, geometry_or_object, material=None, path=[]):
if isinstance(geometry_or_object, Object):
if material is not None:
raise(ArgumentError("Please supply either an Object OR a Geometry and a Material"))
raise(ArgumentError(
"Please supply either an Object OR a Geometry and a Material"))
self.object = geometry_or_object
else:
if material is None:
Expand All @@ -30,8 +33,46 @@ def lower(self):
}


class SetText:
__slots__ = ["object", "path"]

def __init__(self, text, geometry_or_object, plane_width=10,
plane_height=5, material=None, path=[], **kwargs):
self.text_texture = TextTexture(text, **kwargs)
if isinstance(geometry_or_object, Object):
if material is not None:
raise(ArgumentError(
"Please supply either an Object OR a Geometry and a Material"))
self.object = geometry_or_object
else:
if geometry_or_object is None:
geometry_or_object = Plane(width=plane_width, height=plane_height)
# if writing onto the scene, default material is transparent
material = MeshPhongMaterial(map=self.text_texture,
needsUpdate=True, transparent=True)
if material is None:
material = MeshPhongMaterial(map=self.text_texture,
needsUpdate=True)
if isinstance(material, PointsMaterial):
raise(ArgumentError(
"Cannot write text onto points; please supply a mesh material"))
else:
self.object = Mesh(geometry_or_object, material)
self.path = path

def lower(self):
data = {
u"type": u"set_text",
u"object": self.object.lower(),
u"path": self.path.lower()
}
self.text_texture.lower_in_object(data)
return data


class SetTransform:
__slots__ = ["matrix", "path"]

def __init__(self, matrix, path=[]):
self.matrix = matrix
self.path = path
Expand All @@ -46,6 +87,7 @@ def lower(self):

class Delete:
__slots__ = ["path"]

def __init__(self, path):
self.path = path

Expand Down
99 changes: 91 additions & 8 deletions src/meshcat/geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@


class SceneElement(object):

def __init__(self):
self.uuid = unicode(uuid.uuid1())


class ReferenceSceneElement(SceneElement):

def lower_in_object(self, object_data):
object_data.setdefault(self.field, []).append(self.lower(object_data))
return self.uuid
Expand All @@ -44,6 +46,7 @@ class Image(ReferenceSceneElement):


class Box(Geometry):

def __init__(self, lengths):
super(Box, self).__init__()
self.lengths = lengths
Expand All @@ -59,6 +62,7 @@ def lower(self, object_data):


class Sphere(Geometry):

def __init__(self, radius):
super(Sphere, self).__init__()
self.radius = radius
Expand All @@ -68,8 +72,8 @@ def lower(self, object_data):
u"uuid": self.uuid,
u"type": u"SphereGeometry",
u"radius": self.radius,
u"widthSegments" : 20,
u"heightSegments" : 20
u"widthSegments": 20,
u"heightSegments": 20
}


Expand All @@ -78,6 +82,7 @@ class Ellipsoid(Sphere):
An Ellipsoid is treated as a Sphere of unit radius, with an affine
transformation applied to distort it into the ellipsoidal shape
"""

def __init__(self, radii):
super(Ellipsoid, self).__init__(1.0)
self.radii = radii
Expand All @@ -86,11 +91,33 @@ def intrinsic_transform(self):
return np.diag(np.hstack((self.radii, 1.0)))


class Plane(Geometry):

def __init__(self, width=1, height=1, widthSegments=1, heightSegments=1):
super(Plane, self).__init__()
self.width = width
self.height = height
self.widthSegments = widthSegments
self.heightSegments = heightSegments

def lower(self, object_data):
return {
u"uuid": self.uuid,
u"type": u"PlaneGeometry",
u"width": self.width,
u"height": self.height,
u"widthSegments": self.widthSegments,
u"heightSegments": self.heightSegments,
}

"""
A cylinder of the given height and radius. By Three.js convention, the axis of
rotational symmetry is aligned with the y-axis.
"""


class Cylinder(Geometry):

def __init__(self, height, radius=1.0, radiusTop=None, radiusBottom=None):
super(Cylinder, self).__init__()
if radiusTop is not None and radiusBottom is not None:
Expand All @@ -114,11 +141,14 @@ def lower(self, object_data):


class MeshMaterial(Material):
def __init__(self, color=0xffffff, reflectivity=0.5, map=None, **kwargs):

def __init__(self, color=0xffffff, reflectivity=0.5, map=None,
transparent=False, **kwargs):
super(MeshMaterial, self).__init__()
self.color = color
self.reflectivity = reflectivity
self.map = map
self.transparent = transparent
self.properties = kwargs

def lower(self, object_data):
Expand All @@ -127,6 +157,7 @@ def lower(self, object_data):
u"type": self._type,
u"color": self.color,
u"reflectivity": self.reflectivity,
u"transparent": self.transparent
}
data.update(self.properties)
if self.map is not None:
Expand All @@ -135,22 +166,23 @@ def lower(self, object_data):


class MeshBasicMaterial(MeshMaterial):
_type=u"MeshBasicMaterial"
_type = u"MeshBasicMaterial"


class MeshPhongMaterial(MeshMaterial):
_type=u"MeshPhongMaterial"
_type = u"MeshPhongMaterial"


class MeshLambertMaterial(MeshMaterial):
_type=u"MeshLambertMaterial"
_type = u"MeshLambertMaterial"


class MeshToonMaterial(MeshMaterial):
_type=u"MeshToonMaterial"
_type = u"MeshToonMaterial"


class PngImage(Image):

def __init__(self, data):
super(PngImage, self).__init__()
self.data = data
Expand All @@ -167,7 +199,49 @@ def lower(self, object_data):
}


class CanvasImage(Image):

def __init__(self):
super(CanvasImage, self).__init__()

def lower(self, object_data):
return {
u"uuid": self.uuid,
u"url": ""
}


class TextTexture(Texture):

def __init__(self, text, font_size=96, font_face='sans-serif',
width=200, height=100, position=[10, 10]):
super(TextTexture, self).__init__()
self.text = text
# font_size will be passed to the JS side as is; however if the
# text width exceeds canvas width, font_size will be reduced.
self.font_size = font_size
self.font_face = font_face
self.width = width
self.height = height
self.position = position
self.image = CanvasImage()

def lower(self, object_data):
return {
u"uuid": self.uuid,
u"type": u"TextTexture",
u"text": unicode(self.text),
u"font_size": self.font_size,
u"font_face": self.font_face,
u"width": self.width,
u"height": self.height,
u"position": self.position,
u"image": self.image.lower_in_object(object_data)
}


class GenericTexture(Texture):

def __init__(self, properties):
super(GenericTexture, self).__init__()
self.properties = properties
Expand All @@ -182,6 +256,7 @@ def lower(self, object_data):


class ImageTexture(Texture):

def __init__(self, image, wrap=[1001, 1001], repeat=[1, 1], **kwargs):
super(ImageTexture, self).__init__()
self.image = image
Expand All @@ -201,6 +276,7 @@ def lower(self, object_data):


class GenericMaterial(Material):

def __init__(self, properties):
self.properties = properties
self.uuid = str(uuid.uuid1())
Expand All @@ -215,6 +291,7 @@ def lower(self, object_data):


class Object(SceneElement):

def __init__(self, geometry, material=MeshPhongMaterial()):
super(Object, self).__init__()
self.geometry = geometry
Expand Down Expand Up @@ -251,7 +328,8 @@ def item_size(array):
elif array.ndim == 2:
return array.shape[0]
else:
raise ValueError("I can only pack 1- or 2-dimensional numpy arrays, but this one has {:d} dimensions".format(array.ndim))
raise ValueError(
"I can only pack 1- or 2-dimensional numpy arrays, but this one has {:d} dimensions".format(array.ndim))


def threejs_type(dtype):
Expand Down Expand Up @@ -280,6 +358,7 @@ def pack_numpy_array(x):


class ObjMeshGeometry(Geometry):

def __init__(self, contents):
super(ObjMeshGeometry, self).__init__()
self.contents = contents
Expand All @@ -299,6 +378,7 @@ def from_file(fname):


class PointsGeometry(Geometry):

def __init__(self, position, color=None):
super(PointsGeometry, self).__init__()
self.position = position
Expand All @@ -318,6 +398,7 @@ def lower(self, object_data):


class PointsMaterial(Material):

def __init__(self, size=0.001, color=0xffffff):
super(PointsMaterial, self).__init__()
self.size = size
Expand All @@ -336,6 +417,8 @@ def lower(self, object_data):
class Points(Object):
_type = u"Points"

class Texts(Object):
_type = u"_texttexture"

def PointCloud(position, color, **kwargs):
return Points(
Expand Down
Loading

0 comments on commit a374add

Please sign in to comment.