class DofGridSlice(CellGridSlice): dof_grid = WeakRef(DofCellGrid) def __init__(self, dof_grid, **args): self.dof_grid = dof_grid super(DofGridSlice, self).__init__(**args) cell_grid = Property() def _get_cell_grid(self): return self.dof_grid.cell_grid dofs = Property def _get_dofs(self): _, idx2 = self.idx_tuple return self.dof_grid.cell_dof_map[np.ix_( self.elems, self.cell_grid.grid_cell[idx2])]
class Exporter(HasTraits): '''Base class for visualization objects. Each state and operator objects like crease pattern or constraint can define provide tailored visualizations transferring the information into a view objects shared within a particular forming task or a whole forming process. ''' label = Str('default') '''Label of the visualization object. ''' forming_task = WeakRef(FormingTask) '''Link to the visual object to transform into the forming_task_view3d. ''' forming_task_changed = Event '''Event registering changes in the source object.
class DofGridSlice(CellGridSlice): dof_grid = WeakRef(DofCellGrid) def __init__(self, dof_grid, **args): self.dof_grid = dof_grid super(DofGridSlice, self).__init__(**args) cell_grid = Property(depends_on='dof_grid.+changed_structure') @cached_property def _get_cell_grid(self): return self.dof_grid.cell_grid dofs = Property def _get_dofs(self): idx1, idx2 = self.idx_tuple return self.dof_grid.cell_dof_map[ix_(self.elems, self.cell_grid.grid_cell[idx2])]
class VariedParam(HasTraits): """ Association between the spatial function and material parameter. """ mats_eval = WeakRef(IMATSEval) varname = String reference_value = Property(Float, depends_on='mats_eval') @cached_property def _get_reference_value(self): return getattr(self.mats_eval, self.varname) switch = Enum('constant', 'varied') spatial_fn = Instance(MFnNDGrid) variable = Property(Float) def _get_variable(self): return getattr(self.mats_eval, self.varname) def _set_variable(self, value): setattr(self.mats_eval, self.varname, value) def adjust_for_sctx(self, sctx): if self.switch == 'varied': X_pnt = sctx.fets_eval.get_X_pnt(sctx) # X_coord = zeros(3) # X_coord[0:2] = X_pnt coeff = self.spatial_fn(X_pnt) self.variable = self.reference_value * coeff traits_view = View(Group( Item('varname', style='readonly', show_label=False), Item('reference_value', style='readonly'), Item('switch', style='custom', show_label=False), Item('spatial_fn', style='custom', show_label=False, resizable=True)), resizable=True, height=800)
class FEGridNodeSlice(HasStrictTraits): '''General implementation of a slice within the FEGrid ''' fe_grid = WeakRef('ibvpy.mesh.fe_grid.FEGrid') grid_slice = Any def __repr__(self): return repr(self.grid_slice) dof_nodes = Property def _get_dof_nodes(self): return self.fe_grid.dof_grid.cell_grid.point_idx_grid[self.grid_slice] dofs = Property def _get_dofs(self): return self.fe_grid.dof_grid.dofs_Ia[self.dof_nodes, :] dof_X = Property def _get_dof_X(self): return self.fe_grid.dof_grid.cell_grid.point_X_arr[self.dof_nodes, :] geo_nodes = Property def _get_geo_nodes(self): return self.fe_grid.geo_grid.cell_grid.point_idx_grid[self.grid_slice] geo_x = Property def _get_geo_x(self): return self.fe_grid.geo_grid.cell_grid.point_x_arr[self.geo_nodes, :] geo_X = Property def _get_geo_X(self): return self.fe_grid.geo_grid.cell_grid.point_X_arr[self.geo_nodes, :]
class RTraceEval(HasTraits): name = Str('unnamed') ts = WeakRef(ITStepperEval) u_mapping = Callable eval = Callable def __call__(self, sctx, u, *args, **kw): # When crossing the levels - start a mapping # This method might have side effects for the context # - mapping of global to local values # args_mapped = [] kw_mapped = {} if self.u_mapping: u = self.u_mapping(sctx, u) # map everything that has been sent together with u # this might be the time derivatives of u or its # spatial integrals. # args_mapped = [self.u_mapping(sctx, u_value) for u_value in args] kw_mapped = {} for u_name, u_value in kw.items(): kw_mapped[u_name] = self.u_mapping(sctx, u_value) # Invoke the tracer evaluation. # try: val = self.eval(sctx, u, *args_mapped, **kw_mapped) except TypeError, e: raise TypeError, 'tracer name %s: %s %s' % ( self.name, e, self.eval) return val
class FEGridIdxSlice(HasStrictTraits): '''General implementation of a slice within the FEGrid ''' fe_grid = WeakRef('ibvpy.mesh.fe_grid.FEGrid') grid_slice = Any def __repr__(self): return repr(self.grid_slice) dof_grid_slice = Property(depends_on='fe_grid.dof_grid') @cached_property def _get_dof_grid_slice(self): return self.fe_grid.dof_grid[self.grid_slice] geo_grid_slice = Property(depends_on='fe_grid.geo_grid') @cached_property def _get_geo_grid_slice(self): return self.fe_grid.geo_grid[self.grid_slice] elem_grid = Property def _get_elem_grid(self): return self.dof_grid_slice.elem_grid elems = Property def _get_elems(self): return self.dof_grid_slice.elems dof_nodes = Property def _get_dof_nodes(self): return self.dof_grid_slice.nodes dofs = Property def _get_dofs(self): return self.dof_grid_slice.dofs dof_X = Property def _get_dof_X(self): return self.dof_grid_slice.point_X_arr geo_nodes = Property def _get_geo_nodes(self): return self.geo_grid_slice.nodes geo_x = Property def _get_geo_x(self): return self.geo_grid_slice.point_x_arr geo_X = Property def _get_geo_X(self): return self.geo_grid_slice.point_X_arr
class SimulationStep(HasStrictTraits): r"""Class implementing the transition of the formed object from its initial time to the target time :math:`t`. """ forming_task = WeakRef(FormingTask) r'''Backward link to the client forming tasks. This may be an incremental time stepping SimulationTask of a MappingTaks performed in a single iterative step. ''' source_config_changed = Event r'''Notification event for changes in the configuration of the optimization problem. ''' config = Instance(SimulationConfig) r'''Configuration of the optimization problem. ''' # ===================================================================== # Cached properties derived from configuration and position in the pipe # ===================================================================== cp_state = Property(Instance(CreasePatternState), depends_on='source_config_changed') r'''Crease pattern state. ''' @cached_property def _get_cp_state(self): return self.forming_task.formed_object fu = Property(depends_on='source_config_changed') r'''Goal function object. ''' @cached_property def _get_fu(self): self.config.fu.forming_task = self.forming_task return self.config.fu gu = Property(depends_on='source_config_changed') r'''Goal function object. ''' @cached_property def _get_gu(self): self.config.gu.forming_task = self.forming_task return self.config.gu gu_lst = Property(depends_on='source_config_changed') r'''Equality constraint object. ''' @cached_property def _get_gu_lst(self): for gu in self.config.gu_lst: gu.forming_task = self.forming_task return self.config.gu_lst hu_lst = Property(depends_on='source_config_changed') r'''Inequality constraint object. ''' @cached_property def _get_hu_lst(self): for hu in self.config.gu_lst: hu.forming_task = self.forming_task return self.config.hu_lst def __str__(self): s = '' for gu in self.gu_lst: s += str(gu) return s debug_level = DelegatesTo("config") # =========================================================================== # Configuration parameters for the iterative solver # =========================================================================== t = Float(1.0, auto_set=False, enter_set=True) r'''Target time within the step in the range (0,1). ''' # ========================================================================= # Output data # ========================================================================= x_0 = Property r'''Initial position of all nodes. ''' def _get_x_0(self): return self.X_0.reshape(-1, self.n_D) U = Property(Array(float)) r'''Intermediate displacement vector of the cp_state ''' def _get_U(self): return self.cp_state.U def _set_U(self, value): self.cp_state.U = value U_t = Property(depends_on='t') r'''Final displacement vector :math:`X` at target time :math:`t`. ''' @cached_property def _get_U_t(self): U_t = self._solve() return U_t X_t = Property() r'''Vector of node positions :math:`U` at target time :math:`t`. ''' def _get_X_t(self): return self.X_0[np.newaxis, :] + self.U_t x_t = Property() r'''Array of node positions :math:`x_{ij}` at target time :math:`t` - [node, dim]. ''' def _get_x_t(self): n_t = self.X_t.shape[0] return self.X_t.reshape(n_t, -1, self.n_D) u_t = Property() r'''Array of nodal displaceements :math:`x_{ij}` [node, dim]. ''' def _get_u_t(self): n_D = self.cp_state.n_D return self.U_t.reshape(-1, n_D) # =========================================================================== # Iterative solvers # =========================================================================== def _solve(self): '''Decide which solver to take and start it. ''' self.config.validate_input() if self.config.goal_function_type_ is not None: U_t = self._solve_fmin() else: # no goal function - switch to an implicit time-stepping algorithm print('SOLVING NR') U_t = self._solve_nr() return U_t def _solve_nr(self): '''Find the solution using the Newton-Raphson procedure. ''' i = 0 U_save = np.copy(self.U) acc = self.config.acc max_iter = self.config.MAX_ITER while i <= max_iter: dR = self.get_G_du_t(self.U) R = self.get_G_t(self.U) nR = np.linalg.norm(R) if nR < acc: print('==== converged in ', i, 'iterations ====') break try: d_U = np.linalg.solve(dR, -R) self.U += d_U # in-place increment i += 1 except Exception as inst: print('=== Problems solving iteration step %d ====' % i) print('=== Exception message: ', inst) self.U = U_save raise inst else: self.U = U_save print('==== did not converge in %d iterations ====' % i) # update the state object with the new displacement vector return self.U def _solve_fmin(self): '''Solve the problem using the Sequential Least Square Quadratic Programming method. ''' print('==== solving with SLSQP optimization ====') U_save = np.copy(self.U) d0 = self.get_f_t(self.U) eps = d0 * 1e-4 get_f_du_t = None get_G_du_t = None get_H_t = None get_H_du_t = None acc = self.config.acc max_iter = self.config.MAX_ITER if self.config.use_f_du: get_f_du_t = self.get_f_du_t if self.config.use_G_du: get_G_du_t = self.get_G_du_t if self.config.has_H: get_H_t = self.get_H_t if self.config.use_H_du: get_H_du_t = self.get_H_du_t info = fmin_slsqp( self.get_f_t, self.U, fprime=get_f_du_t, f_eqcons=self.get_G_t, fprime_eqcons=get_G_du_t, # f_ieqcons=get_H_t, # fprime_ieqcons=get_H_du_t, acc=acc, iter=max_iter, iprint=2, full_output=True, epsilon=eps) U, f, n_iter, imode, smode = info if imode == 0: print('(time: %g, iter: %d, f: %g)' % (self.t, n_iter, f)) else: # no convergence reached. self.U = U_save print('(time: %g, iter: %d, f: %g, err: %d, %s)' % \ (self.t, n_iter, f, imode, smode)) return U def clear_iter(self): self.u_it_list = [] record_iter = Bool(False) u_it_list = List # ========================================================================== # Goal function # ========================================================================== def get_f_t(self, U): '''Get the goal function value. ''' if self.record_iter: self.u_it_list.append(np.copy(U.reshape(-1, 3))) self.cp_state.U = U f = self.get_f() if self.debug_level > 0: print('f:\n', f) return f def get_f(self): return self.fu.get_f(self.t) def get_f_du_t(self, U): '''Get the goal function derivatives. ''' self.cp_state.U = U f_du = self.get_f_du() if self.debug_level > 2: print('f_du.shape:\n', f_du.shape) print('f_du:\n', f_du) return f_du def get_f_du(self): return self.fu.get_f_du(self.t) # ========================================================================== # Equality constraints # ========================================================================== def get_G_t(self, U): self.cp_state.U = U g = self.get_G(self.t) if self.debug_level > 1: print('G:\n', [g]) return g def get_G(self, t=0): g_lst = [gu.get_G(t) for gu in self.gu_lst] if (g_lst == []): return [] return np.hstack(g_lst) def get_G_du_t(self, U): self.cp_state.U = U return self.get_G_du(self.t) def get_G_du(self, t=0): g_du_lst = [gu.get_G_du(t) for gu in self.gu_lst] if (g_du_lst == []): return [] g_du = np.vstack(g_du_lst) if self.debug_level > 3: print('G_du.shape:\n', g_du.shape) print('G_du:\n', [g_du]) return g_du # ========================================================================== # Inequality constraints # ========================================================================== def get_H_t(self, U): self.cp_state.U = U h = self.get_H(self.t) if self.debug_level > 1: print('H:\n', [h]) return h def get_H(self, t=0): h_lst = [hu.get_H(t) for hu in self.hu_lst] if (h_lst == []): return [] return np.hstack(h_lst) def get_H_du_t(self, U): self.cp_state.U = U return self.get_H_du(self.t) def get_H_du(self, t=0): h_du_lst = [hu.get_H_du(t) for hu in self.hu_lst] if (h_du_lst == []): return [] h_du = np.vstack(h_du_lst) if self.debug_level > 3: print('G_du.shape:\n', h_du.shape) print('G_du:\n', [h_du]) return h_du
class BaseComponent(HasStrictTraits): """ The most base class of the Enaml component heierarchy. All Enaml classes should inherit from this class. This class is not meant to be used directly. """ #: The parent component of this component. It is stored as a weakref #: to mitigate issues with reference cycles. A top-level component's #: parent is None. parent = WeakRef('BaseComponent') #: The list of children components for this component. Subclasses #: should redefine this trait to restrict which types of children #: they allow if necessary. This list should not be manipulated #: outside of the ``*_child(...)`` methods so that the ui may be #: properly updated when the children change. children = List(Instance('BaseComponent')) #: The toolkit specific object that implements the behavior of #: this component and manages the gui toolkit object. Subclasses #: should redefine this trait to specify the specialized type of #: abstract_obj that is accepted. abstract_obj = Instance(AbstractTkBaseComponent) #: A list of hooks that are called during the setup process #: that can modify the behavior of the component such as installing #: listeners at the appropriate times. Hooks should normally be #: appended to this list instead of a list being assigned atomically. setup_hooks = List(Instance(AbstractSetupHook)) #: The toolkit that created this object. This does not need to #: be stored weakly because the toolkit does not maintain refs #: to the compoents that its constructors create. toolkit = Instance(Toolkit) #: Whether or not the widget is enabled. enabled = Bool(True) #: Whether or not the widget is visible. visible = Bool(True) #: Whether the component has been initialized for not. This will be set to #: True after all of the setup() steps defined here are completed. It should #: not be changed afterwards. This can be used to trigger certain actions #: that need to be called after the component has been set up. initialized = Bool(False) #: The background color of the widget bg_color = Property(ColorTrait, depends_on=['_user_bg_color', '_style_bg_color']) #: Private trait holding the user-set background color value _user_bg_color = ColorTrait #: Private trait holding the background color value from the style _style_bg_color = ColorTrait #: The foreground color of the widget fg_color = Property(ColorTrait, depends_on=['_user_fg_color', '_style_fg_color']) #: Private trait holding the user-set foreground color value _user_fg_color = ColorTrait #: Private trait holding the foreground color value from the style _style_fg_color = ColorTrait #: The foreground color of the widget font = Property(FontTrait, depends_on=['_user_font', '_style_font']) #: Private trait holding the user-set foreground color value _user_font = FontTrait #: Private trait holding the foreground color value from the style _style_font = FontTrait #: The attributes on this class that can be set by the styling mechanism _style_tags = Tuple(Str, ('bg_color', 'fg_color', 'font')) #: The optional style identifier for the StyleSheet system. style_id = Str #: The optional style type for the StyleSheet system. This #: is set by default by the constructor object. style_type = Str #: The optional style class for the StyleSheet system. style_class = Str #: An optional name to give to this component to assist in finding #: it in the tree. name = Str def add_child(self, child): """ Add the child to this component. Arguments --------- child : Instance(BaseComponent) The child to add to the component. """ self.children.append(child) def remove_child(self, child): """ Remove the child from this component. Arguments --------- child : Instance(BaseComponent) The child to remove from the container. """ try: self.children.remove(child) except ValueError: raise ValueError('Child %s not in children.' % child) def replace_child(self, child, other_child): """ Replace a child with another child. Arguments --------- child : Instance(BaseComponent) The child being replaced. other_child : Instance(BaseComponent) The child taking the place of the removed child. """ try: idx = self.children.index(child) except ValueError: raise ValueError('Child %s not in children.' % child) self.children[idx] = other_child def swap_children(self, child, other_child): """ Swap the position of the two children. Arguments --------- child : Instance(BaseComponent) The first child in the swap. other_child : Instance(BaseComponent) The second child in the swap. """ try: idx = self.children.index(child) except ValueError: raise ValueError('Child %s not in children.' % child) try: other_idx = self.children.index(other_child) except ValueError: raise ValueError('Child %s not in children.' % other_child) self.children[idx] = other_child self.children[other_idx] = child def traverse(self): """ Yields all of the nodes in the tree in breadth first order. """ deq = deque([self]) while deq: item = deq.popleft() yield item deq.extend(item.children) def find_by_name(self, name): """ Find a component in this tree by name. This method will traverse the tree of components, breadth first, from this point downward, looking for a component with the given name. The first one with the given name is returned, or None if no component is found. Parameters ---------- name : string The name of the component for which to search. Returns ------- result : BaseComponent or None The first component found with the given name, or None if no component is found. """ for cmpnt in self.traverse(): if cmpnt.name == name: return cmpnt def set_style_sheet(self, style_sheet): """ Sets the style sheet for this component. Arguments --------- style_sheet : StyleSheet The style sheet instance for this component. """ raise NotImplementedError('Implement me!') def setup(self, parent=None): """ Run the setup process for the ui tree. This method splits up the setup process into several passes: 1) The child shell objects are given a reference to their parent 2) The abstract objects are given a reference to the shell object 3) The abstract objects create their internal toolkit object 4) The abstract objects initialize their internal toolkit object 5) The abstract objects bind their event handlers 6) The abstract object is added as a listener to the shell object Each of these methods are performed top down. Setup hooks are called for items 3, 4, and 5. After step 6, the `initialized` trait is set to True. Parameters ---------- parent : native toolkit widget, optional If embedding this BaseComponent into a non-Enaml GUI, use this to pass the appropriate toolkit widget that should be the parent. """ self.set_parent_refs() self.set_shell_refs() self.create(parent) self.initialize() self.bind() self.set_listeners() self.initialized = True def set_parent_refs(self): """ Assigns a reference to self for every child in children and dispatches the tree top down. This should not normally be called by user code. """ for child in self.children: child.parent = self child.set_parent_refs() def set_shell_refs(self): """ Assigns a reference to self to the abstract obj and dispatches the tree top down. This should not normally be called by user code. """ self.abstract_obj.shell_obj = self for child in self.children: child.set_shell_refs() @setup_hook def create(self, parent): """ A setup method that tells the abstract object to create its internal toolkit object. This should not normally be called by user code. """ self.abstract_obj.create(parent) # FIXME: toolkit_widget is defined on Component, not BaseComponent. # FIXME: technically, we allow toolkit_widget to be something that is # not precisely a real toolkit widget (e.g. a QLayout). self_widget = self.toolkit_widget for child in self.children: child.create(self_widget) @setup_hook def initialize(self): """ A setup method that tells the abstract object to initialize its internal toolkit object. This should not normally be called by user code. """ self.abstract_obj.initialize() for child in self.children: child.initialize() @setup_hook def bind(self): """ A setup method that tells the abstract object to bind its event handlers to its internal toolkit object. This should not normally be called by user code. """ self.abstract_obj.bind() for child in self.children: child.bind() def set_listeners(self): """ Sets the abstract object as a traits listener for this component with a prefix of 'shell'. """ self.add_trait_listener(self.abstract_obj, 'shell') for child in self.children: child.set_listeners() def _set_bg_color(self, new): """ Property setter for the 'bg_color' background color property. Set values are pushed to the '_user_bg_color' trait. """ self._user_bg_color = new def _get_bg_color(self): """ Property sgtter for the 'bg_color' background color property. We use the '_user_bg_color' trait unless it is None. """ if self._user_bg_color: return self._user_bg_color return self._style_bg_color def _set_fg_color(self, new): """ Property setter for the 'fg_color' foreground color property. Set values are pushed to the '_user_fg_color' trait. """ self._user_fg_color = new def _get_fg_color(self): """ Property sgtter for the 'fg_color' foreground color property. We use the '_user_fg_color' trait unless it is None. """ if self._user_fg_color: return self._user_fg_color return self._style_fg_color def _set_font(self, new): """ Property setter for the 'fg_color' foreground color property. Set values are pushed to the '_user_fg_color' trait. """ self._user_font = new def _get_font(self): """ Property sgtter for the 'fg_color' foreground color property. We use the '_user_fg_color' trait unless it is None. """ if self._user_font: return self._user_font return self._style_font
class Constructor(HasStrictTraits): """ The constructor class to use to populate the toolkit. """ #: A callable object which returns the shell class to use #: for the widget. shell_loader = Callable #: A callable object which returns the abstract implementation class #: to use for the widget. abstract_loader = Callable #: The key with which this constructor was added to the toolkit. #: It is set by the toolkit and used as the type name of the #: instantiated component. style_type = Str #: A reference (stored weakly) to the toolkit in which this #: constructor is contained. It is used to set the toolkit #: attribute on the components as they are created. toolkit = WeakRef('Toolkit') def __init__(self, shell_loader, abstract_loader): """ Initialize a constructor instance. Parameters ---------- shell_loader : Callable A callable object which returns the shell class to use for the widget. abstract_loader : Callable A callable object which returns the abstract implementation class to use for the widget. """ super(Constructor, self).__init__() self.shell_loader = shell_loader self.abstract_loader = abstract_loader def __call__(self, *args, **kwargs): """ Calls the loaders and assembles the component. Subclasses should override this method to implement custom construction behavior if the default is not sufficient. """ shell_cls = self.shell_loader() abstract_cls = self.abstract_loader() component = shell_cls(style_type=self.style_type, toolkit=self.toolkit, abstract_obj=abstract_cls()) return component def clone(self, shell_loader=None, abstract_loader=None): """ Creates a clone of this constructor, optionally changing out one or both of the loaders. """ if shell_loader is None: shell_loader = self.shell_loader if abstract_loader is None: abstract_loader = self.abstract_loader return Constructor(shell_loader, abstract_loader)
class CellGridSlice(HasTraits): '''General implementation of a slice within the FEGrid ''' cell_grid = WeakRef() # 'ibvpy.mesh.cell_grid.cell_grid.CellGrid' ) grid_slice = Any idx_tuple = Property(Tuple, depends_on='grid_slice,cell_grid.+changed_structure') @cached_property def _get_idx_tuple(self): ''' Classify the index specification By default, the idx is assigned to element index to handle the case of a single integer index ''' idx1 = self.grid_slice # The default for node index is the universal slice idx2 = slice(None, None) n_dims = self.cell_grid.n_dims # If the index is an iterable if hasattr(self.grid_slice, '__iter__'): # Get the first n_dims of indexes for the cell grid idx1 = self.grid_slice[:n_dims] # If there are more indexes than n_dims save them for # use within the cell to identify the nodes. if len(self.grid_slice) > n_dims: idx2 = self.grid_slice[n_dims:] return (idx1, idx2) elem_grid = Property def _get_elem_grid(self): idx1, _ = self.idx_tuple return self.cell_grid.cell_idx_grid[idx1] elems = Property def _get_elems(self): # get the cells affected by the slice @todo - rename cell_grid # attribute within CellGrid return self.elem_grid.flatten() # Get the node map associated with the sliced elements # nodes = Property def _get_nodes(self): # get the node map associated with the sliced elements idx1, idx2 = self.idx_tuple sliced_cell_node_map = self.cell_grid.cell_node_map[self.elems] # get the sliced nodes for the sliced cells return sliced_cell_node_map[:, self.cell_grid.grid_cell[idx2]] points = Property def _get_points(self): # get the coordinates for the sliced coords return self.cell_grid.point_X_arr[self.nodes] # Global coordinates of nodes involved in the slice # Structured element by element # point_X_arr = Property def _get_point_X_arr(self): return self.cell_grid.point_X_arr[self.nodes] # Parametric coordinates of nodes involved in the slice # Structured element by element # point_x_arr = Property def _get_point_x_arr(self): return self.cell_grid.point_x_arr[self.nodes]
class CamMove(HasStrictTraits): '''Camera transitions. Attach functional mapping depending on time variable for azimuth, elevation, distance, focal point and roll angle. ''' def __init__(self, *args, **kw): super(CamMove, self).__init__(*args, **kw) fta = WeakRef ftv = WeakRef from_station = WeakRef(CamStation) to_station = WeakRef(CamStation) changed = Event cam_attributes = [ 'azimuth', 'elevation', 'distance', 'fpoint', 'roll'] azimuth_move = Trait('linear', {'linear': linear_cam_move, 'damped': damped_cam_move}) elevation_move = Trait('linear', {'linear': linear_cam_move, 'damped': damped_cam_move}) distance_move = Trait('linear', {'linear': linear_cam_move, 'damped': damped_cam_move}) fpoint_move = Trait('linear', {'linear': linear_cam_move, 'damped': damped_cam_move}) roll_move = Trait('linear', {'linear': linear_cam_move, 'damped': damped_cam_move}) duration = Float(10, label='Duration') bts = Property(label='Start time') def _get_bts(self): return self.from_station.time_stemp ets = Property(label='End time') def _get_ets(self): return self.from_station.time_stemp + self.duration n_t = Int(10, input=True) cmt = Property(Array('float_'), depends_on='n_t') '''Relative camera move time (CMT) running from zero to one. ''' @cached_property def _get_cmt(self): return np.linspace(0, 1, self.n_t) viz_t_move = Property(Array('float_')) '''Time line range during the camera move ''' def _get_viz_t_move(self): return np.linspace(self.bts, self.ets, self.n_t) vot_start = Float(0.0, auto_set=False, enter_set=True, input=True) vot_end = Float(1.0, auto_set=False, enter_set=True, input=True) vot = Property(Array('float_'), depends_on='n_t,vot_start,vot_end') '''Visualization object time (VOT). By default it is the same as the camera time. It can be mapped to a different time profile using viz_time_fn ''' @cached_property def _get_vot(self): return np.linspace(self.vot_start, self.vot_end, self.n_t) def _get_vis3d_center_t(self): '''Get the center of the object''' return self.ftv.get_center_t def _get_vis3d_bounding_box_t(self): '''Get the bounding box of the object''' return self.vis.get_center(self.t_range) transition_arr = Property( Array(dtype='float_'), depends_on='changed,+input') '''Array with azimuth values along the transition ''' @cached_property def _get_transition_arr(self): trans_arr = [getattr(self, attr + '_move_')( getattr(self.from_station, attr), getattr(self.to_station, attr), self.n_t) for attr in self.cam_attributes ] trans_arr.append(self.vot) trans_arr.append(self.viz_t_move) return trans_arr def reset_cam(self, m, a, e, d, f, r): m.view(azimuth=a, elevation=e, distance=d, focalpoint=f) m.roll(r) def take(self, ftv): for a, e, d, f, r, vot, viz_t in zip(*self.transition_arr): ftv.update(vot, viz_t, force=True) self.reset_cam(ftv.mlab, a, float(e), d, f, r) sleep(self.fta.anim_delay) def render_take(self, ftv, fname_base, format_, idx_offset, figsize_factor): im_files = [] for idx, (a, e, d, f, r, vot, viz_t) \ in enumerate(zip(*self.transition_arr)): ftv.update(vot, viz_t, force=True) # @todo: temporary focal point determination - make it optional self.reset_cam(ftv.mlab, a, e, d, f, r) fname = '%s%03d.%s' % (fname_base, idx + idx_offset, format_) ftv.mlab.savefig(fname, size=( figsize_factor * 800, figsize_factor * 500)) # im_files.append(fname) return im_files view = View(VGroup(HGroup(InstanceUItem('from_station@', resizable=True), VGroup(Item('azimuth_move'), Item('elevation_move'), Item('distance_move'), Item('roll_move'), Item('fpoint_move'), Item('n_t'), Item('duration'), ), InstanceUItem('to_station@', resizable=True), ), VGroup(HGroup(UItem('vot_start'), UItem('vot_end'), springy=True ), label='object time range' ), ), buttons=['OK', 'Cancel'])
class BaseComponent(HasStrictTraits): """ The most base class of the Enaml component hierarchy. All declarative Enaml classes should inherit from this class. This class is not meant to be instantiated directly. """ #: A readonly property which returns the current instance of #: the component. This allows declarative Enaml components to #: access self according to the standard attribute scoping rules. self = Property #: The parent component of this component. It is stored as a weakref #: to mitigate issues with reference cycles. A top-level component's #: parent is None. parent = WeakRef('BaseComponent') #: The list of children for this component. This is a read-only #: lazy property that is computed based on the static list of #: _subcomponents and the items they return by calling their #: 'get_actual' method. This list should not be manipulated by #: user code. children = LazyProperty( List(Instance('BaseComponent')), depends_on='_subcomponents:_actual_updated', ) #: Whether the component has been initialized or not. This will be #: set to True after all of the setup() steps defined here are #: completed. It should not be changed afterwards. This can be used #: to trigger certain actions that need to occur after the component #: has been set up. initialized = Bool(False) #: An optional name to give to this component to assist in finding #: it in the tree. See the 'find_by_name' method. name = Str #: A reference to the toolkit that was used to create this object. toolkit = Instance(Toolkit) #: The private dictionary of expression objects that are bound to #: attributes on this component. It should not be manipulated by #: user code. Rather, expressions should be bound by calling the #: 'bind_expression' method. _expressions = Dict(Str, List(Instance(AbstractExpression))) #: The private list of virtual base classes that were used to #: instantiate this component from Enaml source code. The #: EnamlFactory class of the Enaml runtime will directly append #: to this list as necessary. _bases = List #: The private internal list of subcomponents for this component. #: This list should not be manipulated by the user, and should not #: be changed after initialization. It can, however, be redefined #: by subclasses to limit the type or number of subcomponents. _subcomponents = List(Instance('BaseComponent')) #: A private event that should be emitted by a component when the #: results of calling get_actual() will result in new values. #: This event is listened to by the parent of subcomponents in order #: to know when to rebuild its list of children. User code will not #: typically interact with this event. _actual_updated = EnamlEvent #: The HasTraits class defines a class attribute 'set' which is #: a deprecated alias for the 'trait_set' method. The problem #: is that having that as an attribute interferes with the #: ability of Enaml expressions to resolve the builtin 'set', #: since the dynamic attribute scoping takes precedence over #: builtins. This resets those ill-effects. set = Disallow #-------------------------------------------------------------------------- # Special Methods #-------------------------------------------------------------------------- def __repr__(self): """ An overridden repr which returns the repr of the factory from which this component is derived, provided that it is not simply a root constructor. Otherwise, it defaults to the super class' repr implementation. """ # If there are any bases, the last one in the list will always # be a constructor. We want to ignore that one and focus on the # repr of the virtual base class from which the component was # derived in the Enaml source code. bases = self._bases if len(bases) >= 2: base = bases[0] return repr(base) return super(BaseComponent, self).__repr__() #-------------------------------------------------------------------------- # Property Getters #-------------------------------------------------------------------------- def _get_self(self): """ The property getter for the 'self' attribute. """ return self def _get_children(self): """ The lazy property getter for the 'children' attribute. This property getter returns the flattened list of components returned by calling 'get_actual()' on each subcomponent. """ return sum([c.get_actual() for c in self._subcomponents], []) #-------------------------------------------------------------------------- # Component Manipulation #-------------------------------------------------------------------------- def get_actual(self): """ Returns the list of BaseComponent instances which should be included as proper children of our parent. By default this simply returns [self]. This method should be reimplemented by subclasses which need to contribute different components to their parent's children. """ return [self] def add_subcomponent(self, component): """ Adds the given component as a subcomponent of this object. By default, the subcomponent is added to an internal list of subcomponents. This method may be overridden by subclasses to filter or otherwise handle certain subcomponents differently. """ component.parent = self self._subcomponents.append(component) #-------------------------------------------------------------------------- # Setup Methods #-------------------------------------------------------------------------- def setup(self, parent=None): """ Run the setup process for the ui tree. The setup process is fairly complex and involves multiple steps. The complexity is required in order to ensure a consistent state of the component tree so that default values that are computed from expressions have the necessary information available. The setup process is comprised of the following steps: 1) Abstract objects create their internal toolkit object 2) Abstract objects initialize their internal toolkit object 3) Bound expression values are explicitly applied 4) Abstract objects bind their event handlers 5) Abstract objects are added as listeners to the shell object 6) Visibility is initialized 7) Layout is initialized 8) A finalization pass is made 9) Nodes are marked as initialized Many of these setup methods are no-ops, but are defined on this BaseComponent for simplicity and continuity. Subclasses that need to partake in certain portions of the layout process should re-implement the appropriate setup methods. Parameters ---------- parent : native toolkit widget, optional If embedding this BaseComponent into a non-Enaml GUI, use this to pass the appropriate toolkit widget that should be the parent toolkit widget for this component. """ self._setup_create_widgets(parent) self._setup_init_widgets() self._setup_eval_expressions() self._setup_bind_widgets() self._setup_listeners() self._setup_init_visibility() self._setup_init_layout() self._setup_finalize() self._setup_set_initialized() def _setup_create_widgets(self, parent): """ A setup method that, by default, is a no-op. Subclasses that drive gui toolkit widgets should reimplement this method to create the underlying toolkit widget(s). """ for child in self._subcomponents: child._setup_create_widgets(parent) def _setup_init_widgets(self): """ A setup method that, by default, is a no-op. Subclasses that drive gui toolkit widgets should reimplement this method to initialize their internal toolkit widget(s). """ for child in self._subcomponents: child._setup_init_widgets() def _setup_eval_expressions(self): """ A setup method that loops over all of bound expressions and performs a getattr for those attributes. This ensures that all bound attributes are initialized, even if they weren't implicitly initialized in any of the previous setup methods. """ for name in self._expressions: getattr(self, name) for child in self._subcomponents: child._setup_eval_expressions() def _setup_bind_widgets(self): """ A setup method that, by default, is a no-op. Subclasses that drive gui toolkit widgets should reimplement this method to bind any event handlers of their internal toolkit widget(s). """ for child in self._subcomponents: child._setup_bind_widgets() def _setup_listeners(self): """ A setup method that, by default, is a no-op. Subclasses that drive gui toolkit widgets should reimplement this method to setup an traits listeners necessary to drive their internal toolkit widget(s). """ for child in self._subcomponents: child._setup_listeners() def _setup_init_visibility(self): """ A setup method that, by default, is a no-op. Subclasses that drive gui toolkit widgets should reimplement this method to initialize the visibility of their widgets. """ for child in self._subcomponents: child._setup_init_visibility() def _setup_init_layout(self): """ A setup method that, by default, is a no-op. Subclasses that manage layout should reimplement this method to initialize their underlying layout. """ for child in self._subcomponents: child._setup_init_layout() def _setup_finalize(self): """ A setup method that, by default, is a no-op. Subclasses that need to perform process after layout is initialized but before a node is marked as fully initialized should reimplement this method. """ for child in self._subcomponents: child._setup_finalize() def _setup_set_initialized(self): """ A setup method which updates the initialized attribute of the component to True. This is performed bottom-up. """ for child in self._subcomponents: child._setup_set_initialized() self.initialized = True #-------------------------------------------------------------------------- # Teardown Methods #-------------------------------------------------------------------------- def destroy(self): """ Destroys the component by clearing the list of subcomponents and calling 'destroy' on all of the old subcomponents, then gets rid of all references to the subcomponents and bound expressions. Subclasses that need more control over destruction should reimplement this method. """ for child in self._subcomponents: child.destroy() del self._subcomponents[:] self._expressions.clear() #-------------------------------------------------------------------------- # Layout Stubs #-------------------------------------------------------------------------- def relayout(self): """ A method called when the layout of the component's children should be refreshed. By default, this method proxies the call up the hierarchy until an implementor is found. Any implementors should ensure that the necessary operations take place immediately and are complete before the method returns. """ parent = self.parent if parent is not None: parent.relayout() def request_relayout(self): """ A method called when the layout of the component's children should be refreshed at some point in the future. By default, this method proxies the call up the hierarchy until an implementor is found. Any implementors should ensure that this method returns immediately, and that relayout occurs at some point in the future. """ parent = self.parent if parent is not None: parent.request_relayout() def refresh(self): """ A method called when the positioning of the component's children should be refreshed. By default, this method proxies the call up the hierarchy until an implementor is found. Implementors should ensure that this method takes place immediately, and that the refresh is complete before the method returns. Note: This method should perform less work than 'relayout' and should typically only need to be called when the children need to be repositioned, rather than have all of their layout relationships recomputed. """ parent = self.parent if parent is not None: parent.refresh() def request_refresh(self): """ A method called when the positioning of the component's children should be refreshed at some point in the future. By default, this method proxies the call up the hierarchy until an implementor is found. Implementors should ensure that this method returns immediately, and that the refresh is completed at some time in the future. Note: This method should perform less work than 'relayout' and should typically only need to be called when the children need to be repositioned, rather than have all of their layout relationships recomputed. """ parent = self.parent if parent is not None: parent.request_refresh() def request_relayout_task(self, callback, *args, **kwargs): """ Schedule a callback to be executed, followed by a relayout. By default, this method proxies the call up the hierarchy until an implementor is found. Implementors should ensure that the callback is executed with given arguments at some point in the future and is followed by a relayout. It is suggested that implementors collapse multiple calls to this method which results in a single relayout. """ parent = self.parent if parent is not None: parent.request_relayout_task(callback, *args, **kwargs) def request_refresh_task(self, callback, *args, **kwargs): """ Schedule a callback to be executed, followed by a rerfresh. By default, this method proxies the call up the hierarchy until an implementor is found. Implementors should ensure that the callback is executed with given arguments at some point in the future and is followed by a relayout. It is suggested that implementors collapse multiple calls to this method which results in a single refresh. """ parent = self.parent if parent is not None: parent.request_refresh_task(callback, *args, **kwargs) #-------------------------------------------------------------------------- # Bound Attribute Handling #-------------------------------------------------------------------------- def add_attribute(self, name, attr_type=object, is_event=False): """ Adds an attribute to the base component with the given name and ensures that values assigned to this attribute are of a given type. If the object already has an attribute with the given name, an exception will be raised. Parameters ---------- name : string The name of the attribute to add. attr_type : type-like object, optional An object that behaves like a type for the purposes of a call to isinstance. Defaults to object. is_event : bool, optional If True, the added attribute will behave like an event. Otherwise, it will behave like a normal attribute. The default is False. """ # Check to see if a trait is already defined. We don't use # hasattr here since that might prematurely trigger a trait # intialization. We allow overriding traits of type Disallow, # UserAttribute, and UserEvent. The first is a consequence of # using HasStrictTraits, where non-existing attributes are # manifested as a Disallow trait. The others allow a custom # derived component to specialize the attribute and event types # of the component from which it is deriving. curr = self.trait(name) if curr is not None: ttype = curr.trait_type allowed = (UserAttribute, UserEvent) if ttype is not Disallow and not isinstance(ttype, allowed): msg = ("Cannot add '%s' attribute. The '%s' attribute on " "the %s object already exists.") raise TypeError(msg % (name, name, self)) # At this point we know there are no non-overridable traits # defined for the object, but it is possible that there are # methods or other non-trait attributes using the given name. # We could potentially check for those, but its probably more # useful to allow for overriding such things from Enaml, so we # just go ahead and add the attribute. try: if is_event: self.add_trait(name, UserEvent(attr_type)) else: self.add_trait(name, UserAttribute(attr_type)) except TypeError: msg = ("'%s' is not a valid type for the '%s' attribute " "declaration on %s") raise TypeError(msg % (attr_type, name, self)) def bind_expression(self, name, expression, notify_only=False): """ Binds the given expression to the attribute 'name'. If the attribute does not exist, an exception is raised. A strong reference to the expression object is kept internally. If the expression is not notify_only and the object is already fully initialized, the value of the expression will be applied immediately. Parameters ---------- name : string The name of the attribute on which to bind the expression. expression : AbstractExpression A concrete implementation of AbstractExpression. notify_only : bool, optional If True, the expression is only a notifier, in which case multiple binding is allowed, otherwise the new expression overrides any old non-notify expression. Defaults to False. """ curr = self.trait(name) if curr is None or curr.trait_type is Disallow: msg = "Cannot bind expression. %s object has no attribute '%s'" raise AttributeError(msg % (self, name)) # If this is the first time an expression is being bound to the # given attribute, then we hook up a change handler. This ensures # that we only get one notification event per bound attribute. # We also create the notification entry in the dict, which is # a list with at least one item. The first item will always be # the left associative expression (or None) and all following # items will be the notify_only expressions. expressions = self._expressions if name not in expressions: self.on_trait_change(self._on_bound_attr_changed, name) expressions[name] = [None] # There can be multiple notify_only expressions bound to a # single attribute, so they just get appended to the end of # the list. Otherwise, the left associative expression gets # placed at the zero position of the list, overriding any # existing expression. if notify_only: expressions[name].append(expression) else: handler = self._on_expression_changed old = expressions[name][0] if old is not None: old.expression_changed.disconnect(handler) expression.expression_changed.connect(handler) expressions[name][0] = expression # Hookup support for default value computation. if not self.initialized: # We only need to add an ExpressionTrait once, since it # will reach back into the _expressions dict as needed # and retrieve the bound expression. if not isinstance(curr.trait_type, ExpressionTrait): self.add_trait(name, ExpressionTrait(curr)) else: # If the component is already initialized, and the given # expression supports evaluation, update the attribute # with the current value. val = expression.eval() if val is not NotImplemented: setattr(self, name, val) def _on_expression_changed(self, expression, name, value): """ A private signal callback for the expression_changed signal of the bound expressions. It updates the value of the attribute with the new value from the expression. """ setattr(self, name, value) def _on_bound_attr_changed(self, obj, name, old, new): """ A private handler which is called when any attribute which has a bound signal changes. It calls the notify method on each of the expressions bound to that attribute, but only after the component has been fully initialized. """ # The check for None is for the case where there are no left # associative expressions bound to the attribute, so the first # entry in the list is still None. if self.initialized: for expr in self._expressions[name]: if expr is not None: expr.notify(old, new) #-------------------------------------------------------------------------- # Auxiliary Methods #-------------------------------------------------------------------------- def when(self, switch): """ A method which returns itself or None based on the truth of the argument. This can be useful to easily turn off the effects of a component if various situations such as constraints-based layout. Parameters ---------- switch : bool A boolean which indicates whether the instance or None should be returned. Returns ------- result : self or None If 'switch' is boolean True, self is returned. Otherwise, None is returned. """ if switch: return self def traverse(self, depth_first=False): """ Yields all of the nodes in the tree, from this node downward. Parameters ---------- depth_first : bool, optional If True, yield the nodes in depth first order. If False, yield the nodes in breadth first order. Defaults to False. """ if depth_first: stack = [self] stack_pop = stack.pop stack_extend = stack.extend else: stack = deque([self]) stack_pop = stack.popleft stack_extend = stack.extend while stack: item = stack_pop() yield item stack_extend(item.children) def traverse_ancestors(self, root=None): """ Yields all of the nodes in the tree, from the parent of this node updward, stopping at the given root. Parameters ---------- root : BaseComponent, optional The component at which to stop the traversal. Defaults to None """ parent = self.parent while parent is not root and parent is not None: yield parent parent = parent.parent def find_by_name(self, name): """ Locate and return a named item that exists in the subtree which starts at this node. This method will traverse the tree of components, breadth first, from this point downward, looking for a component with the given name. The first one with the given name is returned, or None if no component is found. Parameters ---------- name : string The name of the component for which to search. Returns ------- result : BaseComponent or None The first component found with the given name, or None if no component is found. """ for cmpnt in self.traverse(): if cmpnt.name == name: return cmpnt def toplevel_component(self): """ Walks up the tree of components starting at this node and returns the toplevel node, which is the first node encountered without a parent. """ cmpnt = self while cmpnt is not None: res = cmpnt cmpnt = cmpnt.parent return res
class Engine(HasStrictTraits): """ The Mayavi engine base class. """ # The version of this class. Used for persistence. __version__ = 0 # The scenes associated with this project. scenes = List(Scene, record=True) # The list to provide to a TreeEditor. Always add on a AdderNode. # TODO: It makes more sense to put the modification of the list # in some other UI module, and not here. children_ui_list = Property(record=False) # Our name. name = Str('Mayavi Engine') # Current scene. current_scene = Property(Instance(Scene), record=False) # Current object. current_object = Property(record=False) # Current selection -- the currently selected object on the tree. current_selection = Property(record=False) # Has the Engine started? Use this event to do something after # the engine has been started. started = Event(record=False) # An optional callable that will generate a usable new viewer # containing a `tvtk.pyface.TVTKScene` instance. Ideally # the viewer should have an interface like # `tvtk.pyface.TVTKWindow` -- basically it must # implement the `closing` and `activated` events, however, this is # not necessary. The created viewer is used by the `new_scene` # method to create a new Viewer. This is a mechanism to use a # user specified scene with the Engine and have the ability to # load saved visualizations using the new scene. Handy for things # like off-screen rendering. scene_factory = Callable(viewer_factory) # Are we running? running = Bool(False, record=False) # This event is invoked when the engine has been stopped. closed = Event() # The recorder for script recording. recorder = Instance(Recorder, record=False) ######################################## # Private traits. _current_scene = WeakRef(Scene, allow_none=True) _current_object = WeakRef(HasTraits, allow_none=True) _current_selection = WeakRef(HasTraits, allow_none=True) _viewer_ref = Dict # View related traits. current_selection_view = View(Item(name='_current_selection', enabled_when='_current_selection is not None', style='custom', springy=True, show_label=False,), resizable=True, scrollable=True ) ###################################################################### # `object` interface ###################################################################### def __init__(self, **traits): super(Engine, self).__init__(**traits) # FIXME: This is tied to preferences. It really should not be # we need to use bind_preferences here. # To remove ref cycle with root preferences helper, the trait change # handler is an instance method preference_manager.root.on_trait_change(self._show_helper_nodes_changed, 'show_helper_nodes') def __get_pure_state__(self): d = self.__dict__.copy() for x in ['_current_scene', '_current_object', '__sync_trait__', '_viewer_ref', '__traits_listener__']: d.pop(x, None) return d def __set_pure_state__(self, state): # Current number of scenes. n_scene = len(self.scenes) # Number of scenes in saved state. n_saved_scene = len(state.scenes) # Remove extra ones. for i in range(n_scene - n_saved_scene): self.close_scene(self.scenes[-1]) # Add new ones. for i in range(n_saved_scene - n_scene): self.new_scene() # Set the state. state_pickler.set_state(self, state) def __getstate__(self): return state_pickler.dumps(self) def __setstate__(self, str_state): self.__init__() state = state_pickler.loads_state(str_state) state_pickler.update_state(state) self.__set_pure_state__(state) ###################################################################### # `Engine` interface ###################################################################### def start(self): """This is called by the plugin when the plugin actually starts.""" registry.register_engine(self) # Notify any listeners that the engine is started. self.started = self self.running = True def stop(self): registry.unregister_engine(self) self.running = False self.closed = True @recordable def add_source(self, src, scene=None): """Adds a source to the pipeline. Uses the current scene unless a scene is given in the scene keyword argument.""" passed_scene = scene if scene is not None: tvtk_scene = scene.scene for sc in self.scenes: if sc.scene == tvtk_scene: scene = sc break else: error('This scene is not managed by mayavi') return else: scene = self.current_scene # Create a new scene if none is available. if scene is None: self.new_scene() scene = self.current_scene scene.add_child(src) self.current_object = src @recordable def add_filter(self, fil, obj=None): """Adds a filter to the pipeline at an appropriate point. Adds it to the selected object, or to an object passed as the kwarg `obj`. """ passed_obj = obj if obj is None: obj = self.current_object if not isinstance(obj, Base): msg = 'No valid current object, '\ 'please select an active object.' error(msg) return if (obj is not None) and (not isinstance(obj, Scene)): if obj.running: obj.add_child(fil) self.current_object = fil else: msg = 'Current object is not active, '\ 'please select an active object.' error(msg) else: if obj is None: error('Please create a VTK scene and open some data first.') else: error('No data: cannot use a Filter/Module/ModuleManager.') @recordable def add_module(self, mod, obj=None): """Adds a module to the pipeline at an appropriate point. Adds it to the selected object, or to an object passed through the kwarg `obj`. """ self.add_filter(mod, obj=obj) @recordable def save_visualization(self, file_or_fname): """Given a file or a file name, this saves the current visualization to the file. """ # Save the state of VTK's global warning display. o = vtk.vtkObject w = o.GetGlobalWarningDisplay() o.SetGlobalWarningDisplay(0) # Turn it off. try: #FIXME: This is for streamline seed point widget position which #does not get serialized correctly if is_old_pipeline(): state_pickler.dump(self, file_or_fname) else: state = state_pickler.get_state(self) st = state.scenes[0].children[0].children[0].children[4] l_pos = st.seed.widget.position st.seed.widget.position = [pos.item() for pos in l_pos] saved_state = state_pickler.dumps(state) file_or_fname.write(saved_state) except (IndexError, AttributeError): state_pickler.dump(self, file_or_fname) finally: # Reset the warning state. o.SetGlobalWarningDisplay(w) @recordable def load_visualization(self, file_or_fname): """Given a file/file name this loads the visualization.""" # Save the state of VTK's global warning display. o = vtk.vtkObject w = o.GetGlobalWarningDisplay() o.SetGlobalWarningDisplay(0) # Turn it off. try: # Get the state from the file. state = state_pickler.load_state(file_or_fname) state_pickler.update_state(state) # Add the new scenes. for scene_state in state.scenes: self.new_scene() scene = self.scenes[-1] # Disable rendering initially. if scene.scene is not None: scene.scene.disable_render = True # Update the state. state_pickler.update_state(scene_state) scene.__set_pure_state__(scene_state) # Setting the state will automatically reset the # disable_render. scene.render() finally: # Reset the warning state. o.SetGlobalWarningDisplay(w) @recordable def open(self, filename, scene=None): """Open a file given a filename if possible in either the current scene or the passed `scene`. """ passed_scene = scene reader = registry.get_file_reader(filename) if reader is None: msg = 'No suitable reader found for the file %s'%filename error(msg) else: src = None if scene is None: scene = self.current_scene if scene is None: scene = self.new_scene() try: sc = scene.scene if sc is not None: sc.busy = True callable = reader.get_callable() if reader.factory is None: src = callable() src.initialize(filename) else: # Factory functions are passed the filename and a # reference to the engine. src = callable(filename, self) if src is not None: self.add_source(src, passed_scene) finally: if sc is not None: sc.busy = False if src is not None: return src def record(self, msg): """This is merely a convenience method to record messages to the script recorder. """ r = self.recorder if r is not None: r.record(msg) ###################################################################### # Scene creation/deletion related methods. ###################################################################### def add_scene(self, scene, name=None): """Add given `scene` (a `pyface.tvtk.scene.Scene` instance) to the mayavi engine so that mayavi can manage the scene. This is used when the user creates a scene. Note that for the `EnvisageEngine` this is automatically taken care of when you create a new scene using the TVTK scene plugin. Parameters: ----------- scene - `pyface.tvtk.scene.Scene` The scene that needs to be managed from mayavi. name - `str` The name assigned to the scene. It tries to determine the name of the scene from the passed scene instance. If this is not possible it defaults to 'Mayavi Scene'. """ if name is None: if hasattr(scene, 'name'): name = scene.name else: name = 'Mayavi Scene %d'%next(scene_id_generator) s = Scene(scene=scene, name=name, parent=self) s.start() # We don't want the startup setup to be recorded. recorder = self.recorder self.scenes.append(s) self.current_scene = s if recorder is not None: recorder.register(s) @recordable def remove_scene(self, scene, **kwargs): """Remove a given `scene` (a `pyface.tvtk.scene.Scene` instance) from the mayavi engine if it is already being managed by mayavi. Note that for the `EnvisageEngine` this is automatically taken care of when you close a scene started using the TVTK scene plugin. Parameters: ----------- scene - `pyface.tvtk.scene.Scene` The scene that needs to be removed from mayavi. """ s = None for index, x in enumerate(self.scenes): if x.scene is scene: s = x break if s is not None: s.stop() self.scenes.remove(s) # Don't record it shutting down. To do this we must # unregister it here so we don't record unnecessary calls. recorder = self.recorder if recorder is not None: recorder.unregister(s) # Remove the reference to the viewer if any. if scene in self._viewer_ref: del self._viewer_ref[scene] # Clear the current scene if it has been removed. if scene is self._current_scene: self._current_scene = None @recordable def new_scene(self, viewer=None, name=None, **kwargs): """Create or manage a new VTK scene window. If no `viewer` argument is provided, the method creates a new viewer using `self.scene_factory`. If `self.scene_factory` is `None` then it creates an `ivtk` viewer. This code requires that the `viewer` has a `scene` attribute/trait that is a `pyface.tvtk.scene.Scene`. It also works best if the viewer supports `closing` and `activated` events. The method returns the created viewer. Parameters: ----------- viewer - The viewer object, if None, one is created for you. name - The name attribute of the viewer ``**kwargs`` - The extra keyword arguments are passed along to the scene factory. """ if viewer is None: factory_kwargs = {} factory_kwargs_names = get_args(self.scene_factory) for arg, value in kwargs.items(): if arg in factory_kwargs_names: factory_kwargs[arg] = value viewer = self.scene_factory(**factory_kwargs) process_ui_events() if name is not None: viewer.name = name # Hang on to a reference to this viewer, if not done this will cause a # crash with Qt4. This because the viewer will be closed and gc'd if # there isn't a reference to it. When the viewer is gc'd the scene is # also closed and the engine will have a dead scene causing a crash. self._viewer_ref[viewer.scene] = viewer self.add_scene(viewer.scene) if hasattr(viewer, 'on_trait_change'): viewer.on_trait_change(self._on_scene_closed, 'closing') viewer.on_trait_change(self._on_scene_activated, 'activated') if hasattr(viewer, 'title'): self.current_scene.sync_trait('name', viewer, 'title') return viewer @recordable def close_scene(self, scene): """Given a scene created from new_scene, this method closes it and removes the scene from the list of scenes we manage. Parameters: ----------- scene - `pyface.tvtk.scene.Scene` or an object that holds a reference to a `pyface.tvtk.scene.Scene` in a `scene` attribute. """ viewer = self.get_viewer(scene) self.remove_scene(scene.scene) if hasattr(scene, 'close'): scene.close() elif scene.scene is not None: scene.scene.close() if viewer is not None and hasattr(viewer, 'close'): viewer.close() def get_viewer(self, scene): """Return the viewer associated with a given scene. Parameters: ----------- scene - An `mayavi.core.scene.Scene` instance. """ return self._viewer_ref.get(scene.scene) def dialog_view(self): """ Default dialog view for Engine objects. """ return None ###################################################################### # Non-public interface ###################################################################### def _on_select(self, object): """Called by the EngineTree when an object on the view is selected. This basically sets the current object and current scene.""" self.current_selection = object self._current_object = object try: scene = object.scene for s in self.scenes: if s.scene == scene: self._current_scene = s break except AttributeError: pass def _get_current_scene(self): n_scene = len(self.scenes) if n_scene == 0: return None elif n_scene == 1: return self.scenes[0] elif self._current_scene is not None: return self._current_scene elif n_scene > 1: return self.scenes[-1] else: return None def _set_current_scene(self, scene): old = self._current_scene self._current_scene = scene self.trait_property_changed('current_scene', old, scene) def _get_current_object(self): if self._current_object is not None: return self._current_object elif self.current_scene is not None: return self.current_scene else: return None def _set_current_object(self, object): old = self._current_object self._current_object = object self.trait_property_changed('current_object', old, object) def _get_current_selection(self): return self._current_selection def _set_current_selection(self, object): old = self._current_selection if not isinstance(object, (Base, AdderNode)): object = None self._current_selection = object self.trait_property_changed('current_selection', old, object) def _on_scene_closed(self, obj, name, old, new): self.remove_scene(obj.scene) def _on_scene_activated(self, obj, name, old, new): for scene in self.scenes: if scene.scene is obj.scene: self.current_scene = scene break def _closed_fired(self): """ When the engine is closed, clear the viewer ref which otherwise stores references to scenes to prevent crash on QT4. See: self.new_scene and MlabSceneModel._closed_fired """ self._viewer_ref.clear() self.scenes = [] preference_manager.root.on_trait_change(self._show_helper_nodes_changed, 'show_helper_nodes', remove=True) registry.unregister_engine(self) def _show_helper_nodes_changed(self): self.trait_property_changed('children_ui_list', [], self.children_ui_list) def _get_children_ui_list(self): """ Trait getter for children_ui_list Property. """ if preference_manager.root.show_helper_nodes \ and len(self.scenes) == 0: return [SceneAdderNode(object=self)] else: return self.scenes @on_trait_change('scenes[]') def _trigger_children_ui_list(self, old, new): """ Trigger a children_ui_list change when scenes changed. """ self.trait_property_changed('children_ui_list', old, new) def _recorder_changed(self, old, new): if new is not None: new.record('# Recorded script from Mayavi2') new.record('from numpy import array') new.record('try:') new.record(' engine = mayavi.engine') new.record('except NameError:') new.record(' from mayavi.api import Engine') new.record(' engine = Engine()') new.record(' engine.start()') new.record('if len(engine.scenes) == 0:') new.record(' engine.new_scene()') new.record('# ------------------------------------------- ') elif old is not None: old.record('# ------------------------------------------- ') old.record('from mayavi.tools.show import show') old.record('show()')
class Base(TreeNodeObject): # The version of this class. Used for persistence. __version__ = 0 ######################################## # Traits # The scene (RenderWindow) associated with this component. scene = Instance(TVTKScene, record=False) # Is this object running as part of the mayavi pipeline. running = Property(Bool, record=False) # The object's name. name = Str('') # The default icon. icon = 'module.ico' # The human readable type for this object type = Str('', record=False) # Is this object visible or not. visible = Bool(True, desc='if the object is visible') # Extend the children list with an AdderNode when a TreeEditor needs it. children_ui_list = Property(depends_on=['children'], record=False) # The parent of this object, i.e. self is an element of the parents # children. If there is no notion of a parent/child relationship # this trait is None. parent = WeakRef(record=False) # A helper for the right click menus, context sensitivity etc. menu_helper = Instance(HasTraits, record=False) # Our recorder. recorder = Instance(Recorder, record=False) ################################################## # Private traits _is_running = Bool(False) # This is used to save the state of the object when it is not # running. When the object "starts", the state is loaded. This # is done because a stopped object will not have a meaningful VTK # pipeline setup, so setting its state will lead to all kinds of # errors. _saved_state = Any('') # Hide and show actions _HideShowAction = Instance( Action, kw={ 'name': 'Hide/Show', 'action': 'object._hideshow' }, ) # The menu shown on right-click for this. _menu = Instance(Menu, transient=True) # Path to the icon for this object. _icon_path = Str() # Adder node: a dialog to add children to this object _adder_node_class = None # Name of the file that may host the hand-crafted view _view_filename = Str(transient=True) # Hand crafted view. _module_view = Instance(View, transient=True) # Work around problem with HasPrivateTraits. __ = Python ################################################## ###################################################################### # `object` interface ###################################################################### def __get_pure_state__(self): """Method used by the state_pickler. """ d = self.__dict__.copy() for attr in ('scene', '_is_running', '__sync_trait__', '__traits_listener__', '_icon_path', '_menu', '_HideShowAction', 'menu_helper', 'parent', 'parent_', '_module_view', '_listener_cache', '_view_filename', 'mlab_source'): d.pop(attr, None) return d def __getstate__(self): """Allows standard pickle to work via the state_pickler. """ return state_pickler.dumps(self) def __setstate__(self, str_state): """Allows standard pickle to work via the state_pickler. """ self.__init__() # Get the state from the string and update it. state = state_pickler.loads_state(str_state) state_pickler.update_state(state) # Save the state and load it if we are running. self._saved_state = pickle.dumps(state) if self.running: self._load_saved_state() def __deepcopy__(self, memo): """Method used by copy.deepcopy(). This also uses the state_pickler to work correctly. """ # Create a new instance. new = self.__class__() # If we have a saved state, use it for the new instance. If # not, get our state and save that. saved_state = self._saved_state if len(saved_state) == 0: state = state_pickler.get_state(self) #FIXME: This is for streamline seed point widget position which #does not get serialized correctly if not is_old_pipeline(): try: st = state.children[0].children[4] l_pos = st.seed.widget.position st.seed.widget.position = [pos.item() for pos in l_pos] except (IndexError, AttributeError): pass saved_state = pickle.dumps(state) new._saved_state = saved_state # In the unlikely case that a new instance is running, load # the saved state. if new.running: new._load_saved_state() return new ###################################################################### # `Base` interface ###################################################################### def start(self): """Invoked when this object is added to the mayavi pipeline. """ self.running = True self._load_saved_state() def stop(self): """Invoked when this object is removed from the mayavi pipeline. """ self.running = False def add_child(self, child): """This method intelligently adds a child to this object in the MayaVi pipeline. """ raise NotImplementedError def remove_child(self, child): """Remove specified child from our children. """ raise NotImplementedError() def remove(self): """Remove ourselves from the mayavi pipeline. """ if self.parent is not None: e = get_engine(self) self.parent.remove_child(self) if e.current_object is self: e.current_object = self.parent def render(self): """Invokes render on the scene, this in turn invokes Render on the VTK pipeline. """ s = self.scene if s is not None: s.render() def dialog_view(self): """ Returns a view with an icon and a title. """ view = self.trait_view() icon = self._icon_path + os.sep + 'images' + os.sep \ + self.icon view.icon = ImageResource(icon) view.title = "Edit%s: %s" % (self.type, self.name) view.buttons = ['OK', 'Cancel'] return view def trait_view(self, name=None, view_element=None): """ Gets or sets a ViewElement associated with an object's class. Overridden here to search for a separate file in the same directory for the view to use for this object. The view should be declared in the file named <class name>_view. If a file with this name is not found, the trait_view method on the base class will be called. """ # If a name is specified, then call the HasTraits trait_view method # which will return (or assign) the *view_element* associated with # *name*. if name: return super(Base, self).trait_view(name, view_element) view = self._load_view_cached(name, view_element) # Uncomment this when developping views. #view = self._load_view_non_cached(name, view_element) return view ###################################################################### # `TreeNodeObject` interface ###################################################################### def tno_get_label(self, node): """Gets the label to display for a specified object. """ if self.name == '': self.name = self.__class__.__name__ return self.name def tno_get_view(self, node): """Gets the View to use when editing an object. """ view = self.trait_view() view.kind = 'subpanel' return view def tno_confirm_delete(self, node): """Confirms that a specified object can be deleted or not. """ if preference_manager.root.confirm_delete: return None else: return True def tno_get_menu(self, node): """ Returns the contextual pop-up menu. """ if self._menu is None: return super(Base, self).tno_get_menu(node) return self._menu def tno_get_icon(self, node, is_expanded): return self.icon def tno_get_icon_path(self, node): return self._icon_path def tno_delete_child(self, node, index): if len(self.children_ui_list) > len(self.children): del self.children[index - 1] else: del self.children[index] def tno_append_child(self, node, child): """ Appends a child to the object's children. """ self.children.append(child) def tno_insert_child(self, node, index, child): """ Inserts a child into the object's children. """ if len(self.children_ui_list) > len(self.children): idx = index - 1 else: idx = index self.children[idx:idx] = [child] ###################################################################### # Non-public interface ###################################################################### def _get_running(self): return self._is_running def _set_running(self, new): if self._is_running == new: return else: old = self._is_running self._is_running = new self.trait_property_changed('running', old, new) def _get_children_ui_list(self): """ Getter for Traits Property children_ui_list. For the base class, do not add anything to the children list. """ if ((not preference_manager.root.show_helper_nodes or len(self.children) > 0) or self._adder_node_class is None or (not self.type == ' scene' and 'none' in self.output_info.datasets) # We can't use isinstance, as we would have circular # imports ): return self.children else: return [ self._adder_node_class(object=self), ] @on_trait_change('children[]') def _trigger_children_ui_list(self, old, new): """ Trigger a children_ui_list change when scenes changed. """ self.trait_property_changed('children_ui_list', old, new) def _visible_changed(self, value): # A hack to set the name when the tree view is not active. # `self.name` is set only when tno_get_label is called and this # is never called when the tree view is not shown leading to an # empty name. if len(self.name) == 0: self.tno_get_label(None) if value: #self._HideShowAction.name = "Hide" self.name = self.name.replace(' [Hidden]', '') else: #self._HideShowAction.name = "Show" n = self.name if ' [Hidden]' not in n: self.name = "%s [Hidden]" % n def _load_view_cached(self, name, view_element): """ Use a cached view for the object, for faster refresh. """ if self._module_view is not None: view = self._module_view else: logger.debug( "No view found for [%s] in [%s]. " "Using the base class trait_view instead.", self, self._view_filename) view = super(Base, self).trait_view(name, view_element) return view def _load_view_non_cached(self, name, view_element): """ Loads the view by execing a file. Useful when tweaking views. """ result = {} view_filename = self._view_filename try: exec(compile(open(view_filename).read(), view_filename, 'exec'), {}, result) view = result['view'] except IOError: logger.debug( "No view found for [%s] in [%s]. " "Using the base class trait_view instead.", self, view_filename) view = super(Base, self).trait_view(name, view_element) return view def _hideshow(self): if self.visible: self.visible = False else: self.visible = True def _load_saved_state(self): """Load the saved state (if any) of this object. """ saved_state = self._saved_state if len(saved_state) > 0: state = pickle.loads(saved_state) if hasattr(self, '__set_pure_state__'): self.__set_pure_state__(state) else: state_pickler.set_state(self, state) self._saved_state = '' def __view_filename_default(self): """ The name of the file that will host the view. """ module = self.__module__.split('.') class_filename = module[-1] + '.py' module_dir_name = module[1:-1] base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) view_filename = os.path.join(*([base_dir] + module_dir_name \ + UI_DIR_NAME + [class_filename])) return view_filename def __module_view_default(self): """ Try to load a view for this object. """ view_filename = self._view_filename try: result = imp.load_module('view', open(view_filename, 'r'), view_filename, ('.py', 'U', 1)) view = result.view except: view = None return view def __menu_default(self): extras = [] if self.menu_helper is not None: extras = self.menu_helper.actions + self._extra_menu_items() menu_actions = [Separator()] + extras + \ [Separator(), self._HideShowAction, Separator()] + \ deepcopy(standard_menu_actions) return Menu(*menu_actions) def __icon_path_default(self): return resource_path() def _extra_menu_items(self): """Override this to generate any new menu actions you want on the right click menu.""" return []
class AStrategyBase(HasTraits): ''' Interface for adaptive computations. There are four positions in the algorithm where adaptive changes to the spatial and temporal discretization may be applied: (1) before the computation starts (2) in every iteration (ihandler) (3) on failure of the iteration loop (fhandler) (4) on equilibrium (ehandler) A total of 9 methods is implemented to allow for an flexible adjustment of the global algorithm. The default implementation is neutral regarding the behavior of the global algorithm. ''' tloop = WeakRef() tstepper = Property() def _get_tstepper(self): return self.tloop.tstepper # (1) INITIALIZE # def initialize_strategy(self): '''Called before the computation starts.''' pass # (2) LOAD STEP # def begin_time_step(self, t): '''Prepare a new load step ''' pass def end_time_step(self, t): '''Prepare a new load step ''' pass # (2) ITERATION (ihandler) # def ihandler_needed(self): '''Decides whether the iteration must be adapted. True means that ihandler_invoke is called and the time step is restarted. False deaginctivates any further ihandler actions. ''' return False def ihandler_invoke(self): '''Handle the iteration.''' pass def ihandler_get_scale(self): '''Scale time step with a factor.''' return 1.0 # (3) EQUILIBRIUM (ehandler) # def ehandler_needed(self): '''Decide whether to invoke the equilibrium handler.''' return False def ehandler_accept(self): '''Decide whether to accept or redo the current time step.''' return True def ehandler_invoke(self): ''' Extend algorithm when equilibrium is found (but before potentially accepting the state). ''' pass def ehandler_get_scale(self): '''Scale time step with a factor.''' return 1.0 # (4) FAILURE (fhandler) # def fhandler_get_scale(self): ''' Handle failure of iteration loop. Default: Half the time step. ''' return 1 / 2.
class DOTSListEval(TStepperEval): ''' Domain with uniform FE-time-step-eval. ''' implements(ITStepperEval) sdomain = WeakRef('ibvpy.mesh.fe_domain.FEDomain') dots_list = List def new_cntl_var(self): return zeros(self.sdomain.n_dofs, float_) def new_resp_var(self): return zeros(self.sdomain.n_dofs, float_) K = Property @cached_property def _get_K(self): return SysMtxAssembly() F_int = Property(depends_on='sdomain.changed_structure') @cached_property def _get_F_int(self): n_dofs = self.sdomain.n_dofs return zeros(n_dofs, 'float_') def setup(self, sctx): print 'DEPRECATED CALL TO SETUP' def get_state_array_size(self): return 0 def apply_constraints(self, K): for dots_eval in self.dots_list: dots_eval.apply_constraints(K) def get_corr_pred(self, sctx, U, d_U, tn, tn1, *args, **kw): K = self.K K.reset() F_int = self.F_int F_int[:] = 0.0 U = self.tstepper.U_k d_U = self.tstepper.d_U for dots_eval in self.dots_list: K_mtx_arr = dots_eval.get_corr_pred(sctx, U, d_U, tn, tn1, self.F_int, *args, **kw) K.sys_mtx_arrays.append(K_mtx_arr) return self.F_int, self.K rte_dict = Property(Dict, depends_on='dots_list') @cached_property def _get_rte_dict(self): rte_dict = {} dots_rte_dicts = [] rte_keys = [] for dots_eval in self.dots_list: dots_rte_dict = {} for key, eval in dots_eval.rte_dict.items(): # add the mapping here # if key not in rte_keys: rte_keys.append(key) dots_rte_dict[key] = eval dots_rte_dicts.append(dots_rte_dict) # Get the union of all available rte keys for key in rte_keys: rte_list = [] for rte_dict in dots_rte_dicts: rte_list.append(rte_dict.get(key, None)) rte_dict[key] = tuple(rte_list) return rte_dict traits_view = View()
class RTraceDomain(HasTraits): ''' Trace encompassing the whole spatial domain. ''' label = Str('RTraceDomainField') fets_eval = Delegate('sd') dots = Delegate('sd') position = Enum('nodes', 'int_pnts') sd = WeakRef(ISDomain) point_offset = Int cell_offset = Int # Tag that can be used to skip the domain when gathering the vtk data # skip_domain = Bool(False) #-------------------------ts_eval----------------------------------------- # Visualization pipelines #------------------------------------------------------------------------- mvp_mgrid_geo = Trait(MVUnstructuredGrid) def _mvp_mgrid_geo_default(self): return MVUnstructuredGrid(name='Response tracer mesh', warp=False, warp_var='') def redraw(self): ''' ''' # self.mvp_mgrid_geo.redraw() print 'REDRAWING', self.sd.name self.mvp_mgrid_geo.rebuild_pipeline(self.vtk_node_structure) vtk_node_structure = Property(Instance(tvtk.UnstructuredGrid), depends_on='sd.changed_structure') @cached_property def _get_vtk_node_structure(self): self.position = 'nodes' return self.vtk_structure vtk_ip_structure = Property(Instance(tvtk.UnstructuredGrid), depends_on='sd.changed_structure') @cached_property def _get_vtk_ip_structure(self): self.position = 'int_pnts' return self.vtk_structure vtk_structure = Property(Instance(tvtk.UnstructuredGrid)) def _get_vtk_structure(self): ug = tvtk.UnstructuredGrid() if self.skip_domain: return ug cell_array, cell_offsets, cell_types = self.vtk_cell_data n_cells = cell_types.shape[0] ug.points = self.vtk_X vtk_cell_array = tvtk.CellArray() vtk_cell_array.set_cells(n_cells, cell_array) ug.set_cells(cell_types, cell_offsets, vtk_cell_array) return ug # vtk_X = Property(Array) #TODO: cleanup # def _get_vtk_X(self): # '''Get the discretization points based on the fets_eval # associated with the current domain. # ''' # if self.position == 'int_pnts': # ip_arr = self.fets_eval.ip_coords # # pts = [] # dim_slice = self.fets_eval.dim_slice # for e in self.sd.elements: # X = e.get_X_mtx() # if dim_slice: # X = X[:,dim_slice] # if self.position == 'int_pnts': # ip_arr = ip_arr[:,dim_slice] # if self.position == 'nodes': # pts += list( self.fets_eval.get_vtk_r_glb_arr( X ) ) # elif self.position == 'int_pnts': # pts += list( self.fets_eval.get_vtk_r_glb_arr( X, ip_arr) ) # pts_array = array(pts, dtype = 'float_' ) # return pts_array vtk_X = Property(Array) # TODO: cleanup def _get_vtk_X(self): return self.dots.get_vtk_X(self.position) # debug_cell_data = Bool(True) # vtk_cell_data = Property(Array, depends_on = 'point_offset,cell_offset' )#TODO:check the dependencies # def _get_vtk_cell_data(self): # # if self.position == 'nodes': # subcell_offsets, subcell_lengths, subcells, subcell_types = self.dots.vtk_node_cell_data # elif self.position == 'int_pnts': # subcell_offsets, subcell_lengths, subcells, subcell_types = self.fets_eval.vtk_ip_cell_data # # self.debug_cell_data = True # # if self.debug_cell_data: # print 'subcell_offsets' # print subcell_offsets # print 'subcell_lengths' # print subcell_lengths # print 'subcells' # print subcells # print 'subcell_types' # print subcell_types # # n_subcells = subcell_types.shape[0] # n_cell_points = self.n_cell_points # subcell_size = subcells.shape[0] + n_subcells # # if self.debug_cell_data: # print 'n_cell_points', n_cell_points # print 'n_cells', self.n_cells # # vtk_cell_array = zeros( (self.n_cells, subcell_size), dtype = int ) # # idx_cell_pnts = repeat( True, subcell_size ) # # if self.debug_cell_data: # print 'idx_cell_pnts' # print idx_cell_pnts # # idx_cell_pnts[ subcell_offsets ] = False # # if self.debug_cell_data: # print 'idx_cell_pnts' # print idx_cell_pnts # # idx_lengths = idx_cell_pnts == False # # if self.debug_cell_data: # print 'idx_lengths' # print idx_lengths # # point_offsets = arange( self.n_cells ) * n_cell_points # point_offsets += self.point_offset # # if self.debug_cell_data: # print 'point_offsets' # print point_offsets # # vtk_cell_array[:,idx_cell_pnts] = point_offsets[:,None] + subcells[None,:] # vtk_cell_array[:,idx_lengths] = subcell_lengths[None,:] # # if self.debug_cell_data: # print 'vtk_cell_array' # print vtk_cell_array # # active_cells = self.sd.idx_active_elems # # if self.debug_cell_data: # print 'active cells' # print active_cells # # cell_offsets = active_cells * subcell_size # cell_offsets += self.cell_offset # vtk_cell_offsets = cell_offsets[:,None] + subcell_offsets[None,:] # # if self.debug_cell_data: # print 'vtk_cell_offsets' # print vtk_cell_offsets # # vtk_cell_types = zeros( self.n_cells * n_subcells, dtype = int ).reshape( self.n_cells, # n_subcells ) # vtk_cell_types += subcell_types[None,:] # # if self.debug_cell_data: # print 'vtk_cell_types' # print vtk_cell_types # # return vtk_cell_array.flatten(), vtk_cell_offsets.flatten(), # vtk_cell_types.flatten() # TODO:check the dependencies vtk_cell_data = Property( Array, depends_on='point_offset,cell_offset,sd.changed_structure') def _get_vtk_cell_data(self): # cell_array, cell_offsets, cell_types = self.dots.get_vtk_cell_data(self.position) # cell_array += self.point_offset # cell_offsets += self.cell_offset return self.dots.get_vtk_cell_data(self.position, self.point_offset, self.cell_offset) # number of points n_points = Property(Int, depends_on='vtk_X, sd.changed_structure') @cached_property def _get_n_points(self): if self.skip_domain: return 0 return self.vtk_X.shape[0] # debug_cell_data = Bool( False ) # n_cells = Property( Int ) # def _get_n_cells(self): # '''Return the total number of cells''' # return self.sd.n_active_elems # # n_cell_points = Property( Int ) # def _get_n_cell_points(self): # '''Return the number of points defining one cell''' # return self.fets_eval.n_vtk_r def clear(self): pass view = View(HSplit( VSplit(VGroup(Item('refresh_button', show_label=False), ), ), ), resizable=True)
class MousePickDispatcher(HasTraits): """ An event dispatcher to send pick event on mouse clicks. This objects wires VTK observers so that picking callbacks can be bound to mouse click without movement. The object deals with adding and removing the VTK-level callbacks. """ # The scene events are wired to. scene = WeakRef(Scene) # The list of callbacks, with the picker type they should be using, # and the mouse button that triggers them. callbacks = List(Tuple( Callable, Enum('cell', 'point', 'world'), Enum('Left', 'Middle', 'Right'), ), help="The list of callbacks, with the picker type they " "should be using, and the mouse button that " "triggers them. The callback is passed " "as an argument the tvtk picker." ) #-------------------------------------------------------------------------- # Private traits #-------------------------------------------------------------------------- # Whether the mouse has moved after the button press _mouse_no_mvt = Int # The button that has been pressed _current_button = Enum('Left', 'Middle', 'Right') # The various picker that are used when the mouse is pressed _active_pickers = Dict # The VTK callback numbers corresponding to our callbacks _picker_callback_nbs = Dict(value_trait=Int) # The VTK callback numbers corresponding to mouse movement _mouse_mvt_callback_nb = Int # The VTK callback numbers corresponding to mouse press _mouse_press_callback_nbs = Dict # The VTK callback numbers corresponding to mouse release _mouse_release_callback_nbs = Dict #-------------------------------------------------------------------------- # Callbacks management #-------------------------------------------------------------------------- @on_trait_change('callbacks_items') def dispatch_callbacks_change(self, name, trait_list_event): for item in trait_list_event.added: self.callback_added(item) for item in trait_list_event.removed: self.callback_removed(item) def callback_added(self, item): """ Wire up the different VTK callbacks. """ callback, type, button = item picker = getattr(self.scene.scene.picker, '%spicker' % type) self._active_pickers[type] = picker # Register the pick callback if not type in self._picker_callback_nbs: self._picker_callback_nbs[type] = \ picker.add_observer("EndPickEvent", self.on_pick) # Register the callbacks on the scene interactor if VTK_VERSION>5: move_event = "RenderEvent" else: move_event = 'MouseMoveEvent' if not self._mouse_mvt_callback_nb: self._mouse_mvt_callback_nb = \ self.scene.scene.interactor.add_observer(move_event, self.on_mouse_move) if not button in self._mouse_press_callback_nbs: self._mouse_press_callback_nbs[button] = \ self.scene.scene.interactor.add_observer( '%sButtonPressEvent' % button, self.on_button_press) if VTK_VERSION>5: release_event = "EndInteractionEvent" else: release_event = '%sButtonReleaseEvent' % button if not button in self._mouse_release_callback_nbs: self._mouse_release_callback_nbs[button] = \ self.scene.scene.interactor.add_observer( release_event, self.on_button_release) def callback_removed(self, item): """ Clean up the unecessary VTK callbacks. """ callback, type, button = item # If the picker is no longer needed, clean up its observers. if not [t for c, t, b in self.callbacks if t == type]: picker = self._active_pickers[type] picker.remove_observer(self._picker_callback_nbs[type]) del self._active_pickers[type] # If there are no longer callbacks on the button, clean up # the corresponding observers. if not [b for c, t, b in self.callbacks if b == button]: self.scene.scene.interactor.remove_observer( self._mouse_press_callback_nbs[button]) self.scene.scene.interactor.remove_observer( self._mouse_release_callback_nbs[button]) if len(self.callbacks) == 0 and self._mouse_mvt_callback_nb: self.scene.scene.interactor.remove_observer( self._mouse_mvt_callback_nb) self._mouse_mvt_callback_nb = 0 def clear_callbacks(self): while self.callbacks: self.callbacks.pop() #-------------------------------------------------------------------------- # Mouse movement dispatch mechanism #-------------------------------------------------------------------------- def on_button_press(self, vtk_picker, event): self._current_button = event[:-len('ButtonPressEvent')] self._mouse_no_mvt = 2 def on_mouse_move(self, vtk_picker, event): if self._mouse_no_mvt: self._mouse_no_mvt -= 1 def on_button_release(self, vtk_picker, event): """ If the mouse has not moved, pick with our pickers. """ if self._mouse_no_mvt: x, y = vtk_picker.GetEventPosition() for picker in self._active_pickers.values(): try: picker.pick((x, y, 0), self.scene.scene.renderer) except TypeError: picker.pick(x, y, 0, self.scene.scene.renderer) self._mouse_no_mvt = 0 def on_pick(self, vtk_picker, event): """ Dispatch the pick to the callback associated with the corresponding mouse button. """ picker = tvtk.to_tvtk(vtk_picker) for event_type, event_picker in self._active_pickers.items(): if picker is event_picker: for callback, type, button in self.callbacks: if ( type == event_type and button == self._current_button): callback(picker) break #-------------------------------------------------------------------------- # Private methods #-------------------------------------------------------------------------- def __del__(self): self.clear_callbacks()
class Container(LayoutTaskHandler, PaddingConstraints, ConstraintsWidget): """ A Component subclass that provides functionality for laying out constrainable children according to their system of constraints. """ #: A read-only cached property which returns the constraints layout #: manager for this container, or None if the layout is being managed #: by a parent container. layout_manager = Property( Instance(ConstraintsLayout), depends_on='owns_layout', ) #: The list of children that can participate in constraints based #: layout. This list is composed of components in the list of #: children that are instances of Constrainable. constraints_children = Property( List(Instance(Constrainable)), depends_on='children', ) #: A boolean which indicates whether or not to allow the layout #: ownership of this container to be transferred to an ancestor. #: This is False by default, which means that every container #: get its own layout solver. This improves speed and reduces #: memory use (by keeping a solver's internal tableaux small) #: but at the cost of not being able to share constraints #: across Container boundaries. This flag must be explicitly #: marked as True to enable sharing. share_layout = Bool(False) #: A read-only property which returns True if this container owns #: its layout and is responsible for setting the geometry of its #: children, or False if that responsibility has been transferred #: to another component in the hierarchy. owns_layout = Property(Bool, depends_on='_layout_owner') #: A container has a default padding of 10 on all sides. padding = Box(10, 10, 10, 10) #: Containers freely exapnd in width and height. The size hint #: constraints for a Container are used when the container is #: not sharing its layout. In these cases, expansion of the #: container is typically desired. hug_width = 'ignore' hug_height = 'ignore' #: A private trait which stores a weak reference to the owner of #: the layout for this container, or None if this container owns #: its layout. _layout_owner = WeakRef(allow_none=True) #: A private cached property which computes the size hint whenever #: the size_hint_updated event is fired. _size_hint = Property(Instance(Size), depends_on='size_hint_updated') #: Overridden parent class trait. abstract_obj = Instance(AbstractTkContainer) #-------------------------------------------------------------------------- # Property Getters #-------------------------------------------------------------------------- @cached_property def _get_constraints_children(self): """ Cached property getter for the 'constraints_children' attribute. This getter returns the sublist of children that are instances of ConstraintsWidget. """ flt = lambda child: isinstance(child, Constrainable) return filter(flt, self.children) @cached_property def _get_layout_manager(self): """ The property getter for the 'layout_manager' attribute. """ if self.owns_layout: return ConstraintsLayout() def _get_owns_layout(self): """ The property getter for the 'owns_layout' attribute. """ return self._layout_owner is None @cached_property def _get__size_hint(self): """ The property getter for the '_size_hint' attribtue. """ # XXX we can probably do better than the min size. Maybe have # the layout manager compute a preferred size, or something # similar to a preferred size. But I don't know at the moment # what it would actually mean to have a preferred size from # a set of constraints. return self.compute_min_size() #-------------------------------------------------------------------------- # Change Handlers #-------------------------------------------------------------------------- def _constraints_children_changed(self): """ A handler which requests a relayout when the constraints children change, provided that the container is initialized. """ if self.initialized: self.request_relayout() #-------------------------------------------------------------------------- # Setup Methods #-------------------------------------------------------------------------- def _setup_init_layout(self): """ A reimplemented parent class setup method that performs any layout initialization necessary for the component. The layout is initialized from the bottom up. """ super(Container, self)._setup_init_layout() self.initialize_layout() #-------------------------------------------------------------------------- # Layout Handling #-------------------------------------------------------------------------- def initialize_layout(self): """ A reimplemented parent class method that initializes the layout manager for the first time, and binds the relevant layout change handlers. """ # We only need to initialize the manager if we own the layout. if self.owns_layout: constraints = self.compute_constraints() self.layout_manager.initialize(constraints) # We fire off a size hint updated event here since, if # for some reason, the size hint was computed before # the layout was initialized, the value will be wrong # and cached. So we need to clear the cache so that it # will be recomputed by the next consumer that needs it. self.size_hint_updated() def relayout(self): """ A reimplemented parent class method which forwards the call to the layout owner if necessary. """ if self.owns_layout: super(Container, self).relayout() else: self._layout_owner.relayout() def refresh(self): """ A reimplemented parent class method which forwards the call to the layout owner if necessary. """ if self.owns_layout: super(Container, self).refresh() else: self._layout_owner.refresh() def request_relayout(self): """ A reimplemented parent class method which forwards the call to the layout owner if necessary. """ if self.owns_layout: super(Container, self).request_relayout() else: self._layout_owner.request_relayout() def request_refresh(self): """ A reimplemented parent class method which forwards the call to the layout owner if necessary. """ if self.owns_layout: super(Container, self).request_refresh() else: self._layout_owner.request_refresh() def request_relayout_task(self, callback, *args, **kwargs): """ A reimplemented parent class method which forwards the call to the layout owner if necessary. """ if self.owns_layout: sup = super(Container, self) sup.request_relayout_task(callback, *args, **kwargs) else: self._layout_owner.request_relayout_task(callback, *args, **kwargs) def request_refresh_task(self, callback, *args, **kwargs): """ A reimplemented parent class method which forwards the call to the layout owner if necessary. """ if self.owns_layout: sup = super(Container, self) sup.request_refresh_task(callback, *args, **kwargs) else: self._layout_owner.request_refresh_task(callback, *args, **kwargs) def do_relayout(self): """ A reimplemented LayoutTaskHandler handler method which will actually perform the layout. """ # At this point, we know that we own the layout since the # calls that trigger the call to this method would have # already been forwarded on to the layout owner. So, at # this point, we just have to recompute the constraints # and do a refresh. # TODO There is a big opportunity here to optimize by calling # .update_constraints() on the layout manager instead of just # recomputing everything from scratch, but that will require # tracking the created constraints so for now we just punt. self.layout_manager.initialize(self.compute_constraints()) self.do_refresh() # We emit the size hint updated event at this point since # we are still inside a freeze context. This means that if # our parent is a toplevel window, it can set the new size # of the window before leaving the context which helps # eleminate flicker during the resize. self.size_hint_updated() def do_refresh(self): """ A reimplemented LayoutTaskHandler handler method which will actually perform the refresh. """ # At this point, we know that we own the layout since the # calls that trigger the call to this method would have # already been forwarded on to the layout owner. So, at # this point, we just have to do a refresh. width = self.width height = self.height size = self.size() self.layout_manager.layout(self.apply_layout, width, height, size) def apply_layout(self): """ The callback invoked by the layout manager when there are new layout values available. This traverses the constraints children for which this container has layout ownership and applies the geometry updates. """ stack = [((0, 0), self.constraints_children)] pop = stack.pop push = stack.append while stack: offset, children = pop() for child in children: new_offset = child.update_layout_geometry(*offset) if isinstance(child, Container): if child._layout_owner is self: push((new_offset, child.constraints_children)) #-------------------------------------------------------------------------- # Constraints Computation #-------------------------------------------------------------------------- def compute_constraints(self): """ Descends the tree for all containers and children for which this container can manage layout, and aggregates all of their constraints into a single list. """ expand = expand_constraints cns = [] cns_extend = cns.extend # We don't care about the size hint constraints for a container # which manages a layout because the actual size is the input # to the solver. cns_extend(expand(self, self.hard_constraints())) cns_extend(expand(self, self.padding_constraints())) cns_extend(expand(self, self.user_constraints())) cns_extend(expand(self, self.component_constraints())) stack = list(self.constraints_children) stack_pop = stack.pop stack_extend = stack.extend while stack: child = stack_pop() if isinstance(child, Container): # When we take over layout ownership of a container we # don't care about its size hint constraints since we # deal with its children directly. if child.transfer_layout_ownership(self): cns_extend(expand(child, child.hard_constraints())) cns_extend(expand(child, child.padding_constraints())) cns_extend(expand(child, child.user_constraints())) cns_extend(expand(child, child.component_constraints())) stack_extend(child.constraints_children) else: # If we aren't taking over layout ownership, then we # don't care about any of the container's internal # constraints. cns_extend(expand(child, child.hard_constraints())) cns_extend(expand(child, child.size_hint_constraints())) else: cns_extend(expand(child, child.hard_constraints())) cns_extend(expand(child, child.size_hint_constraints())) cns_extend(expand(child, child.user_constraints())) cns_extend(expand(child, child.component_constraints())) if isinstance(child, PaddingConstraints): cns_extend(expand(child, child.padding_constraints())) return cns def default_user_constraints(self): """ Constraints to use if the constraints trait is an empty list. The default container behavior is to put the layout children into a vertical box layout. """ from ..layout.layout_helpers import vbox return [vbox(*self.constraints_children)] #-------------------------------------------------------------------------- # Overrides #-------------------------------------------------------------------------- def size_hint(self): """ Overridden parent class method to return the size hint of the container from the layout manager. If the container does not own its layout, or if the layout has not been initialized, then this method will return (-1, -1). This method does not rely on the size hint computation of the underlying toolkit widget. Thus, the underlying widget may call back into this method as needed to get a size hint from the layout manager. """ # Since this may be called very often by user code, especially # if the toolkit widget is using it as a replacement for its # internal size hint computation, we must cache the value or # it will be too expensive to use under heavy resize loads. # This returns the value from the cached property which is # updated whenver the size_hint_updated event is fired. return self._size_hint #-------------------------------------------------------------------------- # Auxiliary Methods #-------------------------------------------------------------------------- def transfer_layout_ownership(self, owner): """ A method which can be called by other components in the hierarchy to gain ownership responsibility for the layout of the children of this container. Whether or not to allow the transfer is determined by the boolean value of the 'share_layout' flag. Parameters ---------- owner : BaseComponent The component which has taken ownership responsibility for laying out the children of this component. All relayout and refresh requests will be forwarded to this component. Returns ------- results : bool True if the transfer was allowed, False otherwise. """ if not self.share_layout: return False self._layout_owner = owner return True def compute_min_size(self): """ Calculates the minimum size of the container which would allow all constraints to be satisfied. If this container does not own its layout, or if the layout has not been initialized, then this method will return (-1, -1). """ if self.owns_layout and self.layout_manager.initialized: width = self.width height = self.height w, h = self.layout_manager.get_min_size(width, height) res = Size(int(round(w)), int(round(h))) else: res = Size(-1, -1) return res def compute_max_size(self): """ Calculates the maximum size of the container which would allow all constraints to be satisfied. If this container does not own its layout, or if the layout has not been initialized then this method will return (-1, -1). """ if self.owns_layout and self.layout_manager.initialized: width = self.width height = self.height w, h = self.layout_manager.get_max_size(width, height) res = Size(int(round(w)), int(round(h))) else: res = Size(-1, -1) return res
class FEGrid(FEGridActivationMap): '''Structured FEGrid consisting of potentially independent dof_grid and geo_grid. For isoparametric element formulations, the dof_grid and geo_grid may share a single cell_grid to save memory. Structure of the grid --------------------- The structure of the grid is defined at two levels: 1) within a cell specify the distribution of points (for dof_r and for geo_r). 2) the cells are repeated in respective dimension by n_elem number of elements Services -------- 1) For a given element number return the nodal coordinates respecting the specification in the geo_r 2) For a given element number return the array of dof numbers respecting the specification in the geo_r 3) For a given CellSpec return a CellGrid respecting the geometric parameters of the FEGrid (applicable for response trace fields with finer distribution of nodes. 4) For a given subdivision of the cell return CellGrid usable as visualization field.(where to specify the topology? - probably in the CellSpec? ''' # Grid geometry specification # coord_min = Array(Float, value=[0., 0., 0.]) coord_max = Array(Float, value=[1., 1., 1.]) geo_transform = Callable # number of elements in the individual dimensions shape = Array(Int, value=[1, 1, 1], changes_ndofs=True) changed_structure = Event @on_trait_change('+changed_structure') def set_changed_structure(self): self.changed_structure = True _tree_label = 'subgrid' fets_eval = Instance(IFETSEval) # identifier within the refinement level idx = Int() # links within the dependency prev_grid = WeakRef('ibvpy.mesh.fe_grid.FEGrid', allow_none=True) next_grid = WeakRef('ibvpy.mesh.fe_grid.FEGrid', allow_none=True) _name = Str('') name = Property def _get_name(self): '''Return the name within the level ''' if self._name == '': return 'grid ' + str(self.idx) else: return self._name def _set_name(self, value): self._name = value def __repr__(self): return self.name # dof offset within the global enumeration dof_offset = Property( Int, depends_on='prev_grid.dof_offset,level.dof_offset') # cached_property def _get_dof_offset(self): if self.prev_grid: return self.prev_grid.dof_offset + self.prev_grid.n_dofs elif self.level: return self.level.dof_offset else: return 0 _level = WeakRef( 'ibvpy.mesh.fe_refinement_grid.FERefinementGrid', links_changed=True) # parent domain level = Property def _set_level(self, value): 'reset the parent of this domain' if self._level and self._level != value: # remove the grid from the level self._level._fe_subgrids.remove(self) # unlink it from previous and next if self.prev_grid: self.prev_grid.next_grid = self.next_grid if self.next_grid: self.next_grid.prev_grid = self.prev_grid # set the new parent self._level = value # link the subgrid within the level # prev_grid = self._level.last_subgrid # if prev_grid: # self.prev_grid = prev_grid # prev_grid.next_grid = self # add to the level self.idx = len(self._level._fe_subgrids) self._level._fe_subgrids.append(self) def _get_level(self): return self._level def __del__(self): ''' Release the grid from the dependency structure ''' if self.prev_grid: self.prev_grid.next_grid = self.next_grid if self.next_grid: self.next_grid.prev_grid = self.prev_grid dots = Property @cached_property def _get_dots(self): '''Construct and return a new instance of domain time stepper. ''' return self.fets_eval.dots_class(sdomain=self) dof_r = Property def _get_dof_r(self): return self.fets_eval.dof_r geo_r = Property def _get_geo_r(self): return self.fets_eval.geo_r n_nodal_dofs = Property def _get_n_nodal_dofs(self): return self.fets_eval.n_nodal_dofs #------------------------------------------------------------------------- # Derived properties #------------------------------------------------------------------------- # dof point distribution within the cell converted into the CellSpec format # CellSpec can derive the shape of the single grid cell, i.e. # the number of points specified in the individual directions. # dof_grid_spec = Property(Instance(CellSpec), depends_on='fets_eval.dof_r') def _get_dof_grid_spec(self): return CellSpec(node_coords=self.dof_r) # geo point distribution within the cell ... the same as above ... # geo_grid_spec = Property(Instance(CellSpec), depends_on='fets_eval.geo_r') def _get_geo_grid_spec(self): return CellSpec(node_coords=self.geo_r) # dof_grid is represented by the DofCellGrid class maintaining # the internal data structure to retrieve the element mappings # within the grid. # dof_grid = Property(Instance(DofCellGrid), depends_on='fets_eval.dof_r,shape,coord_min,coord_max') def _get_dof_grid(self): return self._grids[0] dof_vertex_X_grid = Property def _get_dof_vertex_X_grid(self): return self.dof_grid.cell_grid.vertex_X_grid intg_elem_grid = Property def _get_intg_elem_grid(self): return self.dof_grid.cell_grid.cell_idx_grid ls_mask = Property def _get_ls_mask(self): return zeros(self.intg_elem_grid.shape, dtype=bool) ls_elem_grid = Property def _get_ls_elem_grid(self): return self.dof_grid.cell_grid.cell_idx_grid intg_elem_grid_dof_map = Property def _get_intg_elem_grid_dof_map(self): return self.dof_grid.cell_grid_dof_map def get_cell_point_X_arr(self, elem): return self.geo_grid.get_cell_point_X_arr(elem) # geo_grid is represented by the GeoCellGrid class maintaining # the internal data structure to retrieve the element mappings # within the grid. # geo_grid = Property(Instance(GeoCellGrid), depends_on='fets_eval.geo_r,shape,coord_min,coord_max') def _get_geo_grid(self): return self._grids[1] traits_view = View( # Include( 'assemb_view' ), # Group( # Item( 'shape' ), # Item( 'coord_min' ), # Item( 'coord_max' ), # Item( 'n_nodal_dofs' ), # Item( 'fe_cell_array' ), # label = 'Geometry data' ), Group( Item('n_dofs'), Item('dof_offset'), label='DOF data'), resizable=True, scrollable=True, ) I = Property def _get_I(self): return ISliceProperty(fe_grid=self) def __getitem__(self, idx): if isinstance(idx, tuple) or isinstance(idx, int): return FEGridIdxSlice(fe_grid=self, grid_slice=idx) elif isinstance(idx, str): return FEGridLevelSetSlice(fe_grid=self, ls_function_eval=idx) else: raise TypeError(type(idx), 'is unsupported type for slicing') #------------------------------------------------------------- # Implement the FEDomain interface #------------------------------------------------------------- n_dofs = Property(Int) def _get_n_dofs(self): return self.dof_grid.n_dofs def get_cell_offset(self, idx_tuple): return self.dof_grid.get_cell_offset(idx_tuple) #------------------------------------------------------------------------- # Implement the parent interface #------------------------------------------------------------------------- def deactivate(self, idx): '''Exclude the specified element from the integration. ''' if isinstance(idx, tuple): self.inactive_elems.append(self.dof_grid.get_cell_offset(idx)) elif isinstance(idx, int): self.inactive_elems.append(idx) if self.level: self.level.set_changed_structure() #------------------------------------------------------------- # Implement the IFEUniformDomain interface #------------------------------------------------------------- # Full elem dof map ignoring masked elements. ls_elem_dof_map = Property def _get_ls_elem_dof_map(self): return self.dof_grid.elem_dof_map # Full elem dof map ignoring masked elements. elem_dof_map_unmasked = Property def _get_elem_dof_map_unmasked(self): return self.dof_grid.elem_dof_map elem_dof_map = Property( depends_on='fets_eval.dof_r,shape,dof_offset, changed_structure') def _get_elem_dof_map(self): elem_dof_map = self.dof_grid.elem_dof_map[self.activation_map, :] return elem_dof_map elem_X_map = Property( depends_on='fets_eval.geo_r,shape,coord_min,coord_max,n_nodal_dofs, changed_structure') @cached_property def _get_elem_X_map(self): elem_X_map = self.geo_grid.elem_X_map[self.activation_map, :].copy() return elem_X_map ls_elem_X_map = Property def _get_ls_elem_X_map(self): return self.geo_grid.elem_X_map.copy() elem_X_map_unmasked = Property def _get_elem_X_map_unmasked(self): return self.geo_grid.elem_X_map.copy() elem_x_map = Property( depends_on='fets_eval.geo_r,shape,coord_min,coord_max,n_nodal_dofs, changed_structure') @cached_property def _get_elem_x_map(self): elem_x_map = self.geo_grid.elem_x_map[self.activation_map, :].copy() return copy(elem_x_map) elem_x_map_unmasked = Property def _get_elem_x_map_unmasked(self): return self.geo_grid.elem_x_map.copy() ls_elem_x_map = Property def _get_ls_elem_x_map(self): return self.geo_grid.elem_x_map.copy() X_dof_arr = Property def _get_X_dof_arr(self): return self.dof_grid.cell_grid.point_X_arr elem_node_map = Property def _get_elem_node_map(self): return self.dof_grid.cell_grid.cell_node_map n_grid_elems = Property def _get_n_grid_elems(self): return reduce(lambda x, y: x * y, self.shape) n_active_elems = Property def _get_n_active_elems(self): return self.elem_dof_map.shape[0] elements = Property( List, depends_on='fets_eval.dof_r,fets_eval.geo_r,shape+,coord_min,coord_max,fets_eval.n_nodal_dofs,dof_offset, changed_structure') @cached_property def _get_elements(self): return [MElem(dofs=dofs, point_X_arr=point_X_arr, point_x_arr=point_x_arr) for dofs, point_X_arr, point_x_arr in zip(self.elem_dof_map, self.elem_X_map, self.elem_x_map)] dof_Eid = Property '''Mapping of Element, Node, Dimension -> DOF ''' def _get_dof_Eid(self): return self.dof_grid.dof_Eid dofs = Property def _get_dofs(self): return self.dof_grid.dofs I_Ei = Property '''For a given element and its node number return the global index of the node''' def _get_I_Ei(self): return self.geo_grid.cell_grid.cell_node_map X_Id = Property() '''Array of containing the coordinate d \in (0,1,3) of a node I ''' def _get_X_Id(self): return self.geo_grid.point_X_arr def apply_on_ip_grid(self, fn, ip_mask): ''' Apply the function fn over the first dimension of the array. @param fn: function to apply for each ip from ip_mask and each element. @param ip_mask: specifies the local coordinates within the element. ''' X_el = self.elem_X_map # test call to the function with single output - to get the shape of # the result. out_single = fn(ip_mask[0], X_el[0]) out_grid_shape = (X_el.shape[0], ip_mask.shape[0],) + out_single.shape out_grid = zeros(out_grid_shape) for el in range(X_el.shape[0]): for ip in range(ip_mask.shape[0]): out_grid[el, ip, ...] = fn(ip_mask[ip], X_el[el]) return out_grid def apply_on_ip_grid_unmasked(self, fn, ip_mask): ''' Apply the function fn over the first dimension of the array. @param fn: function to apply for each ip from ip_mask and each element. @param ip_mask: specifies the local coordinates within the element. ''' X_el = self.geo_grid.elem_X_map # test call to the function with single output - to get the shape of # the result. out_single = fn(ip_mask[0], X_el[0]) out_grid_shape = (X_el.shape[0], ip_mask.shape[0],) + out_single.shape out_grid = zeros(out_grid_shape) for el in range(X_el.shape[0]): for ip in range(ip_mask.shape[0]): out_grid[el, ip, ...] = fn(ip_mask[ip], X_el[el]) return out_grid #------------------------------------------------------------- # Queries #------------------------------------------------------------- # The delegation had to be replaced with explicit calls to functions. # The reason is lost binding if a method # # fe_domain.get_left_dofs # # If the discretization parameter changed, then a new dof_grid was generated # but the call fe_domain.get_left_dofs went to the original dof_grid. # # get_all_dofs = DelegatesTo('dof_grid') def get_all_dofs(self): return self.dof_grid.get_all_dofs() # get_left_dofs = Delegate('dof_grid') def get_left_dofs(self): return self.dof_grid.get_left_dofs() # get_right_dofs = Delegate('dof_grid') def get_right_dofs(self): return self.dof_grid.get_right_dofs() # get_top_dofs = Delegate('dof_grid') def get_top_dofs(self): return self.dof_grid.get_top_dofs() # get_bottom_dofs = Delegate('dof_grid') def get_bottom_dofs(self): return self.dof_grid.get_bottom_dofs() # get_front_dofs = Delegate('dof_grid') def get_front_dofs(self): return self.dof_grid.get_front_dofs() # get_back_dofs = Delegate('dof_grid') def get_back_dofs(self): return self.dof_grid.get_back_dofs() # get_bottom_left_dofs = Delegate('dof_grid') def get_bottom_left_dofs(self): return self.dof_grid.get_bottom_left_dofs() # get_bottom_front_dofs = Delegate('dof_grid') def get_bottom_front_dofs( self): return self.dof_grid.get_bottom_front_dofs() # get_bottom_back_dofs = Delegate('dof_grid') def get_bottom_back_dofs(self): return self.dof_grid.get_bottom_back_dofs() # get_top_left_dofs = Delegate('dof_grid') def get_top_left_dofs(self): return self.dof_grid.get_top_left_dofs() # get_bottom_right_dofs = Delegate('dof_grid') def get_bottom_right_dofs( self): return self.dof_grid.get_bottom_right_dofs() # get_top_right_dofs = Delegate('dof_grid') def get_top_right_dofs(self): return self.dof_grid.get_top_right_dofs() # get_bottom_middle_dofs = Delegate('dof_grid') def get_bottom_middle_dofs( self): return self.dof_grid.get_bottom_middle_dofs() # get_top_middle_dofs = Delegate('dof_grid') def get_top_middle_dofs(self): return self.dof_grid.get_top_middle_dofs() # get_left_middle_dofs = Delegate('dof_grid') def get_left_middle_dofs(self): return self.dof_grid.get_left_middle_dofs() # get_right_middle_dofs = Delegate('dof_grid') def get_right_middle_dofs( self): return self.dof_grid.get_right_middle_dofs() # get_left_front_bottom_dof = Delegate('dof_grid') def get_left_front_bottom_dofs( self): return self.dof_grid.get_left_front_bottom_dofs() # get_left_front_middle_dof = Delegate('dof_grid') def get_left_front_middle_dofs( self): return self.dof_grid.get_left_front_middle_dofs() #------------------------------------------------------------- # LevelSet Methods #------------------------------------------------------------- def get_lset_subdomain(self, lset_function): '''@TODO - implement the subdomain selection method ''' raise NotImplementedError def get_boundary(self, side=None): '''@todo:-implement the boundary extraction ''' raise NotImplementedError def get_interior(self): '''@todo:-implement the boundary extraction ''' raise NotImplementedError def get_ls_mask(self, ls_mask_function): ''' Return a boolean array indicating masked nodes. ''' return self.geo_grid.get_ls_mask(ls_mask_function) def get_intersected_elems(self, ls_function, ls_limits=None): ''' Return elems intersected by specified domain. Requirements on the e_domain - - - - - - - - - - - - - - - - - - - - - - - - - - - - The e_domain must be at most n - 1 dimensionality of the grid. Two methods must be supported by the e_domain 1 ) The e_domain must be discretizable within the current grid. That means it should return arrays with points on grid lines that intersect the e_domain's boundaries 2) With the list of intersections, it is possible to identify the intersected elements. These two methods would avoid a full search through all the grid points. Applicable: ----------- Boundary conditions including the shape function coefficient Local - element enrirchments (XFEM) Method result ------------- The method returns the ''' return self.geo_grid.get_intersected_cells(ls_function, ls_limits) def get_negative_elems(self, ls_function): return self.geo_grid.get_negative_cells(ls_function) def get_affected_elems(self, e_domain): ''' 3) It should have a notion of inside / outside to decide whether or not a point is to be included or not. ''' def get_enclosed_nodes(self, e_domain): ''' Get elements that are inside of the e_domain. The elements are not intersected by the boundaries of the e_domain. The e_domain must be at least the same dimension as the grid. The e_domain must have an operator inside/outside to decide about whether or not a point is included. The method returns an array of element numbers within the specified e_domain. ''' def get_subgrid(self, bounding_box): ''' Return subgrid with base_node number ''' def get_ls_value(self, X_pnt): # @TODO: make it work for 3d return self.ls_function.level_set_fn(X_pnt[0], X_pnt[1]) _grids = Property( depends_on='fets_eval.dof_r,fets_eval.geo_r,shape+,coord_min+,coord_max+,fets_eval.n_nodal_dofs,dof_offset,geo_transform') @cached_property def _get__grids(self): '''Regenerate grids based on the specification ''' # Check if the specifiers for both grids are identical # cell_grid = CellGrid( grid_cell_spec=self.dof_grid_spec, geo_transform=self.geo_transform, shape=self.shape, coord_min=self.coord_min, coord_max=self.coord_max ) _xdof_grid = DofCellGrid( cell_grid=cell_grid, n_nodal_dofs=self.n_nodal_dofs, dof_offset=self.dof_offset ) # if self.dof_r.all() != self.geo_r.all(): # # @todo check whether this is universally valid (tolerance of coordinate values) # if not array_equal(self.dof_r, self.geo_r): # if (self.dof_r.shape[0] != self.geo_r.shape[0])or\ # ((self.dof_r.shape == self.geo_r.shape) and\ # linalg.norm(self.dof_r - self.geo_r) > 1.e-3) : # If the geometry grid differs from the dof_grid specifier # construct a separate cell grid, otherwise reuse the dof_grid cell_grid = CellGrid(grid_cell_spec=self.geo_grid_spec, geo_transform=self.geo_transform, shape=self.shape, coord_min=self.coord_min, coord_max=self.coord_max) _xgeo_grid = GeoCellGrid(cell_grid=cell_grid) return (_xdof_grid, _xgeo_grid) traits_view = View(Item('name'), Item('n_dofs'), Item('geo_transform@'), Item('dof_offset'), Item('fe_cell_array'), Item('prev_grid'), Item('fets_eval@', resizable=True), resizable=True, scrollable=True, width=0.5, height=0.5, )
class RTraceDomainList(HasTraits): label = Str('RTraceDomainField') sd = WeakRef(ISDomain) position = Enum('nodes', 'int_pnts') subfields = List def redraw(self): '''Delegate the calculation to the pipeline ''' # self.mvp_mgrid_geo.redraw() # 'label_scalars') self.mvp_mgrid_geo.rebuild_pipeline(self.vtk_node_structure) vtk_node_structure = Property(Instance(tvtk.UnstructuredGrid)) #@cached_property def _get_vtk_node_structure(self): self.position = 'nodes' return self.vtk_structure vtk_ip_structure = Property(Instance(tvtk.UnstructuredGrid)) #@cached_property def _get_vtk_ip_structure(self): self.position = 'int_pnts' return self.vtk_structure vtk_structure = Property(Instance(tvtk.UnstructuredGrid)) def _get_vtk_structure(self): ug = tvtk.UnstructuredGrid() cell_array, cell_offsets, cell_types = self.vtk_cell_data n_cells = cell_types.shape[0] ug.points = self.vtk_X vtk_cell_array = tvtk.CellArray() vtk_cell_array.set_cells(n_cells, cell_array) ug.set_cells(cell_types, cell_offsets, vtk_cell_array) return ug vtk_X = Property def _get_vtk_X(self): point_arr_list = [] for sf in self.subfields: if sf.skip_domain: continue sf.position = self.position sf_vtk_X = sf.vtk_X if sf_vtk_X.shape[0] == 0: # all elem are deactivated continue point_arr_list.append(sf_vtk_X) if len(point_arr_list) > 0: # print 'point_arr_list ', point_arr_list return vstack(point_arr_list) else: return zeros((0, 3), dtype='float_') # point offset to use when more fields are patched together within # RTDomainList point_offset = Int(0) # cell offset to use when more fields are patched together within # RTDomainList cell_offset = Int(0) vtk_cell_data = Property def _get_vtk_cell_data(self): cell_array_list = [] cell_offset_list = [] cell_types_list = [] point_offset = self.point_offset cell_offset = self.cell_offset for sf in self.subfields: if sf.skip_domain: continue sf.position = self.position sf.point_offset = point_offset sf.cell_offset = cell_offset cell_array, cell_offsets, cell_types = sf.vtk_cell_data cell_array_list.append(cell_array) cell_offset_list.append(cell_offsets) cell_types_list.append(cell_types) point_offset += sf.n_points cell_offset += cell_array.shape[0] if len(cell_array_list) > 0: cell_array = hstack(cell_array_list) cell_offsets = hstack(cell_offset_list) cell_types = hstack(cell_types_list) else: cell_array = array([], dtype='int_') cell_offsets = array([], dtype='int_') cell_types = array([], dtype='int_') return (cell_array, cell_offsets, cell_types) #------------------------------------------------------------------------- # Visualization pipelines #------------------------------------------------------------------------- mvp_mgrid_geo = Trait(MVUnstructuredGrid) # def _mvp_mgrid_geo_default(self): # return MVUnstructuredGrid( name = 'Response tracer mesh', # points = self.vtk_r, # cell_data = self.vtk_cell_data, # ) def _mvp_mgrid_geo_default(self): return MVUnstructuredGrid(name='Response tracer mesh', warp=False, warp_var='') view = View(resizable=True)
class TStepperEval(HasTraits): """ Base class for time stepper evaluators (TSE). Each time stepper classes implement the methods evaluating the governing equations of the simulated problem at a discrete time instance t. The time-steppers can be chained in order to reflect the hierarchical structure of spatial integration starting from the domain and using embedded loops over the finite elements, layers and material points. Furthermore, the time-step-evals specify the variables that can be evaluated for the provided spatial context and state array (rte_dict) attribute. """ implements(ITStepperEval) tstepper = WeakRef('ibvpy.core.tstepper.TStepper') def get_state_array_size(self): ''' Get the array representing the physical state of an object. ''' return 0 def setup(self, sctx): ''' Setup the spatial context and state array ''' pass def apply_constraints(self, K): ''' Apply implicity constraints associated with internal mappings within the time stepper evaluator. This method is used by hierarchical representation of the domain with local refinements and subdomains. ''' pass def get_corr_pred(self, sctx, u, tn, tn1): ''' Get the corrector and predictor. ''' raise NotImplementedError def identify_parameters(self): ''' Extract the traits that are floating points and can be associated with a statistical distribution. ''' params = {} for name, trait in self.traits().items(): if trait.trait_type.__class__ is Float: params[name] = trait return params def register_mv_pipelines(self, e): ''' Register the visualization pipelines in mayavi engine (empty by default) ''' pass # Resp-trace-evals # rte_dict = Trait(Dict) def _rte_dict_default(self): return {}
class Viz3D(HasStrictTraits): '''Base class for visualization objects. Each state and operator objects like crease pattern or constraint can define provide tailored visualizations transferring the information into a view objects shared within a particular forming task or a whole forming process. ''' label = Str('default') '''Label of the visualization object. ''' anim_t_start = PrototypedFrom('vis3d') anim_t_end = PrototypedFrom('vis3d') order = Int(1, auto_set=False, enter_set=True) '''Deprecated -- is only here to have a control parameter that avoids text visualization at the beginning of the time line because then mlab fails. ''' vis3d = WeakRef(Visual3D) '''Link to the visual object to transform into the forming_task_view3d. ''' vis3d_changed = Event '''Event registering changes in the source object. ''' ftv = WeakRef '''Folding task view3d object. ''' pipes = Dict() def register(self, ftv): '''Construct the visualization within the forming task view3d object. ''' ftv.viz3d_dict[self.label] = self return def plot(self): '''Plot the object within ftv ''' return hidden = Bool(False) def _show(self): if self.hidden == True: self.show() self.hidden = False def _hide(self): if self.hidden == False: self.hide() self.hidden = True def hide(self): for pipe in list(self.pipes.values()): pipe.visible = False def show(self): for pipe in list(self.pipes.values()): pipe.visible = True def update_t(self, anim_t=0.0, vot=0.0): '''Update with regard to the global time line. ''' if anim_t >= self.anim_t_start and anim_t <= self.anim_t_end \ or self.anim_t_end < 0.0: self._show() self.update(vot) else: self._hide() def update(self, vot=0.0): '''Update the visualization within the view3d object. ''' return min_max = Property '''Bounding box limits set to none by default. ''' def _get_min_max(self): return None, None viz3d_view = tui.Group(tui.Item('hode', resizable=True))
class FEGridLevelSetSlice(HasTraits): # Link to the source grid to slice # fe_grid = WeakRef('ibvpy.mesh.fe_grid.FEGrid') # Specification of further methods to be provided by the fe_grid_cell # # @todo - # 1) describe the functionality of the methods # 2) decide where to implement the details of the method # Generally, the lookup for elements should be directed # to the geo_grid, while the lookup for individual degrees # of freedom should go into the dof_grid # ls_function_eval = Str ls_spec_list = Property(Tuple, depends_on='ls_function_eval') @cached_property def _get_ls_spec_list(self): fn_list = self.ls_function_eval.split(';') fns = [] lim = [] for fn in fn_list: try: afn, limits = fn.split('@') except: # convenience - of there is just one fn, user does not have to add region afn = fn limits = '1' fns.append(afn.upper()) # transform to uppercase lim.append(limits) # limits.upper() ) return fns, lim ls_fns = Property def _get_ls_fns(self): return self.ls_spec_list[0] ls_lims = Property def _get_ls_lims(self): return self.ls_spec_list[1] def ls_function(self, X=None, Y=None, Z=None): ''' get_value() evaluates the value for all functions as defined in set_fns, will be 0 if grid point is not defined in any function ''' val = 0.0 for fn, limit in zip(self.ls_fns, self.ls_lims): if eval(limit): val = eval(fn) return val def ls_mask_function(self, X=None, Y=None, Z=None): '''Return True if the point is within one of the ranges of the supplied ls functions. ''' masked = True for limit in self.ls_lims: if eval(limit): if masked == False: raise ValueError( 'overlapping level set limits - two level sets defined' ) masked = False return masked # tip_elems = Property(Array(int)) def _get_tip_elems(self): '''Get the elements at the tip of the level set function The current implementation does not consider the association between the ls_mask function and the ls_function. This is probably not correct. The tip elems should be removed from the level set alone. ''' ls_mask = self.fe_grid.get_ls_mask(self.ls_mask_function) # get the nodes having True value masked_nodes = np.argwhere(ls_mask) # find out the element associated with these nodes neighbour_idx = np.array([[-1, -1], [-1, 0], [0, -1], [0, 0]], dtype=int) # get for each node the neighbors e_arr = masked_nodes[:, None, :] + neighbour_idx[None, :, :] # get for each node the corresponding neighbors e_flattened = [ e_arr[:, :, idx].flatten() for idx in range(e_arr.shape[2]) ] e_masked = np.c_[tuple(e_flattened)] # find the intersection with the level set intersected_elem_offsets = self.fe_grid.get_intersected_elems( self.ls_function) # get the indices of the intersected by the level set # this should be done more efficiently e_ls = np.array([ self.fe_grid.geo_grid.cell_grid.get_cell_idx(offset) for offset in intersected_elem_offsets ], dtype=int) # make a broadcasted comparison between the masked elements # (elements containing a node outside outside the level set domain) # and elements intersected by the level set. # broadcasted_compare = (e_ls == e_masked[:, None, :]) # along the axis 2 - there is a comparison of the coordinates # - if all are True, then all coordinates are equal - this # is detected by the boolean product (logical and along axis 2) # logical_and_along_ax2 = broadcasted_compare.prod(axis=2, dtype=bool) # along the axis 0 - there is a each-with-each comparison # - if one of the entries is True, then the second list contains # the same entry as well. This is detected by the sum operator # (logical or along axis 0) logical_or_along_ax0 = logical_and_along_ax2.sum(axis=0, dtype=bool) #res = ( e_ls == e_masked[:, None, :] ).prod( axis = 2, dtype = bool ).sum( axis = 0, dtype = bool ) return e_ls[logical_or_along_ax0] elems = Property(Array(int)) def _get_elems(self): # 1. for each ls_function identify the intersected elements # 2. choose those lying within the ls_domain - including the tip # elements. return self.fe_grid.get_intersected_elems(self.ls_function, self.ls_mask_function) neg_elems = Property(Array(Int)) def _get_neg_elems(self): return self.fe_grid.get_negative_elems(self.ls_function) pos_elems = Property(Array(Int)) def _get_pos_elems(self): # return self.fe_grid.get_positive_elems() raise NotImplementedError dof_nodes_values = Property(Array(Float)) @cached_property def _get_dof_nodes_values(self): #nodes = self.dof_grid.cell_node_map[ self.get_intersected_elems() ] # print "map ", nodes #coords = self.dof_grid.get_cell_points(self.get_intersected_elems()) coords = self.dof_X # @todo - make the dimensional dependency # ls_fn = np.frompyfunc(self.ls_function, 2, 1) o_shape = coords.shape # save shape for later if coords.shape[2] == 1: X = coords.reshape(o_shape[0] * o_shape[1], o_shape[2]).T Y = np.zeros_like(X) elif coords.shape[2] == 2: X, Y = coords.reshape(o_shape[0] * o_shape[1], o_shape[2]).T values = ls_fn(X, Y) return values.reshape(o_shape[0], o_shape[1]) geo_X = Property def _get_geo_X(self): '''Get global coordinates geometrical points of the intersected elements. ''' return self.fe_grid.geo_grid.get_cell_point_X_arr(self.elems) geo_x = Property def _get_geo_x(self): return self.fe_grid.geo_grid.get_cell_point_x_arr(self.elems) dof_X = Property def _get_dof_X(self): '''Get global coordinates field points of the intersected elements. ''' return self.fe_grid.dof_grid.get_cell_point_X_arr(self.elems) r_i = Property(Array(float)) # 2 points/element @cached_property def _get_r_i(self): ''' Return local coordinates of the intersection points Assuming rectangular parametric coordinates Works for 1D and 2D ''' i_elements = self.fe_grid.get_intersected_elems(self.ls_function) el_pnts = [] for elem in i_elements: inter_pts = [] # X_mtx = self.elements[elem].get_X_mtx() # skips deactivated # elements X_mtx = self.fe_grid.geo_grid.get_cell_point_X_arr(elem) dim = X_mtx.shape[1] # TODO:merge 1 and 2d if dim == 1: r_coord = self._get_intersect_pt(self.ls_fn_r, (0., X_mtx)) if r_coord != None: inter_pts.append([r_coord]) elif dim == 2: for c_coord in [-1., 1.]: args = (c_coord, X_mtx) s_coord = self._get_intersect_pt(self.ls_fn_s, args) r_coord = self._get_intersect_pt(self.ls_fn_r, args) if s_coord != None: inter_pts.append([c_coord, s_coord]) if r_coord != None: inter_pts.append([r_coord, c_coord]) elif dim == 3: raise NotImplementedError('not available for 3D yet') el_pnts.append(inter_pts) return np.array(el_pnts) dofs = Property def _get_dofs(self): '''Get number of affected DOFs @todo - reshape dofs so that they are of dimension ( elem, node, dim ) ''' dofs = [] for elem in self.elems: dof_map = self.fe_grid.dof_grid.get_cell_dofs(elem).flatten() dofs.append(dof_map) return np.array(dofs) i_neg_dofs = Property def _get_i_neg_dofs(self): '''Get intersected dofs with negative value of the level set at their position. ''' shape = self.dofs.shape n_node_dofs = shape[1] / self.dof_X.shape[1] return self.dofs.reshape(shape[0], self.dof_X.shape[1], n_node_dofs)[self.dof_nodes_values < 0.] neg_dofs = Property def _get_neg_dofs(self): '''Get dofs with negative value of the level set at their position. ''' dofs = [] for elem in self.neg_elems: dof_map = self.fe_grid.dof_grid.get_cell_dofs(elem).flatten() dofs.append(dof_map) if dofs == []: return np.array(dofs) return np.hstack(dofs) def _get_intersect_pt(self, fn, args): try: return brentq(fn, -1, 1, args=args) except ValueError: return def ls_fn_r(self, r, s, X_mtx): # TODO:dimensionless treatment X_pnt = self.fe_grid.fets_eval.map_r2X([r, s], X_mtx) if X_pnt.shape[0] == 1: Y = 0. else: Y = X_pnt[1] return self.ls_function(X_pnt[0], Y) def ls_fn_s(self, s, r, X_mtx): X_pnt = self.fe_grid.fets_eval.map_r2X([r, s], X_mtx) return self.ls_function(X_pnt[0], X_pnt[1]) def ls_fn_X(self, X, Y): return self.ls_function(X, Y)
class StyleManager(HasStrictTraits): implements(IStyleNodeData) component = WeakRef('enaml.widgets.component.Component') style_sheet = DelegatesTo('parent') style_node = DelegatesTo('parent') listen = Bool(False) @on_trait_change('style_sheet') def sheet_changed(self, sheet): if self.listen: self.apply() @on_trait_change('style_sheet:updated') def sheet_updated(self, tags): if self.listen: self.update_tags(tags) def apply(self, set_listen=True): style_sheet = self.style_sheet tags = set(style_sheet.get_tags()) self.update_tags(tags) if set_listen: self.listen = True def update_tags(self, tags): style_sheet = self.style_sheet style_node = self.style_node for tag in tags: if hasattr(style_node, tag): val = style_sheet.get_property(tag, self) if val is not NO_STYLE: setattr(style_node, tag, val) #-------------------------------------------------------------------------- # IStyleNodeData interface #-------------------------------------------------------------------------- def node_id(self): """ Returns the id of the element being styled as a string. """ return self.component.identifier def node_classes(self): """ Returns the classes of the element being styled as a space separated string. """ return self.style_node.style_class def node_type(self): """ Returns the type of the element being styled as a string. """ return self.component.type_name def parent_node(self): """ Returns an IStyleNodeData object for the parent element of the element being styled. """ return self.component.parent.style_manager
class ISliceProperty(HasStrictTraits): fe_grid = WeakRef() def __getitem__(self, idx): return FEGridNodeSlice(fe_grid=self.fe_grid, grid_slice=idx)
class TestClass(HasTraits): atr = WeakRef(Fruit) atr2 = WeakRef(Fruit()) atr3 = WeakRef("Fruit")