note description: "[ Every figure has a center. The position of the center are the values x and y. It is the position on the screen. The center of the figure can be moved arround and the figure can be rotated around its center and scaled in x and y direction. 2*pi is a full rotation. The direction of a rotation is clockwise. ]" legal: "See notice at end of class." status: "See notice at end of class." date: "$Date$" revision: "$Revision$" deferred class EV_MODEL inherit EV_MODEL_DOUBLE_MATH export {NONE} all {ANY} generating_type undefine default_create end HASHABLE rename hash_code as id undefine default_create end EV_ABSTRACT_PICK_AND_DROPABLE undefine is_equal, copy redefine default_create, create_interface_objects end feature {NONE} -- initialization create_interface_objects do create internal_invalid_rectangle create center Precursor {EV_ABSTRACT_PICK_AND_DROPABLE} end default_create -- Initialize a figure. do is_show_requested := True internal_is_sensitive := True is_center_valid := False are_events_sent_to_group := True create_interface_objects Precursor {EV_ABSTRACT_PICK_AND_DROPABLE} assign_draw_id set_deny_cursor (default_deny_cursor) set_accept_cursor (default_accept_cursor) end feature -- Access x: INTEGER -- `X' position of the center on the screen. do if not is_center_valid then set_center end Result := center.x end y: INTEGER -- `Y' position of the center on the screen . do if not is_center_valid then set_center end Result := center.y end angle: DOUBLE -- `Current' has to be rotated around (`x',`y') for -`angle' -- to be in upright position. Upright position has to -- be defined for every figure. If a figure is in upright position -- and you scale it to `x' direction, the figure should be -- scaled in the direction of the `x' direction of the screen. deferred end point_count: INTEGER -- Number of points needed to describe `Current'. -- Without center. do Result := point_array.count ensure correct: Result = point_array.count end id: INTEGER -- Unique id. do if internal_hash_id = 0 then counter.put (counter.item + 1) internal_hash_id := counter.item end Result := internal_hash_id end pebble: detachable ANY -- Data to be transported by pick and drop mechanism. pebble_function: detachable FUNCTION [detachable ANY] -- Returns data to be transported by pick and drop mechanism. -- When not `Void', `pebble' is ignored. pointer_style: detachable EV_POINTER_STYLE -- Cursor displayed when pointer is over this figure. do if attached internal_pointer_style as l_internal_pointer_style then Result := l_internal_pointer_style elseif attached group as l_group then Result := l_group.pointer_style end end group: detachable EV_MODEL_GROUP -- The group `Current' is part of. Void if `Current' is -- not part of a group. world: detachable EV_MODEL_WORLD -- The world `Current' is part of. Void if `Current' is -- not part of a world do if attached {like world} group as l_world then Result := l_world elseif attached group as l_group then Result := l_group.world end end feature -- Visitor project (a_projector: EV_MODEL_DRAWING_ROUTINES) -- Project `Current' onto `a_projector'. deferred end feature -- Element change set_x (a_x: INTEGER) -- Set `x' to `an_x'. local a_delta_x: INTEGER l_point_array: SPECIAL [EV_COORDINATE] l_coordinate: EV_COORDINATE i, nb: INTEGER do a_delta_x := a_x - x if a_delta_x /= 0 then l_point_array := point_array from i := 0 nb := l_point_array.count - 1 until i > nb loop l_coordinate := l_point_array.item (i) l_coordinate.set_x_precise (l_coordinate.x_precise + a_delta_x) i := i + 1 end if is_in_group and then attached group as l_group and then l_group.is_center_valid then l_group.center_invalidate end invalidate center.set_x (a_x) end ensure x_set: a_x = x center_valid: is_center_valid end set_y (a_y: INTEGER) -- Set `y' to `an_y'. local a_delta_y: INTEGER l_point_array: SPECIAL [EV_COORDINATE] l_coordinate: EV_COORDINATE i, nb: INTEGER do a_delta_y := a_y - y if a_delta_y /= 0 then l_point_array := point_array from i := 0 nb := point_array.count - 1 until i > nb loop l_coordinate := l_point_array.item (i) l_coordinate.set_y_precise (l_coordinate.y_precise + a_delta_y) i := i + 1 end if is_in_group and then attached group as l_group and then l_group.is_center_valid then l_group.center_invalidate end invalidate center.set_y (a_y) end ensure y_set: a_y = y center_valid: is_center_valid end set_x_y (a_x, a_y: INTEGER) -- Set `x' to `a_x' and `y' to `a_y'. local a_delta_y, a_delta_x: INTEGER l_point_array: SPECIAL [EV_COORDINATE] l_coordinate: EV_COORDINATE i, nb: INTEGER do a_delta_y := a_y - y a_delta_x := a_x - x if a_delta_y /= 0 or a_delta_x /= 0 then l_point_array := point_array from i := 0 nb := l_point_array.count - 1 until i > nb loop l_coordinate := l_point_array.item (i) l_coordinate.set_precise (l_coordinate.x_precise + a_delta_x, l_coordinate.y_precise + a_delta_y) i := i + 1 end if is_in_group and then attached group as l_group and then l_group.is_center_valid then l_group.center_invalidate end center.set (a_x, a_y) invalidate end ensure set: a_x = x and a_y = y center_valid: is_center_valid end rotate (an_angle: DOUBLE) -- Rotate around the center for `an_angle'. require is_rotatable: is_rotatable do if not is_center_valid then set_center end projection_matrix.rotate (an_angle, center.x_precise, center.y_precise) recursive_transform (projection_matrix) is_center_valid := True ensure center_valid: is_center_valid end rotate_around (an_angle: DOUBLE; ax, ay: INTEGER) -- Rotate around (`ax', `ay') for `an_angle'. require is_rotatable: is_rotatable do if not is_center_valid then set_center end projection_matrix.rotate (an_angle, ax, ay) recursive_transform (projection_matrix) is_center_valid := True ensure center_valid: is_center_valid end scale_x (a_scale_x: DOUBLE) -- Scale to x direction for `a_scale_x'. require is_scalable: is_scalable a_scale_x_bigger_zero: a_scale_x > 0.0 do if not is_center_valid then set_center end projection_matrix.scale (a_scale_x, 1, center.x_precise, center.y_precise, angle) recursive_transform (projection_matrix) if is_in_group and then attached group as l_group and then l_group.is_center_valid then l_group.center_invalidate end end scale_y (a_scale_y: DOUBLE) -- Scale to y direction for `a_scale_y'. require is_scalable: is_scalable a_scale_y_bigger_zero: a_scale_y > 0.0 do if not is_center_valid then set_center end projection_matrix.scale (1, a_scale_y, center.x_precise, center.y_precise, angle) recursive_transform (projection_matrix) if is_in_group and then attached group as l_group and then l_group.is_center_valid then l_group.center_invalidate end end scale (a_scale: DOUBLE) -- Scale to x and y direction for `a_scale'. require is_scalable: is_scalable a_scale_bigger_zero: a_scale > 0.0 do if not is_center_valid then set_center end projection_matrix.scale (a_scale, a_scale, center.x_precise, center.y_precise, angle) recursive_transform (projection_matrix) if is_in_group and then attached group as l_group and then l_group.is_center_valid then l_group.center_invalidate end end scale_x_abs (a_scale_x: DOUBLE) -- Scale absolute to x direction for `a_scale_x'. -- Do not rotate around `angle' first. require is_scalable: is_scalable a_scale_x_bigger_zero: a_scale_x > 0.0 do if not is_center_valid then set_center end projection_matrix.scale_abs (a_scale_x, 1, center.x_precise, center.y_precise) recursive_transform (projection_matrix) if is_in_group and then attached group as l_group and then l_group.is_center_valid then l_group.center_invalidate end end scale_y_abs (a_scale_y: DOUBLE) -- Scale to y direction for `a_scale_y'. -- Do not rotate around `angle' first. require is_scalable: is_scalable a_scale_y_bigger_zero: a_scale_y > 0.0 do if not is_center_valid then set_center end projection_matrix.scale_abs (1, a_scale_y, center.x_precise, center.y_precise) recursive_transform (projection_matrix) if is_in_group and then attached group as l_group and then l_group.is_center_valid then l_group.center_invalidate end end scale_abs (a_scale: DOUBLE) -- Scale to x and y direction for `a_scale'. -- Do not rotate around `angle' first. require is_scalable: is_scalable a_scale_bigger_zero: a_scale > 0.0 do if not is_center_valid then set_center end projection_matrix.scale_abs (a_scale, a_scale, center.x_precise, center.y_precise) recursive_transform (projection_matrix) if is_in_group and then attached group as l_group and then l_group.is_center_valid then l_group.center_invalidate end end transform (a_transformation: EV_MODEL_TRANSFORMATION) -- Transform all points in `point_array' using `a_transformation'. -- You can do any transformation you want. You can -- for example rotate `Current' around an other point -- than the center. require a_transformation_not_void: a_transformation /= Void is_transformable: is_transformable do recursive_transform (a_transformation) if is_in_group and then attached group as l_group and then l_group.is_center_valid then l_group.center_invalidate end end set_pebble (a_pebble: like pebble) -- Assign `a_pebble' to `pebble'. do pebble := a_pebble end remove_pebble -- Make `pebble' `Void' and `pebble_function' `Void, -- Removing transport. do pebble := Void pebble_function := Void end set_pebble_function (a_function: attached like pebble_function) -- Set `a_function' to compute `pebble'. do pebble_function := a_function end set_pointer_style (a_cursor: like pointer_style) -- Assign `a_cursor' to `pointer_style'. require a_cursor_not_void: a_cursor /= Void do internal_pointer_style := a_cursor ensure pointer_style_assigned: pointer_style = a_cursor end set_accept_cursor (a_cursor: detachable like accept_cursor) -- Set `a_cursor' to be displayed when the screen pointer is over a -- target that accepts `pebble' during pick and drop. do if a_cursor /= Void then accept_cursor := a_cursor else accept_cursor := default_accept_cursor end end set_deny_cursor (a_cursor: detachable like deny_cursor) -- Set `a_cursor' to be displayed when the screen pointer is not -- over a valid target. do if a_cursor /= Void then deny_cursor := a_cursor else deny_cursor := default_deny_cursor end end feature -- Status Report is_rotatable: BOOLEAN -- Is this figure rotatable? deferred end is_scalable: BOOLEAN -- Is this figure scalable? deferred end is_transformable: BOOLEAN -- Is this figure transformable no matter what kind of transformation? deferred end is_in_group: BOOLEAN -- Is `Current' part of a group? do Result := group /= Void ensure group_defines_is_in_group: Result = (group /= Void) end is_in_world: BOOLEAN -- Is `Current' part of a world? do Result := world /= Void ensure world_defines_is_in_world: Result = (world /= Void) end accept_cursor: EV_POINTER_STYLE -- Accept cursor set by user. -- To be displayed when the screen pointer is over a target that accepts -- `pebble' during pick and drop. deny_cursor: EV_POINTER_STYLE -- Deny cursor set by user. -- To be displayed when the screen pointer is not over a valid target. has_capture: BOOLEAN -- Are all events sent to `Current'? do if attached world as l_world then Result := l_world.capture_figure = Current end end is_show_requested: BOOLEAN -- Will `Current' be displayed when its parent is? is_sensitive: BOOLEAN -- Is object sensitive to user input? do if group = Void or else attached group as l_group and then l_group.is_sensitive then Result := internal_is_sensitive end end is_center_valid: BOOLEAN -- Is the position of the center valid? are_events_sent_to_group: BOOLEAN -- Are events for `pointer_motion_actions', `pointer_button_press_actions', -- `pointer_double_press_actions', `pointer_button_release_actions' -- `pointer_enter_actions' and `pointer_leave_actions' send to `Current's -- group (if any) even if `Current' catch the event. (Default True). feature -- Status settings enable_capture -- Grab all mouse events for `world'. require in_world: world /= Void local l_world: like world do l_world := world check l_world /= Void then end l_world.set_capture_figure (Current) ensure capture_set: has_capture end disable_capture -- Disable grab of all events on `world'. require in_world: world /= Void has_capture: has_capture local l_world: like world do l_world := world check l_world /= Void then end l_world.remove_capture_figure ensure capture_released: not has_capture end show -- Request that `Current' be displayed when its `group' is. -- `True' by default. do is_show_requested := True invalidate ensure is_show_requested: is_show_requested end hide -- Request that `Current' not be displayed even when its `group' is. do is_show_requested := False invalidate ensure not_is_show_requested: not is_show_requested end enable_sensitive -- Make object sensitive to user input. do internal_is_sensitive := True ensure sensitive_requested: internal_is_sensitive end disable_sensitive -- Make object non-sensitive to user input. do internal_is_sensitive := False ensure insensitive_requested: not internal_is_sensitive end disable_events_sended_to_group -- Set `are_events_sent_to_group' to False. do are_events_sent_to_group := False ensure events_blocked: not are_events_sent_to_group end enable_events_sended_to_group -- Set `are_events_sent_to_group' to True. do are_events_sent_to_group := True ensure events_sended_to_group: are_events_sent_to_group end feature -- Action sequences pointer_motion_actions: EV_POINTER_MOTION_ACTION_SEQUENCE -- Actions to be performed when screen pointer moves. do if internal_pointer_motion_actions = Void then create internal_pointer_motion_actions end Result := internal_pointer_motion_actions end conforming_pick_actions: EV_NOTIFY_ACTION_SEQUENCE -- Actions to be performed when a pebble that fits this hole is -- picked up from another source. -- (when drop_actions.accepts_pebble (pebble)) do if internal_conforming_pick_actions = Void then create internal_conforming_pick_actions end Result := internal_conforming_pick_actions end pick_actions: EV_PND_START_ACTION_SEQUENCE -- Actions to be performed when `pebble' is picked up. do if internal_pick_actions = Void then create internal_pick_actions end Result := internal_pick_actions end drop_actions: EV_PND_ACTION_SEQUENCE -- Actions to take when pick and drop transport drops on `Current'. do if internal_drop_actions = Void then create internal_drop_actions init_drop_actions (internal_drop_actions) end Result := internal_drop_actions end pointer_button_press_actions: EV_POINTER_BUTTON_ACTION_SEQUENCE -- Actions to be performed when screen pointer button is pressed. do if internal_pointer_button_press_actions = Void then create internal_pointer_button_press_actions end Result := internal_pointer_button_press_actions end pointer_double_press_actions: EV_POINTER_BUTTON_ACTION_SEQUENCE -- Actions to be performed when screen pointer is double clicked. do if internal_pointer_double_press_actions = Void then create internal_pointer_double_press_actions end Result := internal_pointer_double_press_actions end pointer_button_release_actions: EV_POINTER_BUTTON_ACTION_SEQUENCE -- Actions to be performed when screen pointer button is released. do if internal_pointer_button_release_actions = Void then create internal_pointer_button_release_actions end Result := internal_pointer_button_release_actions end pointer_enter_actions: EV_NOTIFY_ACTION_SEQUENCE -- Actions to be performed when screen pointer enters widget. do if internal_pointer_enter_actions = Void then create internal_pointer_enter_actions end Result := internal_pointer_enter_actions end pointer_leave_actions: EV_NOTIFY_ACTION_SEQUENCE -- Actions to be performed when screen pointer leaves widget. do if internal_pointer_leave_actions = Void then create internal_pointer_leave_actions end Result := internal_pointer_leave_actions end feature -- Events position_on_figure (a_x, a_y: INTEGER): BOOLEAN -- Is the point on (`a_x', `a_y') on this figure? --| Used to generate events. deferred end bounding_box: EV_RECTANGLE -- Smallest orthogonal rectangular area `Current' fits in. -- Every call returns a new instance. local l_point_array: SPECIAL [EV_COORDINATE] i, nb: INTEGER min_x, min_y, max_x, max_y, val: DOUBLE ax, ay, w, h: INTEGER l_item: EV_COORDINATE do if internal_bounding_box /= Void and then internal_bounding_box.has_area then Result := internal_bounding_box.twin else if point_count = 0 then create Result else l_point_array := point_array l_item := l_point_array.item (0) min_x := l_item.x_precise max_x := min_x min_y := l_item.y_precise max_y := min_y from i := 1 nb := l_point_array.count - 1 until i > nb loop l_item := l_point_array.item (i) val := l_item.x_precise max_x := max_x.max (val) min_x := min_x.min (val) val := l_item.y_precise max_y := max_y.max (val) min_y := min_y.min (val) i := i + 1 end ax := as_integer (min_x) ay := as_integer (min_y) w := as_integer (max_x) - ax + 1 h := as_integer (max_y) - ay + 1 create Result.make (ax, ay, w, h) end if internal_bounding_box /= Void then internal_bounding_box.copy (Result) else internal_bounding_box := Result.twin end end ensure Result_not_void: Result /= Void internal_is_twin: internal_bounding_box /= Result end update_rectangle_to_bounding_box (a_bbox: EV_RECTANGLE) -- Update `a_bbox' to match `bounding_box' of `Current'. do if internal_bounding_box /= Void and then internal_bounding_box.has_area then a_bbox.copy (internal_bounding_box) else a_bbox.copy (bounding_box) end end feature --{EV_FIGURE} -- Status settings center_invalidate -- The position of the center may have changed. do if is_center_valid then is_center_valid := False if is_in_group and then attached group as l_group and then l_group.is_center_valid then l_group.center_invalidate end end end feature {EV_MODEL, EV_MODEL_DRAWER} -- Access point_array: SPECIAL [EV_COORDINATE] -- All points of the Figure. invalidate -- Some property of `Current' has changed. do if attached internal_bounding_box then -- Reset to a zero size so that calls to `bounding_box' will recalculate. internal_bounding_box.move_and_resize (0, 0, 0, 0) end if attached last_update_rectangle then last_update_rectangle.move_and_resize (0, 0, 0, 0) end if valid then valid := False if is_in_group and then attached group as l_group then l_group.invalidate end end end validate -- `Current' has been updated by a projector. do if not valid then valid := True if internal_invalid_rectangle = Void then create internal_invalid_rectangle end update_rectangle_to_bounding_box (internal_invalid_rectangle) if not is_show_requested then internal_invalid_rectangle.move_and_resize (0, 0, 0, 0) end end end invalid_rectangle: detachable EV_RECTANGLE -- Area that needs erasing. do if not valid then Result := internal_invalid_rectangle if Result /= Void and then not Result.has_area then Result := Void end end end internal_invalid_rectangle: detachable EV_RECTANGLE note option: stable attribute end -- Area that needs updating. update_rectangle: detachable EV_RECTANGLE -- Area that needs redrawing. do if is_show_requested and then not valid then if last_update_rectangle = Void then create last_update_rectangle end Result := last_update_rectangle update_rectangle_to_bounding_box (Result) if not Result.has_area then Result := Void end end end real_pebble (a_x, a_y: INTEGER): detachable ANY -- Calculated `pebble'. do if attached pebble_function as l_pebble_function then Result := l_pebble_function.item ([a_x, a_y]) else Result := pebble end end feature {EV_MODEL, EV_MODEL_PROJECTOR, EV_MODEL_PROJECTION_ROUTINES} -- Access draw_id: INTEGER -- Used to look up drawing routine. -- Obsolete. This is nor marked obsolete because this would cause some warnings -- during the compilation of Vision. -- This is not used anymore. Redefine `project' to perform projections. last_update_rectangle: detachable EV_RECTANGLE note option: stable attribute end -- Last calculated bounding box when validate was called. feature {EV_MODEL, EV_MODEL_PROJECTION_ROUTINES} valid: BOOLEAN -- Is there no change to `Current'? feature {EV_MODEL_GROUP} -- Figure group set_group (a_group: detachable EV_MODEL_GROUP) -- Set `group' to `a_group' do if attached group as l_group and then l_group /= a_group then l_group.prune_all (Current) end group := a_group ensure group_set: group = a_group is_in_group_proper_defined: is_in_group = (a_group /= Void) end unreference_group -- Set group to Void after removal from it. -- Remove `Current' from `group'. require group_exists: group /= Void do group := Void ensure not_in_group: not is_in_group end recursive_transform (a_transformation: EV_MODEL_TRANSFORMATION) -- Same as transform but without precondition -- is_transformable and without invalidating -- groups center require a_transformation_exists: a_transformation /= Void local l_point_array: like point_array i, nb: INTEGER do from l_point_array := point_array i := 0 nb := l_point_array.count - 1 until i > nb loop a_transformation.project (l_point_array.item (i)) i := i + 1 end invalidate is_center_valid := False end feature {EV_MODEL_WIDGET_PROJECTOR} -- Implementation default_accept_cursor: EV_POINTER_STYLE -- Used in lieu of a user defined `accept_cursor'. once Result := Default_pixmaps.Standard_cursor end default_deny_cursor: EV_POINTER_STYLE -- Used in lieu of a user defined `deny_cursor'. once Result := Default_pixmaps.No_cursor end feature {NONE} -- Contract support all_points_exist (list: like point_array): BOOLEAN -- Are all items in `list' non-`Void'? require list_exists: list /= Void local i: INTEGER do Result := True from i := point_array.lower until i > point_array.upper or not Result loop Result := point_array.item (i) /= Void i := i + 1 end end feature {EV_MODEL_WIDGET_PROJECTOR} -- Internal action Sequence internal_pointer_motion_actions: detachable EV_POINTER_MOTION_ACTION_SEQUENCE note option: stable attribute end -- Implementation of once per object `pointer_motion_actions'. internal_pointer_button_press_actions: detachable EV_POINTER_BUTTON_ACTION_SEQUENCE note option: stable attribute end -- Implementation of once per object `pointer_button_press_actions'. internal_pointer_double_press_actions: detachable EV_POINTER_BUTTON_ACTION_SEQUENCE note option: stable attribute end -- Implementation of once per object `pointer_double_press_actions'. internal_pointer_button_release_actions: detachable EV_POINTER_BUTTON_ACTION_SEQUENCE note option: stable attribute end -- Implementation of once per object -- `pointer_button_release_actions'. internal_pointer_enter_actions: detachable EV_NOTIFY_ACTION_SEQUENCE note option: stable attribute end -- Implementation of once per object `pointer_enter_actions'. internal_pointer_leave_actions: detachable EV_NOTIFY_ACTION_SEQUENCE note option: stable attribute end -- Implementation of once per object `pointer_leave_actions'. internal_pick_actions: detachable EV_PND_START_ACTION_SEQUENCE note option: stable attribute end -- Implementation of once per object `pick_actions'. internal_conforming_pick_actions: detachable EV_NOTIFY_ACTION_SEQUENCE note option: stable attribute end -- Implementation of once per object `conforming_pick_actions'. internal_drop_actions: detachable EV_PND_ACTION_SEQUENCE note option: stable attribute end -- Implementation of once per object `drop_actions'. feature {NONE} -- Implementation internal_hash_id: INTEGER -- Unique id if not 0 counter: CELL [INTEGER] -- Id counter. once create Result.put (0) ensure counter_not_void: Result /= Void end projection_matrix: EV_MODEL_TRANSFORMATION -- The transformation used to rotate and scale. do if internal_projection_matrix = Void then create internal_projection_matrix.make_id end Result := internal_projection_matrix end internal_projection_matrix: detachable EV_MODEL_TRANSFORMATION note option: stable attribute end -- Internal projection matrix. internal_pointer_style: detachable EV_POINTER_STYLE -- `pointer_style'. internal_is_sensitive: BOOLEAN -- `is_sensitive'. default_colors: EV_STOCK_COLORS -- Eiffel Vision colors. once create Result end draw_id_counter: CELL [INTEGER] -- Last assigned `draw_id'. -- Obsolete. This is nor marked obsolete because this would cause some warnings -- during the compilation of Vision. -- This is not used anymore. Redefine `project' to perform projections. once create Result.put (1) ensure draw_id_count_not_void: Result /= Void end known_draw_ids: HASH_TABLE [INTEGER, STRING] -- Table of assigned `draw_id's, hashed by the -- generated types of EV_FIGURE descendants. -- Obsolete. This is nor marked obsolete because this would cause some warnings -- during the compilation of Vision. -- This is not used anymore. Redefine `project' to perform projections. once create Result.make (20) end assign_draw_id -- Obsolete. This is nor marked obsolete because this would cause some warnings -- during the compilation of Vision. -- This is not used anymore. Redefine `project' to perform projections. do known_draw_ids.search (generator) draw_id := known_draw_ids.found_item if draw_id = 0 then draw_id := draw_id_counter.item draw_id_counter.put (draw_id + 1) known_draw_ids.put (draw_id, generator) end end center: EV_COORDINATE -- Position of the center. set_center -- Set the position of the center deferred ensure center_valid: is_center_valid end internal_bounding_box: detachable EV_RECTANGLE note option: stable attribute end -- Used to speed up bounding box calculation. invariant point_array_exists: point_array /= Void center_exists: center /= Void x_is_center_x: is_center_valid implies x = center.x y_is_center_y: is_center_valid implies y = center.y all_points_exist: all_points_exist (point_array) projection_matrix_not_void: projection_matrix /= Void is_transfomable_implies_rotatable_and_scalable: is_transformable implies (is_rotatable and is_scalable) note copyright: "Copyright (c) 1984-2015, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software 5949 Hollister Ave., Goleta, CA 93117 USA Telephone 805-685-1006, Fax 805-685-6869 Website http://www.eiffel.com Customer support http://support.eiffel.com ]" end -- class EV_MODEL