77
88from .interface import IOMFReader , InvalidOMFFile
99
10- from .. import base , pointset , texture
10+ from .. import attribute , base , lineset , pointset , texture
1111
1212COMPATIBILITY_VERSION = b'OMF-v0.9.0'
1313_default = object ()
@@ -29,6 +29,10 @@ def load(self, include_binary: bool = True, project_json: bool = None):
2929 self ._project = self ._read_json (json_start )
3030 return self ._copy_project (project_uuid )
3131
32+ def _test_data_needed (self , * args , ** kwargs ):
33+ # temporary placeholder
34+ raise InvalidOMFFile ('Test data required' )
35+
3236 def _read_header (self ):
3337 """Checks magic number and version; gets project uid and json start"""
3438 self ._f .seek (0 )
@@ -47,8 +51,8 @@ def _read_json(self, json_start):
4751 return json .loads (self ._f .read ().decode ('utf-8' ))
4852
4953 # Safe access to attributes
50- @staticmethod
51- def __get_attr (src , attr , optional = False , converter = None , default = _default ):
54+ @classmethod
55+ def __get_attr (cls , src , attr , optional = False , converter = None , default = _default ):
5256 if attr not in src :
5357 if not optional :
5458 raise InvalidOMFFile (f"Attribute { attr } missing" )
@@ -67,8 +71,8 @@ def __require_attr(cls, src, attr, required_value):
6771 if value != required_value :
6872 raise InvalidOMFFile (f"Invalid attribute { attr } . Expected: { required_value } , actual: { value } " )
6973
70- @staticmethod
71- def __copy_attr (src , src_attr , dst , dst_attr = None ,
74+ @classmethod
75+ def __copy_attr (cls , src , src_attr , dst , dst_attr = None ,
7276 optional_src = False , optional_dst = False , converter = None , default = _default ):
7377 if dst_attr is None :
7478 dst_attr = src_attr
@@ -91,14 +95,29 @@ def __copy_attr(src, src_attr, dst, dst_attr=None,
9195 else :
9296 setattr (dst , dst_attr , value )
9397
94- # reading numpy arrays
98+ # reading arrays
9599 def _load_array (self , scalar_array ):
96100 scalar_class = self .__get_attr (scalar_array , '__class__' )
97- shape_lookup = {'Vector3Array' : ('*' , 3 ),
98- }
101+ converter_lookup = {
102+ 'StringArray' : self ._test_data_needed ,
103+ 'DateTimeArray' : self ._test_data_needed ,
104+ 'ColorArray' : self ._test_data_needed ,
105+ }
106+ shape_lookup = {
107+ 'ScalarArray' : ('*' ,),
108+ 'Int2Array' : ('*' , 2 ),
109+ 'Int3Array' : ('*' , 3 ),
110+ 'Vector2Array' : ('*' , 2 ),
111+ 'Vector3Array' : ('*' , 3 ),
112+ }
113+ converter = self .__get_attr (converter_lookup , scalar_class , optional = True )
114+ if converter is not None :
115+ return converter (scalar_array )
116+
99117 shape = self .__get_attr (shape_lookup , scalar_class )
100118 shape = tuple (- 1 if s == '*' else s for s in shape )
101119 base_vector = self .__get_attr (scalar_array , 'array' )
120+
102121 start = self .__get_attr (base_vector , 'start' )
103122 length = self .__get_attr (base_vector , 'length' )
104123 dtype = self .__get_attr (base_vector , 'dtype' )
@@ -168,8 +187,31 @@ def _copy_textures(self, src, dst):
168187 dst .textures = [self ._copy_texture (texture_uuid ) for texture_uuid in texture_uuids ]
169188
170189 # data columns
171- def _copy_data (self , src , dst ):
172- pass
190+ def _copy_scalar_data (self , data_v1 ):
191+ data = attribute .NumericAttribute ()
192+ self ._copy_scalar_array (data_v1 , 'array' , data )
193+ if self .__get_attr (data_v1 , 'colormap' , optional = True ) is not None :
194+ self ._test_data_needed (data_v1 , data )
195+ return data
196+
197+ def _copy_project_element_data (self , data_uuid , valid_locations ):
198+ data_v1 = self .__get_attr (self ._project , data_uuid )
199+ data_class = self .__get_attr (data_v1 , '__class__' )
200+
201+ converters = {'ScalarData' : self ._copy_scalar_data }
202+ converter = self .__get_attr (converters , data_class )
203+ data = converter (data_v1 )
204+ location = self .__get_attr (data_v1 , 'location' )
205+ if location not in valid_locations :
206+ raise InvalidOMFFile (f'Invalid data location: { location } ' )
207+ self ._copy_content_model (data_v1 , data )
208+ return data
209+
210+ def _copy_data (self , src , dst , valid_locations ):
211+ data_uuids = self .__get_attr (src , 'data' , optional = True )
212+ if data_uuids is None :
213+ return
214+ dst .attributes = [self ._copy_project_element_data (data_uuid , valid_locations ) for data_uuid in data_uuids ]
173215
174216 # points
175217 def _copy_pointset_element (self , points_v1 ):
@@ -185,17 +227,35 @@ def _copy_pointset_element(self, points_v1):
185227 valid_locations = ('vertices' ,)
186228 return points , valid_locations
187229
230+ # line sets
231+ def _copy_lineset_element (self , lines_v1 ):
232+ geometry_uuid = self .__get_attr (lines_v1 , 'geometry' )
233+ geometry_v1 = self .__get_attr (self ._project , geometry_uuid )
234+
235+ lines = lineset .LineSet ()
236+ self .__copy_attr (lines_v1 , 'subtype' , lines .metadata )
237+ self ._copy_project_element_geometry (geometry_v1 , lines )
238+ self ._copy_scalar_array (geometry_v1 , 'vertices' , lines )
239+ self ._copy_scalar_array (geometry_v1 , 'segments' , lines )
240+
241+ valid_locations = ('vertices' , 'segments' )
242+ return lines , valid_locations
243+
188244 # element list
189245 def _copy_project_element (self , element_uuid ):
190246 element_v1 = self .__get_attr (self ._project , element_uuid )
191247 element_class = self .__get_attr (element_v1 , '__class__' )
192248
193- converters = {'PointSetElement' : self ._copy_pointset_element }
249+ converters = {'PointSetElement' : self ._copy_pointset_element ,
250+ 'LineSetElement' : self ._copy_lineset_element ,
251+ 'SurfaceElement' : self ._test_data_needed ,
252+ 'VolumeElement' : self ._test_data_needed ,
253+ }
194254 converter = self .__get_attr (converters , element_class )
195255 element , valid_locations = converter (element_v1 )
196256
197257 self ._copy_content_model (element_v1 , element )
198- self ._copy_data (element_v1 , element )
258+ self ._copy_data (element_v1 , element , valid_locations )
199259 self .__copy_attr (element_v1 , 'color' , element .metadata )
200260 return element
201261
0 commit comments