Skip to content
This repository was archived by the owner on Jul 14, 2020. It is now read-only.

Commit 8cc5f7c

Browse files
committed
Implemented basic builders
1 parent 1310189 commit 8cc5f7c

File tree

8 files changed

+568
-29
lines changed

8 files changed

+568
-29
lines changed

.pylintrc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[TYPECHECK]
2+
ignored-classes=EasyDict

setup.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
from setuptools import setup
22

3+
with open("README.md", "r") as fh:
4+
descr = fh.read()
5+
36
setup(
47
name='xviz',
58
version='0.0.1',
69
description='Python implementation of XVIZ protocol',
710
author='Yuanxin Zhong',
811
author_email='[email protected]',
12+
url="https://github.com/cmpute/xviz.py",
13+
long_description=descr,
914
packages=['xviz'],
10-
install_requires=['numpy'],
15+
install_requires=['numpy', 'easydict'],
1116
)

xviz/builder/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
"""
2+
This module contains classes to build messages for the XVIZ protocol.
3+
4+
# Reference
5+
[@xviz/builder](https://github.com/uber/xviz/blob/master/modules/builder/README.md)
6+
"""

xviz/builder/base_builder.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
2+
class XVIZBaseBuilder:
3+
"""
4+
# Reference
5+
[@xviz/builder/xviz-base-builder]/(https://github.com/uber/xviz/blob/master/modules/builder/src/builders/xviz-base-builder.js)
6+
"""
7+
def __init__(self, category, metadata, validator):
8+
self._streamId = None
9+
self._category = category
10+
self._metadata = metadata
11+
self._validator = validator
12+
13+
def stream(self, streamId):
14+
if self._streamId:
15+
self._flush()
16+
self._streamId = streamId
17+
return self
18+
19+
@property
20+
def StreamId(self):
21+
return self._streamId
22+
@property
23+
def Category(self):
24+
return self._category
25+
@property
26+
def Metadata(self):
27+
return self._metadata
28+
29+
def _flush(self):
30+
raise NotImplementedError("Derived class should implement this method")
31+
def reset(self):
32+
self._category = None
33+
34+
def _validate(self):
35+
self._validator.hasProp(self, '_streamId')
36+
self._validator.hasProp(self, '_category')
37+
self._validator.matchMetadata(self)
38+
39+
def validateWarn(self, msg):
40+
self._validator.warn(msg)
41+
def validateError(self, msg):
42+
self._validator.error(msg)
43+
def validatePropSetOnce(self, prop):
44+
self._validator.propSetOnce(self, prop)

xviz/builder/pose_builder.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
2+
from easydict import EasyDict as edict
3+
4+
from xviz.builder.base_builder import XVIZBaseBuilder
5+
from xviz.builder.validator import CATEGORY
6+
7+
class XVIZPoseBuilder(XVIZBaseBuilder):
8+
"""
9+
# Reference
10+
[@xviz/builder/xviz-pose-builder]/(https://github.com/uber/xviz/blob/master/modules/builder/src/builders/xviz-pose-builder.js)
11+
"""
12+
def __init__(self, metadata, validator):
13+
super().__init__(CATEGORY.POSE, metadata, validator)
14+
15+
self._poses = None
16+
self.reset()
17+
18+
def mapOrigin(self, longitude, latitude, altitude):
19+
self._map_origin = dict(longitude=longitude, latitude=latitude, altitude=altitude)
20+
return self
21+
22+
def position(self, x, y, z):
23+
self._position = [x, y, z]
24+
return self
25+
26+
def orientation(self, roll, pitch, yaw):
27+
self._orientation = [roll, pitch, yaw]
28+
return self
29+
30+
def timestamp(self, timestamp):
31+
self._timestamp = timestamp
32+
return self
33+
34+
def _flush(self):
35+
if not self._poses:
36+
self._poses = {}
37+
38+
data = edict()
39+
if not self._timestamp:
40+
data.timestamp = self._timestamp
41+
if not self._map_origin:
42+
data.map_origin = self._map_origin
43+
if not self._position:
44+
data.position = self._position
45+
if not self._orientation:
46+
data.orientation = self._orientation
47+
self._poses[self._streamId] = data
48+
49+
def reset(self):
50+
super().reset()
51+
52+
self._category = CATEGORY.POSE
53+
self._timestamp = None
54+
self._map_origin = None
55+
self._position = None
56+
self._orientation = None
57+
58+
def getData(self):
59+
if self._streamId:
60+
self._flush()
61+
62+
return edict(poses=self._poses)

