Skip to content

Commit

Permalink
Updated version + fix
Browse files Browse the repository at this point in the history
  • Loading branch information
gabrielelanaro committed Feb 20, 2016
1 parent ce65070 commit dc07da4
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 71 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
[![Documentation Status](https://readthedocs.org/projects/chemview/badge/?version=latest)](https://readthedocs.org/projects/chemview/?badge=latest)
[![Build Status](https://travis-ci.org/gabrielelanaro/chemview.svg?branch=master)](https://travis-ci.org/gabrielelanaro/chemview)

Version: 0.2
Version: 0.6

The new generation molecular viewer for the IPython notebook.

Expand Down
4 changes: 3 additions & 1 deletion chemview/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from .install import enable_notebook
from .widget import RepresentationViewer
from .viewer import MolecularViewer, TrajectoryControls
from .trajectory import TrajectoryViewer
from .trajectory import TrajectoryViewer

__version__ = '0.6'
144 changes: 79 additions & 65 deletions chemview/scene.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
import numpy as np
import six


class Validator(object):

def validate(self, value):
pass

def cast(self, value):
pass

Expand All @@ -16,105 +17,114 @@ def default(self, inp=None):


class BoundedScalar(Validator):

def __init__(self, start, end, type, ginclusive=True, linclusive=True, default=None):
self.start = start
self.end = end
self.ginclusive = ginclusive
self.linclusive = linclusive
self.type = type
self._default = default

def validate(self, value):
lo = operator.le if self.linclusive else operator.lt
go = operator.ge if self.ginclusive else operator.gt

return go(value, self.start) and lo(value, self.end), 'should be between {} and {}'.format(self.start, self.end)

def cast(self, value):
return self.type(value)


class TypedList(Validator):

def __init__(self, type, length=None, match_length=None, default_item=None, default=None):
self.type = type
self.length = length
self.match_length = match_length
self.default_item = default_item
self._default = default



def default(self, inp=None):
if self._default is not None:
return self._default

if self.match_length is not None and self.default_item is not None:
return [self.default_item] * len(inp[self.match_length])
else:
raise ValueError("This field is required")

def validate(self, value):
# We check the first element only
length_condition = True if self.length is None else len(value) == self.length

length_condition = True if self.length is None else len(
value) == self.length

if len(value) == 0:
type_condition = True
else:
type_condition = isinstance(value[0], self.type)

return length_condition and type_condition, 'should be a list with elements of type {}'.format(self.type)

def cast(self, value):
return [self.type(v) for v in value]


class UniqueID(Validator):

def default(self, inp=None):
raise ValueError("No default for this field")

def validate(self, value):
return isinstance(value, int), 'UUid not valid'

def cast(self, value):
return value



class Array(Validator):

def __init__(self, shape, type):
self.shape = shape
self.type = type

def default(self, inp=None):
raise ValueError("No default for this field")

def validate(self, value):
return True, ''

def cast(self, value):
return np.array(value, dtype=self.type)


class Keyword(Validator):

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

def default(self, inp=None):
raise ValueError("No default for this field")

def validate(self, value):
return value == self.value, 'should be {}'.format(self.value)

def cast(self, value):
return six.u(value)


class Boolean(Validator):

def __init__(self, default=None):
self._default = default

def validate(self, value):
return isinstance(value, bool), 'should be a boolean, got {}'.format(value)

def cast(self, value):
return bool(value)


class MatchSchema(object):

def __init__(self, matchers, field):
Expand All @@ -123,58 +133,59 @@ def __init__(self, matchers, field):

def match(self, value):
return self.matchers[value[self.field]]



class ValidationError(Exception):
pass

from collections import OrderedDict

POINTS_SCHEMA = {
POINTS_SCHEMA = {
"rep_id": UniqueID(),
"rep_type": Keyword("points"),
"options":
"options":
OrderedDict({'coordinates': Array(shape=(-1, 3), type=np.float32),
'colors': TypedList(int, match_length='coordinates', default_item=0xffffff),
'sizes': TypedList(float, match_length='coordinates', default_item=0.1),
'visible': TypedList(float, match_length='coordinates', default_item=1.0)})
}

SPHERES_SCHEMA = {
SPHERES_SCHEMA = {
"rep_id": UniqueID(),
"rep_type": Keyword("spheres"),
"options" :
"options":
OrderedDict({'coordinates': Array(shape=(-1, 3), type=np.float32),
'colors': TypedList(int, match_length='coordinates', default_item=0xffffff),
'radii': TypedList(float, match_length='coordinates', default_item=0.1),
'alpha': TypedList(float, match_length='coordinates', default_item=1.0)})
}


CYLINDERS_SCHEMA = {
CYLINDERS_SCHEMA = {
"rep_id": UniqueID(),
"rep_type": Keyword("cylinders"),
"options" :
"options":
OrderedDict({'startCoords': Array(shape=(-1, 3), type=np.float32),
'endCoords': Array(shape=(-1, 3), type=np.float32),
'colors': TypedList(int, match_length='startCoords', default_item=0xffffff),
'radii': TypedList(float, match_length='startCoords', default_item=0.1),
'alpha': TypedList(float, match_length='startCoords', default_item=1.0)})
}

LINES_SCHEMA = {
LINES_SCHEMA = {
"rep_id": UniqueID(),
"rep_type": Keyword("lines"),
"options" :
"options":
OrderedDict({'startCoords': Array(shape=(-1, 3), type=np.float32),
'endCoords': Array(shape=(-1, 3), type=np.float32),
'startColors': TypedList(int, match_length='startCoords', default_item=0xffffff),
'endColors': TypedList(float, match_length='endCoords', default_item=1.0)})
}

SURFACE_SCHEMA = {
SURFACE_SCHEMA = {
"rep_id": UniqueID(),
"rep_type": Keyword("surface"),
"options" :
"options":
OrderedDict({'verts': Array(shape=(-1, 3), type=np.float32),
'faces': Array(shape=(-1, 3), type=np.uint32),
'colors': TypedList(int, match_length='verts', default_item=0xffffff),
Expand All @@ -184,7 +195,7 @@ class ValidationError(Exception):
SMOOTHTUBE_SCHEMA = {
"rep_id": UniqueID(),
"rep_type": Keyword("smoothtube"),
"options" :
"options":
OrderedDict({'coordinates': Array(shape=(-1, 3), type=np.float32),
'radius': BoundedScalar(0.0, float('inf'), float, default=1.0),
'color': BoundedScalar(0, 0xffffff, int, default=0xffffff),
Expand All @@ -194,7 +205,7 @@ class ValidationError(Exception):
RIBBON_SCHEMA = {
"rep_id": UniqueID(),
"rep_type": Keyword("ribbon"),
"options" :
"options":
OrderedDict({'coordinates': Array(shape=(-1, 3), type=np.float32),
'normals': Array(shape=(-1, 3), type=np.float32),
'color': BoundedScalar(0, 0xffffff, int, default=0xffffff),
Expand All @@ -204,64 +215,67 @@ class ValidationError(Exception):


SCHEMA = {
'camera' : {'aspect': BoundedScalar(0.0, float('inf'), float, default=1.0, ginclusive=False),
'vfov': BoundedScalar(0.0, 180.0, float, default=90.0),
'location': TypedList(float, 3, default=[0.0, 0.0, -1.0]),
'quaternion': TypedList(float, 4, default=[0.0, 0.0, 0.0, 1.0]),
'target': TypedList(float, 3, default=[0.0, 0.0, 0.0])},
'representations' : MatchSchema([POINTS_SCHEMA,
SPHERES_SCHEMA,
LINES_SCHEMA,
CYLINDERS_SCHEMA,
SURFACE_SCHEMA,
SMOOTHTUBE_SCHEMA], 'rep_type')
'camera': {'aspect': BoundedScalar(0.0, float('inf'), float, default=1.0, ginclusive=False),
'vfov': BoundedScalar(0.0, 180.0, float, default=90.0),
'location': TypedList(float, 3, default=[0.0, 0.0, -1.0]),
'quaternion': TypedList(float, 4, default=[0.0, 0.0, 0.0, 1.0]),
'target': TypedList(float, 3, default=[0.0, 0.0, 0.0])},

'representations': MatchSchema([POINTS_SCHEMA,
SPHERES_SCHEMA,
LINES_SCHEMA,
CYLINDERS_SCHEMA,
SURFACE_SCHEMA,
SMOOTHTUBE_SCHEMA], 'rep_type')
}


def validate_schema(value, schema):
retval = {}
for key, validator in schema.items():

if isinstance(validator, dict):
inp = value.get(key, {}).copy()
validated_inp = validate_schema(inp, validator)
retval[key] = validated_inp

elif isinstance(validator, MatchSchema):
inp = value.get(key, []).copy()
inp = value.get(key, [])[:] # copy

validated_inp = []
for val in inp:
sub_schema = validator.match(val)
validated_inp.append(validate_schema(val, sub_schema))

retval[key] = validated_inp



elif isinstance(validator, Validator):
if key not in value:
try:
retval[key] = validator.default(value)
except ValueError:
raise ValidationError('Problem: "{}" is required.'.format(key))
raise ValidationError(
'Problem: "{}" is required.'.format(key))
else:
try:
cast = validator.cast(value[key])
except ValueError:
raise ValidationError('Problem: "{}" is {}, cannot cast'.format(key, value[key]))

raise ValidationError(
'Problem: "{}" is {}, cannot cast'.format(key, value[key]))

ok, msg = validator.validate(cast)
if not ok:
raise ValidationError('Problem: "{}" is {} but {}'.format(key, value[key], msg))
raise ValidationError(
'Problem: "{}" is {} but {}'.format(key, value[key], msg))
retval[key] = cast
else:
raise ValueError("schema not valid")

return retval



def normalize_scene(scene):
"""Normalize incomplete scene with sane defaults"""
retval = scene.copy()

return validate_schema(scene, SCHEMA)
4 changes: 2 additions & 2 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@
# built documents.
#
# The short X.Y version.
version = '0.1'
version = '0.6'
# The full version, including alpha/beta/rc tags.
release = '0.1'
release = '0.6'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
5 changes: 3 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
from setuptools import setup, find_packages

setup(name='chemview',
version='0.5',
version='0.6',
description='Interactive molecular viewer for IPython notebook',
author='Gabriele Lanaro',
install_requires=['notebook', 'numpy', 'matplotlib', 'vapory'],
author_email='[email protected]',
url='https://github.com/gabrielelanaro/chemview',
packages=find_packages(),
package_data = {'': ['*.js', "*.css"]},
package_data={'': ['*.js', "*.css"]},
include_package_data=True
)

0 comments on commit dc07da4

Please sign in to comment.