#!/usr/bin/env python
# -*- coding: utf-8 -*-
from collections import namedtuple, Sequence
from traitlets import (
Unicode, Int, CInt, Instance, Enum, List, Dict, Float, CFloat,
Bool, Tuple, Undefined, TraitError, Union, TraitType
)
from ipywidgets import widget_serialization
from ipydatawidgets import DataUnion, NDArrayWidget
def _castable_namedtuple(typename, field_names):
base = namedtuple('%s_base' % typename, field_names)
def new_new(cls, *args, **kwargs):
if not kwargs and len(args) == 1 and isinstance(args, Sequence):
return base.__new__(cls, *args[0], **kwargs)
return base.__new__(cls, *args, **kwargs)
return type(typename, (base,), {'__new__': new_new})
[docs]class Vector2(Tuple):
"""A trait for a 2-tuple corresponding to a three.js Vector2.
"""
default_value = (0, 0)
info_text = 'a two-element vector'
def __init__(self, trait=CFloat, default_value=Undefined, **kwargs):
if default_value is Undefined:
default_value = self.default_value
super(Vector2, self).__init__(*(trait, trait), default_value=default_value, **kwargs)
[docs]class Vector3(Tuple):
"""A trait for a 3-tuple corresponding to a three.js Vector3.
"""
default_value = (0, 0, 0)
info_text = 'a three-element vector'
def __init__(self, trait=CFloat, default_value=Undefined, **kwargs):
if default_value is Undefined:
default_value = self.default_value
super(Vector3, self).__init__(*(trait, trait, trait), default_value=default_value, **kwargs)
[docs]class Vector4(Tuple):
"""A trait for a 4-tuple corresponding to a three.js Vector4.
"""
default_value = (0, 0, 0, 0)
info_text = 'a four-element vector'
def __init__(self, trait=CFloat, default_value=Undefined, **kwargs):
if default_value is Undefined:
default_value = self.default_value
super(Vector4, self).__init__(*(trait, trait, trait, trait), default_value=default_value, **kwargs)
[docs]class Matrix3(Tuple):
"""A trait for a 9-tuple corresponding to a three.js Matrix3.
"""
default_value = (
1, 0, 0,
0, 1, 0,
0, 0, 1
)
info_text = 'a three-by-three matrix (9 element tuple)'
def __init__(self, trait=CFloat, default_value=Undefined, **kwargs):
if default_value is Undefined:
default_value = self.default_value
super(Matrix3, self).__init__(*((trait,) * 9), default_value=default_value, **kwargs)
[docs]class Matrix4(Tuple):
"""A trait for a 16-tuple corresponding to a three.js Matrix4.
"""
default_value = (
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
)
info_text = 'a four-by-four matrix (16 element tuple)'
def __init__(self, trait=CFloat, default_value=Undefined, **kwargs):
if default_value is Undefined:
default_value = self.default_value
super(Matrix4, self).__init__(*((trait,) * 16), default_value=default_value, **kwargs)
[docs]class Face3(Tuple):
"""A trait for a named tuple corresponding to a three.js Face3.
Accepts named tuples with the field names:
('a', 'b', 'c', 'normal', 'color', 'materialIndex')
"""
klass = _castable_namedtuple('Face3', ('a', 'b', 'c', 'normal', 'color', 'materialIndex'))
_cast_types = (list, tuple)
info_text = 'a named tuple representing a Face3'
def __init__(self, **kwargs):
super(Face3, self).__init__(
CInt(), # a - Vertex A index.
CInt(), # b - Vertex B index.
CInt(), # c - Vertex C index.
Union([ # normal - (optional) Face normal (Vector3) or array of 3 vertex normals.
Vector3(allow_none=True),
Tuple((Vector3(),) * 3),
]),
Union([ # color - (optional) Face color or array of vertex colors.
Unicode(allow_none=True),
Tuple((Unicode(),) * 3),
]),
CInt(allow_none=True), # materialIndex - (optional) which index of an array of materials to associate with the face.
default_value=(0, 0, 0, None, None, None)
)
[docs]class Euler(Tuple):
"""A trait for a set of Euler angles.
Expressed as a tuple of tree floats (the angles), and the order as a string.
See the three.js docs for futher details.
"""
info_text = 'a set of Euler angles'
default_value = (0, 0, 0, 'XYZ')
_accepted_orders = ['XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX']
def __init__(self, default_value=Undefined, **kwargs):
if default_value is Undefined:
default_value = self.default_value
super(Euler, self).__init__(
CFloat(), CFloat(), CFloat(),
Enum(self._accepted_orders, self._accepted_orders[0]),
default_value=default_value , **kwargs)
[docs]class WebGLDataUnion(DataUnion):
"""A trait that accepts either a numpy array, or an NDArrayWidget reference.
Also constrains the use of 64-bit arrays, as this is not supported by WebGL.
"""
[docs] def validate(self, obj, value):
value = super(WebGLDataUnion, self).validate(obj, value)
array = value.array if isinstance(value, NDArrayWidget) else value
if array is not Undefined and str(array.dtype) == 'float64':
if isinstance(value, NDArrayWidget):
raise TraitError('Cannot use a float64 data widget as a BufferAttribute source.')
else:
# 64-bit not supported, coerce to 32-bit
value = value.astype('float32')
return value
[docs]class Uninitialized:
"""Placeholder sentinel used while waiting for a initialization via sync"""
pass
_widget_to_json = widget_serialization['to_json']
def _serialize_uninitialized(value, owner):
if isinstance(value, Uninitialized):
return 'uninitialized'
return _widget_to_json(value, owner)
unitialized_serialization = {
'to_json': _serialize_uninitialized,
'from_json': widget_serialization['from_json'],
}
UninitializedSentinel = Uninitialized()