xviz/builder/primitive_builder.py

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
2+
import numpy as np
3+
from easydict import EasyDict as edict
4+
5+
from xviz.builder.base_builder import XVIZBaseBuilder
6+
from xviz.builder.validator import CATEGORY, PRIMITIVE_TYPES
7+
8+
class PrimitiveBuilder(XVIZBaseBuilder):
9+
"""
10+
# Reference
11+
[@xviz/builder/xviz-primitive-builder]/(https://github.com/uber/xviz/blob/master/modules/builder/src/builders/xviz-primitive-builder.js)
12+
"""
13+
def __init__(self, metadata, validator):
14+
super().__init__(CATEGORY.PRIMITIVE, metadata, validator)
15+
16+
self._primitives = edict()
17+
self.reset()
18+
19+
def image(self, data):
20+
if self._type:
21+
self._flush()
22+
23+
if not isinstance(data, np.ndarray) or not isinstance(data, str):
24+
# TODO: support PILLOW and other image types
25+
self.validateError("An image data must be a string or numpy array")
26+
self.validatePropSetOnce("_image")
27+
self._type = PRIMITIVE_TYPES.image
28+
self._image = edict(data=data)
29+
30+
return self
31+
32+
def dimensions(self, widthPixel=None, heightPixel=None):
33+
if not self._image:
34+
self.validateError("An image needs to be set first")
35+
36+
self._image.width_px = widthPixel
37+
self._image.height_px = heightPixel
38+
39+
return self
40+
41+
def polygon(self, vertices):
42+
if not self._type:
43+
self._flush()
44+
45+
self.validatePropSetOnce("_vertices")
46+
self._vertices = vertices
47+
self._type = PRIMITIVE_TYPES.polygon
48+
49+
return self
50+
51+
def polyline(self, vertices):
52+
if not self._type:
53+
self._flush()
54+
55+
self.validatePropSetOnce("_vertices")
56+
self._vertices = vertices
57+
self._type = PRIMITIVE_TYPES.polyline
58+
59+
return self
60+
61+
def points(self, vertices):
62+
if not self._type:
63+
self._flush()
64+
65+
self.validatePropSetOnce("_vertices")
66+
self._vertices = vertices
67+
self._type = PRIMITIVE_TYPES.point
68+
69+
return self
70+
71+
def circle(self, position, radius):
72+
if not self._type:
73+
self._flush()
74+
75+
self.validatePropSetOnce("_radius")
76+
self.position(position)
77+
78+
self._radius = radius
79+
self._type = PRIMITIVE_TYPES.circle
80+
81+
return self
82+
83+
def stadium(self, start, end, radius):
84+
if not self._type:
85+
self._flush()
86+
87+
self.validatePropSetOnce("_radius")
88+
89+
if len(start) != 3:
90+
self.validateError("The start position must be of the form [x, y, z] where {} was provided".format(start))
91+
if len(end) != 3:
92+
self.validateError("The end position must be of the form [x, y, z] where {} was provided".format(end))
93+
94+
95+
self._vertices = [start, end]
96+
self._radius = radius
97+
self._type = PRIMITIVE_TYPES.stadium
98+
99+
return self
100+
101+
def text(self, message):
102+
# XXX: is not actually defined yet
103+
if not self._type:
104+
self._flush()
105+
106+
self.validatePropSetOnce('_text')
107+
108+
self._text = message
109+
self._type = PRIMITIVE_TYPES.text
110+
111+
return self
112+
113+
def position(self, point):
114+
sel.validatePropSetOnce("_vertices")
115+
116+
if len(point) != 3:
117+
self.validateError("A position must be of the form [x, y, z] where {} was provided".format(point))
118+
119+
self._vertices = [point]
120+
return self
121+
122+
def colors(self, colorArray):
123+
self.validatePropSetOnce('_colors')
124+
self._colors = colorArray
125+
126+
return self
127+
128+
def style(self, style):
129+
self._validatePrerequisite()
130+
self.validatePropSetOnce('_style')
131+
self._stype = style
132+
133+
return self
134+
135+
def id(self, identifier):
136+
self._validatePrerequisite()
137+
self.validatePropSetOnce('_id')
138+
self._id = identifier
139+
140+
return self
141+
142+
def classes(self, classList):
143+
self._validatePrerequisite()
144+
self.validatePropSetOnce('_classes')
145+
146+
self._classes = classList
147+
return self
148+
149+
def _validate(self):
150+
super()._validate()
151+
152+
if self._type == PRIMITIVE_TYPES.image:
153+
if self._image == None or self._image.data == None:
154+
self.validateWarn("Stream {} image data are not provided.".format(self._streamId))
155+
else:
156+
if self._vertices == None:
157+
self.validateWarn("Stream {} primitives vertices are not provided.".format(self._streamId))
158+
159+
def _flush(self):
160+
self._validate()
161+
self._flushPrimitives()
162+
163+
def getData(self):
164+
if self._type:
165+
self._flush()
166+
167+
if len(self._primitives) == 0:
168+
return None
169+
170+
return self._primitives
171+
172+
def _validatePrerequisite(self):
173+
if self._type == None:
174+
self.validateError("Start from a primitive first, e.g polygon(), image(), etc.")
175+
176+
def _flushPrimitives(self):
177+
if self._streamId not in self._primitives.keys():
178+
self._primitives[self._streamId] = edict()
179+
stream = self._primitives[self._streamId]
180+
181+
primitive = self._formatPrimitive()
182+
183+
arrayFieldName = self._type + 's'
184+
185+
if arrayFieldName not in stream:
186+
stream[arrayFieldName] = []
187+
array = stream[arrayFieldName]
188+
189+
array.push(primitive)
190+
191+
self.reset()
192+
193+
def _formatPrimitive(self):
194+
obj = edict()
195+
196+
# Embed primitive data
197+
if self._type == PRIMITIVE_TYPES.polygon or self._type == PRIMITIVE_TYPES.polyline:
198+
obj.vertices = self._vertices
199+
elif self._type == PRIMITIVE_TYPES.point:
200+
if self._colors != None:
201+
obj.colors = self._colors
202+
self.points = self._vertices
203+
elif self._type == PRIMITIVE_TYPES.text:
204+
obj.position = self._vertices[0]
205+
obj.text = self._text
206+
elif self._type == PRIMITIVE_TYPES.circle:
207+
obj.center = self._vertices[0]
208+
obj.radius = self._radius
209+
elif self._type == PRIMITIVE_TYPES.stadium:
210+
obj.start = self._vertices[0]
211+
obj.end = self._vertices[1]
212+
obj.radius = self._radius
213+
elif self._type == PRIMITIVE_TYPES.image:
214+
if self._vertices != None:
215+
self._image.position = self._vertices[0]
216+
217+
obj.update(self._image)
218+
219+
# Embed base data
220+
haveBase = False
221+
base = edict()
222+
223+
if self._id != None:
224+
haveBase = True
225+
base.object_id = self._id
226+
if self._style != None:
227+
haveBase = True
228+
base.style = self._style
229+
if self._classes != None:
230+
haveBase = True
231+
base.classes = self._classes
232+
233+
if haveBase:
234+
obj.base = base
235+
236+
return obj
237+
238+
def _validateStyle(self):
239+
self._validator.validateStyle(self)
240+
241+
def reset(self):
242+
self._type = None
243+
244+
self._image = None
245+
self._vertices = None
246+
self._radius = None
247+
self._text = None
248+
self._colors = None
249+
250+
self._id = None
251+
self._style = None
252+
self._classes = None

0 commit comments

Comments
 (0)