class SensorsH5(H5File): def __init__(self, path): super(SensorsH5, self).__init__(path) self.sensors_type = Scalar(Sensors.sensors_type, self) self.labels = DataSet(NArray(dtype=STORE_STRING), self, "labels") self.locations = DataSet(Sensors.locations, self) self.has_orientation = Scalar(Sensors.has_orientation, self) self.orientations = DataSet(Sensors.orientations, self) self.number_of_sensors = Scalar(Sensors.number_of_sensors, self) self.usable = DataSet(Sensors.usable, self) def get_locations(self): return self.locations.load() def get_labels(self): return self.labels.load() def store(self, datatype, scalars_only=False, store_references=False): # type: (Sensors, bool, bool) -> None super(SensorsH5, self).store(datatype, scalars_only, store_references) self.labels.store(datatype.labels.astype(STORE_STRING)) def load_into(self, datatype): # type: (Sensors) -> None super(SensorsH5, self).load_into(datatype) datatype.labels = self.labels.load().astype(MEMORY_STRING)
class ConnectivityH5(H5File): def __init__(self, path): super(ConnectivityH5, self).__init__(path) self.region_labels = DataSet(NArray(dtype=STORE_STRING), self, "region_labels") self.weights = DataSet(Connectivity.weights, self) self.undirected = Scalar(Connectivity.undirected, self) self.tract_lengths = DataSet(Connectivity.tract_lengths, self) self.centres = DataSet(Connectivity.centres, self) self.cortical = DataSet(Connectivity.cortical, self) self.hemispheres = DataSet(Connectivity.hemispheres, self) self.orientations = DataSet(Connectivity.orientations, self) self.areas = DataSet(Connectivity.areas, self) self.number_of_regions = Scalar(Connectivity.number_of_regions, self) self.number_of_connections = Scalar(Connectivity.number_of_connections, self) self.parent_connectivity = Scalar(Connectivity.parent_connectivity, self) self.saved_selection = Json(Connectivity.saved_selection, self) def get_centres(self): return self.centres.load() def get_region_labels(self): return self.region_labels.load() def store(self, datatype, scalars_only=False, store_references=False): # type: (Connectivity, bool, bool) -> None super(ConnectivityH5, self).store(datatype, scalars_only, store_references) self.region_labels.store(datatype.region_labels.astype(STORE_STRING)) def load_into(self, datatype): # type: (Connectivity) -> None super(ConnectivityH5, self).load_into(datatype) datatype.region_labels = self.region_labels.load().astype(MEMORY_STRING)
class StimuliSurfaceH5(H5File): def __init__(self, path): super(StimuliSurfaceH5, self).__init__(path) self.spatial = Scalar(Attr(str), self, name='spatial') self.temporal = Scalar(Attr(str), self, name='temporal') self.surface = Reference(StimuliSurface.surface, self) self.focal_points_surface = DataSet( StimuliSurface.focal_points_surface, self) self.focal_points_triangles = DataSet( StimuliSurface.focal_points_triangles, self) def store(self, datatype, scalars_only=False): self.surface.store(datatype.surface) self.focal_points_surface.store(datatype.focal_points_surface) self.focal_points_triangles.store(datatype.focal_points_triangles) self.spatial.store(datatype.spatial.to_json(datatype.spatial)) self.temporal.store(datatype.temporal.to_json(datatype.temporal)) def load_into(self, datatype): datatype.gid = self.gid.load() datatype.surface = self.surface.load() datatype.focal_points_triangles = self.focal_points_triangles.load() datatype.focal_points_surface = self.focal_points_surface.load() spatial_eq = self.spatial.load() spatial_eq = datatype.spatial.from_json(spatial_eq) datatype.spatial = spatial_eq temporal_eq = self.temporal.load() temporal_eq = datatype.temporal.from_json(temporal_eq) datatype.temporal = temporal_eq
class PropsDataTypeFile(H5File): def __init__(self, path): super(PropsDataTypeFile, self).__init__(path) self.n_node = Scalar(PropsDataType.n_node, self) # You cannot define Accessors for trait_properties # You have to manually map them, using datatype independent accessors # breaks: self.weights = DataSet(PropsDataType.weights, self) self.weights = DataSet(NArray(), self, name='weights') # As any independent accessors weights will be ignored by H5File.load_into, H5File.store # Properties have no obvious serialization semantics. # They might not be writable or may need a specific initalisation order # in this case setting n_node before weights self.is_directed = Scalar(Attr(bool), self, name='is_directed') self.once = DataSet(NArray(), self, name='once') def store(self, datatype, scalars_only=False): super(PropsDataTypeFile, self).store(datatype, scalars_only=scalars_only) # handle the trait_properties manually self.weights.store(datatype.weights) # is_directed is read only, We choose to store it in the h5 as an example # it will never be read from there. # One might want to store these in h5 so that manually opened h5 are more informative self.is_directed.store(datatype.is_directed) # self.once is read only, no need to store def load_into(self, datatype): super(PropsDataTypeFile, self).load_into(datatype) # n_node is loaded, so weights will not complain about missing shape info datatype.weights = self.weights.load()
class SimulatorH5(SimulatorConfigurationH5): def __init__(self, path): super(SimulatorH5, self).__init__(path) self.connectivity = Reference(Simulator.connectivity, self) self.conduction_speed = Scalar(Simulator.conduction_speed, self) self.coupling = Reference(Simulator.coupling, self) self.surface = Reference(Simulator.surface, self) self.stimulus = Reference(Simulator.stimulus, self) self.model = Reference(Simulator.model, self) self.integrator = Reference(Simulator.integrator, self) self.initial_conditions = DataSet(Simulator.initial_conditions, self) self.monitors = Json(Simulator.monitors, self) self.simulation_length = Scalar(Simulator.simulation_length, self) self.simulation_state = Reference(Attr(field_type=uuid.UUID), self, name='simulation_state') def store(self, datatype, scalars_only=False, store_references=False): # type: (Simulator, bool, bool) -> None self.gid.store(datatype.gid) self.connectivity.store(datatype.connectivity) self.conduction_speed.store(datatype.conduction_speed) self.initial_conditions.store(datatype.initial_conditions) self.simulation_length.store(datatype.simulation_length) integrator_gid = self.store_config_as_reference(datatype.integrator) self.integrator.store(integrator_gid) coupling_gid = self.store_config_as_reference(datatype.coupling) self.coupling.store(coupling_gid) model_gid = self.store_config_as_reference(datatype.model) self.model.store(model_gid) # TODO: handle multiple monitors monitor_gid = self.store_config_as_reference(datatype.monitors[0]) self.monitors.store([monitor_gid.hex]) if datatype.surface: cortex_gid = self.store_config_as_reference(datatype.surface) self.surface.store(cortex_gid) if datatype.stimulus: self.stimulus.store(datatype.stimulus) self.type.store(self.get_full_class_name(type(datatype))) def load_into(self, datatype): # type: (Simulator) -> None datatype.conduction_speed = self.conduction_speed.load() datatype.initial_conditions = self.initial_conditions.load() datatype.simulation_length = self.simulation_length.load() datatype.integrator = self.load_from_reference(self.integrator.load()) datatype.coupling = self.load_from_reference(self.coupling.load()) datatype.model = self.load_from_reference(self.model.load()) # TODO: handle multiple monitors datatype.monitors = [self.load_from_reference(self.monitors.load()[0])] if self.surface.load(): datatype.surface = self.load_from_reference(self.surface.load())
class RegionMappingH5(H5File): def __init__(self, path): super(RegionMappingH5, self).__init__(path) self.array_data = DataSet(RegionMapping.array_data, self) self.connectivity = Reference(RegionMapping.connectivity, self) self.surface = Reference(RegionMapping.surface, self) def get_region_mapping_slice(self, start_idx, end_idx): """ Get a slice of the region mapping as used by the region viewers. For each vertex on the surface, alpha-indices will be the closest region-index :param start_idx: vertex index on the surface :param end_idx: vertex index on the surface :return: NumPy array with [closest_reg_idx ...] """ return self.array_data.load()[int(start_idx):int(end_idx)].T
class StimuliRegionH5(H5File): def __init__(self, path): super(StimuliRegionH5, self).__init__(path) self.spatial = Scalar(Attr(str), self, name='spatial') self.temporal = Scalar(Attr(str), self, name='temporal') self.connectivity = Reference(StimuliRegion.connectivity, self) self.weight = DataSet(StimuliRegion.weight, self) def store(self, datatype, scalars_only=False): self.connectivity.store(datatype.connectivity) self.weight.store(datatype.weight) self.spatial.store(datatype.spatial.to_json(datatype.spatial)) self.temporal.store(datatype.temporal.to_json(datatype.temporal)) def load_into(self, datatype): datatype.gid = self.gid.load() datatype.connectivity = self.connectivity.load() datatype.weight = self.weight.load() spatial_eq = self.spatial.load() spatial_eq = datatype.spatial.from_json(spatial_eq) datatype.spatial = spatial_eq temporal_eq = self.temporal.load() temporal_eq = datatype.temporal.from_json(temporal_eq) datatype.temporal = temporal_eq
class SimulationStateH5(H5File): def __init__(self, path): super(SimulationStateH5, self).__init__(path) self.history = DataSet(NArray(), self, name='history') self.current_state = DataSet(NArray(), self, name='current_state') self.current_step = Scalar(Int(), self, name='current_step') for i in range(1, 16): setattr(self, 'monitor_stock_%i' % i, DataSet(NArray(), self, name='monitor_stock_%i' % i)) self.integrator_noise_rng_state_algo = Scalar( Attr(str), self, name='integrator_noise_rng_state_algo') self.integrator_noise_rng_state_keys = DataSet( NArray(dtype='uint32'), self, name='integrator_noise_rng_state_keys') self.integrator_noise_rng_state_pos = Scalar( Int(), self, name='integrator_noise_rng_state_pos') self.integrator_noise_rng_state_has_gauss = Scalar( Int(), self, name='integrator_noise_rng_state_has_gauss') self.integrator_noise_rng_state_cached_gauss = Scalar( Float(), self, name='integrator_noise_rng_state_cached_gauss') def store(self, simulator, scalars_only=False): # type: (Simulator, bool) -> None self.history.store(simulator.history.buffer.copy()) self.current_step.store(simulator.current_step) self.current_state.store(simulator.current_state) for i, monitor in enumerate(simulator.monitors): field_name = "monitor_stock_" + str(i + 1) getattr(self, field_name).store(monitor._stock) if isinstance(simulator.integrator, IntegratorStochastic): rng_state = simulator.integrator.noise.random_stream.get_state() self.integrator_noise_rng_state_algo.store(rng_state[0]) self.integrator_noise_rng_state_keys.store(rng_state[1]) self.integrator_noise_rng_state_pos.store(rng_state[2]) self.integrator_noise_rng_state_has_gauss.store(rng_state[3]) self.integrator_noise_rng_state_cached_gauss.store(rng_state[4]) def load_into(self, simulator): """ Populate a Simulator object from current stored-state. """ simulator.history.initialize(self.history.load()) simulator.current_step = self.current_step.load() simulator.current_state = self.current_state.load() for i, monitor in enumerate(simulator.monitors): monitor._stock = getattr(self, "monitor_stock_" + str(i + 1)).load() if self.integrator_noise_rng_state_algo is not None: rng_state = (self.integrator_noise_rng_state_algo.load(), self.integrator_noise_rng_state_keys.load(), self.integrator_noise_rng_state_pos.load(), self.integrator_noise_rng_state_has_gauss.load(), self.integrator_noise_rng_state_cached_gauss.load()) simulator.integrator.noise.random_stream.set_state(rng_state)
class TractsH5(H5File): MAX_N_VERTICES = 2 ** 16 def __init__(self, path): super(TractsH5, self).__init__(path) self.vertices = DataSet(Tracts.vertices, self, expand_dimension=0) self.tract_start_idx = DataSet(Tracts.tract_start_idx, self) self.tract_region = DataSet(Tracts.tract_region, self) self.region_volume_map = Reference(Tracts.region_volume_map, self) def get_tract(self, i): """ get a tract by index """ start, end = self.tract_start_idx[i:i + 2] return self.vertices[start:end] def _get_tract_ids(self, region_id): tract_ids = numpy.where(self.tract_region.load() == region_id)[0] return tract_ids def _get_track_ids_webgl_chunks(self, region_id): """ webgl can draw up to MAX_N_VERTICES vertices in a draw call. Assuming that no one track exceeds this limit we partition the tracts such that each track bundle has fewer than the max vertices :return: the id's of the tracts in a region chunked by the above criteria. """ # We have to split the int64 range in many uint16 ranges tract_ids = self._get_tract_ids(region_id) tract_id_chunks = [] chunk = [] count = 0 tidx = 0 while tidx < len(tract_ids): # tidx always grows tid = tract_ids[tidx] start, end = self.tract_start_idx[tid:tid + 2] track_len = end - start if track_len >= self.MAX_N_VERTICES: raise ValueError('Currently tracts are too long to be handled!') count += track_len if count < self.MAX_N_VERTICES: # add this track to the current chunk and advance to next track chunk.append(tid) tidx += 1 else: # stay with the same track and start a new chunk tract_id_chunks.append(chunk) chunk = [] count = 0 if chunk: tract_id_chunks.append(chunk) return tract_id_chunks def get_vertices(self, region_id, slice_number=0): """ Concatenates the vertices for all tracts starting in region_id. Returns a completely flat array as required by gl.bindBuffer apis """ region_id = int(region_id) slice_number = int(slice_number) chunks = self._get_track_ids_webgl_chunks(region_id) tract_ids = chunks[slice_number] tracts_vertices = [] for tid in tract_ids: tracts_vertices.append(self.get_tract(tid)) self.close() if tracts_vertices: tracts_vertices = numpy.concatenate(tracts_vertices) return tracts_vertices.ravel() else: return numpy.array([]) def get_line_starts(self, region_id): """ Returns a compact representation of the element buffers required to draw the streams via gl.drawElements A list of indices that describe where the first vertex for a tract is in the vertex array returned by get_tract_vertices_starting_in_region """ region_id = int(region_id) chunks = self._get_track_ids_webgl_chunks(region_id) chunk_line_starts = [] tract_start_idx = self.tract_start_idx # traits make the . expensive for tract_ids in chunks: offset = 0 tract_offsets = [0] for tid in tract_ids: start, end = tract_start_idx[tid:tid + 2] track_len = end - start offset += track_len tract_offsets.append(offset) chunk_line_starts.append(tract_offsets) return chunk_line_starts def write_vertices_slice(self, partial_result): """ Append a new value to the ``vertices`` attribute. """ self.vertices.append(partial_result)
class SurfaceH5(H5File): def __init__(self, path): super(SurfaceH5, self).__init__(path) self.vertices = DataSet(Surface.vertices, self) self.triangles = DataSet(Surface.triangles, self) self.vertex_normals = DataSet(Surface.vertex_normals, self) self.triangle_normals = DataSet(Surface.triangle_normals, self) self.number_of_vertices = Scalar(Surface.number_of_vertices, self) self.number_of_triangles = Scalar(Surface.number_of_triangles, self) self.edge_mean_length = Scalar(Surface.edge_mean_length, self) self.edge_min_length = Scalar(Surface.edge_min_length, self) self.edge_max_length = Scalar(Surface.edge_max_length, self) self.zero_based_triangles = Scalar(Surface.zero_based_triangles, self) self.split_triangles = DataSet(NArray(dtype=int), self, name="split_triangles") self.number_of_split_slices = Scalar(Int(), self, name="number_of_split_slices") self.split_slices = Json(Attr(field_type=dict), self, name="split_slices") self.bi_hemispheric = Scalar(Surface.bi_hemispheric, self) self.surface_type = Scalar(Surface.surface_type, self) self.valid_for_simulations = Scalar(Surface.valid_for_simulations, self) # cached header like information, needed to interpret the rest of the file # Load the data that is required in order to interpret the file format # number_of_vertices and split_slices are needed for the get_vertices_slice read call if not self.is_new_file: self._split_slices = self.split_slices.load() self._split_triangles = self.split_triangles.load() self._number_of_vertices = self.number_of_vertices.load() self._number_of_triangles = self.number_of_triangles.load() self._number_of_split_slices = self.number_of_split_slices.load() self._bi_hemispheric = self.bi_hemispheric.load() # else: this is a new file def store(self, datatype, scalars_only=False, store_references=True): # type: (Surface, bool, bool) -> None super(SurfaceH5, self).store(datatype, scalars_only=scalars_only, store_references=store_references) # When any of the header fields change we have to update our cache of them # As they are an invariant of SurfaceH5 we don't do that in the accessors but here. # This implies that direct public writes to them via the accessors will break the invariant. # todo: should we make the accessors private? In complex formats like this one they are private # for this type direct writes to accessors should not be done self._number_of_vertices = datatype.number_of_vertices self._number_of_triangles = datatype.number_of_triangles self._bi_hemispheric = datatype.bi_hemispheric self.prepare_slices(datatype) self.number_of_split_slices.store(self._number_of_split_slices) self.split_slices.store(self._split_slices) self.split_triangles.store(self._split_triangles) def read_subtype_attr(self): return self.surface_type.load() def center(self): """ Compute the center of the surface as the mean spot on all the three axes. """ # is this different from return numpy.mean(self.vertices, axis=0) ? return [float(numpy.mean(self.vertices[:, 0])), float(numpy.mean(self.vertices[:, 1])), float(numpy.mean(self.vertices[:, 2]))] def get_number_of_split_slices(self): return self._number_of_split_slices def prepare_slices(self, datatype): """ Before storing Surface in H5, make sure vertices/triangles are split in slices that are readable by WebGL. WebGL only supports triangle indices in interval [0.... 2^16] """ # Do not split when size is conveniently small: if self._number_of_vertices <= SPLIT_MAX_SIZE + SPLIT_BUFFER_SIZE and not self._bi_hemispheric: self._number_of_split_slices = 1 self._split_slices = {0: {KEY_TRIANGLES: {KEY_START: 0, KEY_END: self._number_of_triangles}, KEY_VERTICES: {KEY_START: 0, KEY_END: self._number_of_vertices}, KEY_HEMISPHERE: HEMISPHERE_UNKNOWN}} self._split_triangles = numpy.array([], dtype=numpy.int32) return # Compute the number of split slices: left_hemisphere_slices = 0 left_hemisphere_vertices_no = 0 if self._bi_hemispheric: # when more than one hemisphere right_hemisphere_vertices_no = numpy.count_nonzero(datatype.hemisphere_mask) left_hemisphere_vertices_no = self._number_of_vertices - right_hemisphere_vertices_no LOG.debug("Right %d Left %d" % (right_hemisphere_vertices_no, left_hemisphere_vertices_no)) left_hemisphere_slices = self._get_slices_number(left_hemisphere_vertices_no) self._number_of_split_slices = left_hemisphere_slices self._number_of_split_slices += self._get_slices_number(right_hemisphere_vertices_no) LOG.debug("Hemispheres Total %d Left %d" % (self._number_of_split_slices, left_hemisphere_slices)) else: # when a single hemisphere self._number_of_split_slices = self._get_slices_number(self._number_of_vertices) LOG.debug("Start to compute surface split triangles and vertices") split_triangles = [] ignored_triangles_counter = 0 self._split_slices = {} for i in range(self._number_of_split_slices): split_triangles.append([]) if not self._bi_hemispheric: self._split_slices[i] = {KEY_VERTICES: {KEY_START: i * SPLIT_MAX_SIZE, KEY_END: min(self._number_of_vertices, (i + 1) * SPLIT_MAX_SIZE + SPLIT_BUFFER_SIZE)}, KEY_HEMISPHERE: HEMISPHERE_UNKNOWN} else: if i < left_hemisphere_slices: self._split_slices[i] = {KEY_VERTICES: {KEY_START: i * SPLIT_MAX_SIZE, KEY_END: min(left_hemisphere_vertices_no, (i + 1) * SPLIT_MAX_SIZE + SPLIT_BUFFER_SIZE)}, KEY_HEMISPHERE: HEMISPHERE_LEFT} else: self._split_slices[i] = {KEY_VERTICES: {KEY_START: left_hemisphere_vertices_no + (i - left_hemisphere_slices) * SPLIT_MAX_SIZE, KEY_END: min(self._number_of_vertices, left_hemisphere_vertices_no + SPLIT_MAX_SIZE * (i + 1 - left_hemisphere_slices) + SPLIT_BUFFER_SIZE)}, KEY_HEMISPHERE: HEMISPHERE_RIGHT} # Iterate Triangles and find the slice where it fits best, based on its vertices indexes: for i in range(self._number_of_triangles): current_triangle = [datatype.triangles[i][j] for j in range(3)] fit_slice, transformed_triangle = self._find_slice(current_triangle) if fit_slice is not None: split_triangles[fit_slice].append(transformed_triangle) else: # triangle ignored, as it has vertices over multiple slices. ignored_triangles_counter += 1 continue final_split_triangles = [] last_triangles_idx = 0 # Concatenate triangles, to be stored in a single HDF5 array. for slice_idx, split_ in enumerate(split_triangles): self._split_slices[slice_idx][KEY_TRIANGLES] = {KEY_START: last_triangles_idx, KEY_END: last_triangles_idx + len(split_)} final_split_triangles.extend(split_) last_triangles_idx += len(split_) self._split_triangles = numpy.array(final_split_triangles, dtype=numpy.int32) if ignored_triangles_counter > 0: LOG.warning("Ignored triangles from multiple hemispheres: " + str(ignored_triangles_counter)) LOG.debug("End compute surface split triangles and vertices " + str(self._split_slices)) @staticmethod def _get_slices_number(vertices_number): """ Slices are for vertices [SPLIT_MAX_SIZE * i ... SPLIT_MAX_SIZE * (i + 1) + SPLIT_BUFFER_SIZE] Slices will overlap : |........SPLIT_MAX_SIZE|...SPLIT_BUFFER_SIZE| <-- split 1 |......... SPLIT_MAX_SIZE|...SPLIT_BUFFER_SIZE| <-- split 2 If we have trailing data smaller than the SPLIT_BUFFER_SIZE, then we no longer split but we need to have at least 1 slice. """ slices_number, trailing = divmod(vertices_number, SPLIT_MAX_SIZE) if trailing > SPLIT_BUFFER_SIZE or (slices_number == 0 and trailing > 0): slices_number += 1 return slices_number def _find_slice(self, triangle): mn = min(triangle) mx = max(triangle) for i in range(self._number_of_split_slices): v = self._split_slices[i][KEY_VERTICES] # extracted for performance slice_start = v[KEY_START] if slice_start <= mn and mx < v[KEY_END]: return i, [triangle[j] - slice_start for j in range(3)] return None, triangle def get_slice_vertex_boundaries(self, slice_idx): if str(slice_idx) in self._split_slices: start_idx = max(0, self._split_slices[str(slice_idx)][KEY_VERTICES][KEY_START]) end_idx = min(self._split_slices[str(slice_idx)][KEY_VERTICES][KEY_END], self._number_of_vertices) return start_idx, end_idx else: LOG.warning("Could not access slice indices, possibly due to an incompatibility with code update!") return 0, min(SPLIT_BUFFER_SIZE, self._number_of_vertices) def _get_slice_triangle_boundaries(self, slice_idx): if str(slice_idx) in self._split_slices: start_idx = max(0, self._split_slices[str(slice_idx)][KEY_TRIANGLES][KEY_START]) end_idx = min(self._split_slices[str(slice_idx)][KEY_TRIANGLES][KEY_END], self._number_of_triangles) return start_idx, end_idx else: LOG.warn("Could not access slice indices, possibly due to an incompatibility with code update!") return 0, self._number_of_triangles def get_vertices_slice(self, slice_number=0): """ Read vertices slice, to be used by WebGL visualizer. """ slice_number = int(slice_number) start_idx, end_idx = self.get_slice_vertex_boundaries(slice_number) return self.vertices[start_idx: end_idx: 1] def get_vertex_normals_slice(self, slice_number=0): """ Read vertex-normal slice, to be used by WebGL visualizer. """ slice_number = int(slice_number) start_idx, end_idx = self.get_slice_vertex_boundaries(slice_number) return self.vertex_normals[start_idx: end_idx: 1] def get_triangles_slice(self, slice_number=0): """ Read split-triangles slice, to be used by WebGL visualizer. """ if self._number_of_split_slices == 1: return self.triangles.load() slice_number = int(slice_number) start_idx, end_idx = self._get_slice_triangle_boundaries(slice_number) return self._split_triangles[start_idx: end_idx: 1] def get_lines_slice(self, slice_number=0): """ Read the gl lines values for the current slice number. """ return Surface._triangles_to_lines(self.get_triangles_slice(slice_number)) def get_slices_to_hemisphere_mask(self): """ :return: a vector af length number_of_slices, with 1 when current chunk belongs to the Right hemisphere """ if not self._bi_hemispheric or self._split_slices is None: return None result = [1] * self._number_of_split_slices for key, value in self._split_slices.items(): if value[KEY_HEMISPHERE] == HEMISPHERE_LEFT: result[int(key)] = 0 return result # todo: many of these do not belong in the data access layer but higher, adapter or gui layer ####################################### Split for Picking ####################################### def get_pick_vertices_slice(self, slice_number=0): """ Read vertices slice, to be used by WebGL visualizer with pick. """ slice_number = int(slice_number) slice_triangles = self.triangles[ slice_number * SPLIT_PICK_MAX_TRIANGLE: min(self._number_of_triangles, (slice_number + 1) * SPLIT_PICK_MAX_TRIANGLE) ] result_vertices = [] cache_vertices = self.vertices.load() for triang in slice_triangles: result_vertices.append(cache_vertices[triang[0]]) result_vertices.append(cache_vertices[triang[1]]) result_vertices.append(cache_vertices[triang[2]]) return numpy.array(result_vertices) def get_pick_vertex_normals_slice(self, slice_number=0): """ Read vertex-normals slice, to be used by WebGL visualizer with pick. """ slice_number = int(slice_number) slice_triangles = self.triangles[ slice_number * SPLIT_PICK_MAX_TRIANGLE: min(self.number_of_triangles.load(), (slice_number + 1) * SPLIT_PICK_MAX_TRIANGLE) ] result_normals = [] cache_vertex_normals = self.vertex_normals.load() for triang in slice_triangles: result_normals.append(cache_vertex_normals[triang[0]]) result_normals.append(cache_vertex_normals[triang[1]]) result_normals.append(cache_vertex_normals[triang[2]]) return numpy.array(result_normals) def get_pick_triangles_slice(self, slice_number=0): """ Read triangles slice, to be used by WebGL visualizer with pick. """ slice_number = int(slice_number) no_of_triangles = (min(self._number_of_triangles, (slice_number + 1) * SPLIT_PICK_MAX_TRIANGLE) - slice_number * SPLIT_PICK_MAX_TRIANGLE) triangles_array = numpy.arange(no_of_triangles * 3).reshape((no_of_triangles, 3)) return triangles_array