19928/jj_vitp/jj_override/ev_model_group.e

1081 lines
22 KiB
Plaintext
Raw Permalink Normal View History

2024-06-17 07:09:33 +00:00
note
description: "[
A EV_FIGURE_GROUP is an ARRAYED_LIST of EV_FIGURE.
Since EV_FIGURE_GROUP is also an EV_FIGURE (composite pattern) you
can rotate, scale and change the position of an EV_FIGURE_GROUP.
All elements in the group are rotated around the center
of the EV_FIGURE_GROUP. A EV_FIGURE can only be grouped
in one group at the same time.
]"
legal: "See notice at end of class."
status: "See notice at end of class."
date: "$Date: 2014-06-03 19:49:45 -0400 (Tue, 03 Jun 2014) $"
revision: "$Revision: 44 $"
class
EV_MODEL_GROUP
inherit
EV_MODEL
undefine
point_count
redefine
rotate,
create_interface_objects,
default_create,
recursive_transform,
set_x,
set_y,
set_x_y,
invalid_rectangle,
update_rectangle,
invalidate,
validate,
bounding_box
end
ARRAYED_LIST [EV_MODEL]
rename
make as list_make
export
{NONE} put_i_th
undefine
is_equal,
copy
redefine
append,
force,
extend,
replace,
insert,
prune_all,
wipe_out,
remove,
merge_left,
merge_right,
make_from_array,
default_create,
swap,
has,
make_filled,
list_make,
-- I added these
at,
last,
first
end
EV_MODEL_SINGLE_POINTED
undefine
default_create
end
create
default_create,
make_with_point,
make_with_position
create {EV_MODEL_GROUP}
list_make,
make_filled
feature {NONE} -- Initialization
create_interface_objects
-- <Precursor>
do
create lookup_table.make (initiale_size)
create point_array.make_empty (1)
make_empty_area (initiale_size)
Precursor {EV_MODEL}
end
default_create
-- Create an empty EV_MODEL_GROUP.
do
Precursor {EV_MODEL}
point_array.extend (create {EV_COORDINATE}.make (0, 0))
index := 0
is_grouped := True
ensure then
is_grouped: is_grouped
not_is_in_group: not is_in_group
end
make_filled (n: INTEGER_32)
-- <Precursor>
do
default_create
Precursor {ARRAYED_LIST} (n)
end
list_make (n: INTEGER)
-- <Precursor>
do
default_create
Precursor {ARRAYED_LIST} (n)
end
feature -- Access
angle: DOUBLE
-- `Current' has to be rotated around (`x',`y') for -`angle'
-- to be in upright position.
do
Result := current_angle
ensure then
Result_equal_current_angle: Result = current_angle
end
point_x: INTEGER
-- x position of `point'.
do
Result := point_array.item (0).x
end
point_y: INTEGER
-- y position of `point'.
do
Result := point_array.item (0).y
end
deep_elements: LIST [EV_MODEL]
-- All elements in `Current' and in its subgroups.
local
l_group: detachable EV_MODEL_GROUP
l_list: ARRAYED_LIST [EV_MODEL]
do
create l_list.make (0)
from
start
until
after
loop
l_group ?= item
if l_group /= Void then
l_list.append (l_group.deep_elements)
end
l_list.extend (item)
forth
end
Result := l_list
ensure
Result_not_Void: Result /= Void
end
at alias "@" (i: INTEGER): like item assign put_i_th
-- Item at `i'-th position, redefined by jjj to conform to `item'
do
check attached {like item} Precursor {ARRAYED_LIST} (i) as v then
Result := v
end
end
first: like item
-- Item at first position, redefined by jjj to conform to `item'
do
check attached {like item} Precursor {ARRAYED_LIST} as v then
Result := v
end
end
last: like item
-- Item at last position, redefined by jjj to conform to `item'
do
check attached {like item} Precursor {ARRAYED_LIST} as v then
Result := v
end
end
feature -- Status report
is_rotatable: BOOLEAN
-- Is this figure group rotatable?
local
l_area: like area
i, nb: INTEGER
do
if is_grouped then
Result := True
l_area := area
from
i := 0
nb := count - 1
until
i > nb or not Result
loop
Result := l_area.item (i).is_rotatable
i := i + 1
end
else
Result := True
end
end
is_scalable: BOOLEAN
-- Is this figure group scalable?
local
l_area: like area
i, nb: INTEGER
do
if is_grouped then
Result := True
l_area := area
from
i := 0
nb := count - 1
until
i > nb or not Result
loop
Result := l_area.item (i).is_scalable
i := i + 1
end
else
Result := True
end
end
is_transformable: BOOLEAN
-- Is this figure group transformable?
local
l_area: like area
i, nb: INTEGER
do
if is_grouped then
Result := True
l_area := area
from
i := 0
nb := count - 1
until
i > nb or not Result
loop
Result := l_area.item (i).is_transformable
i := i + 1
end
else
Result := True
end
end
is_grouped: BOOLEAN
-- Is grouped?
has (v: like item): BOOLEAN
-- Does current include `v'?
-- (based on v.id)
do
if v /= Void then
Result := lookup_table.has (v.id)
end
end
has_deep (figure: EV_MODEL): BOOLEAN
-- Does any item contains `figure'?
local
grp: detachable EV_MODEL_GROUP
do
from
start
until
after or Result
loop
grp ?= item
if grp = Void then
Result := figure = item
elseif grp = figure then
Result := True
else
Result := grp.has_deep (figure)
end
forth
end
end
invalid_rectangle: detachable EV_RECTANGLE
-- Rectangle that needs erasing.
-- `Void' if no change is made.
local
r: detachable EV_RECTANGLE
l_area: like area
i, nb: INTEGER
do
if not valid then
from
l_area := area
i := 0
nb := count - 1
until
i > nb
loop
r := l_area [i].invalid_rectangle
if r /= Void and then r.has_area and then l_area [i].is_show_requested then
if Result = Void then
if internal_invalid_rectangle = Void then
create internal_invalid_rectangle
end
Result := internal_invalid_rectangle
Result.copy (r)
else
Result.merge (r)
end
end
i := i + 1
end
end
end
update_rectangle: detachable EV_RECTANGLE
-- Rectangle that needs redrawing.
-- `Void' if no change is made.
local
r: detachable EV_RECTANGLE
l_area: like area
i, nb: INTEGER
do
if not valid and then is_show_requested and then is_grouped then
from
l_area := area
i := 0
nb := count - 1
until
i > nb
loop
r := l_area [i].update_rectangle
if r /= Void then
if Result = Void then
if last_update_rectangle = Void then
-- Reuse `local_update_rectangle'
create last_update_rectangle
end
Result := last_update_rectangle
Result.copy (r)
else
Result.merge (r)
end
end
i := i + 1
end
end
end
feature -- Visitor
project (a_projector: EV_MODEL_DRAWING_ROUTINES)
-- <Precursor>
do
across Current as ic_models loop
if ic_models.item.is_show_requested then
ic_models.item.project (a_projector)
end
end
end
feature -- Element change
set_x (a_x: INTEGER)
-- Set `x' to `an_x'.
local
a_delta_x: INTEGER
do
a_delta_x := a_x - x
if a_delta_x /= 0 then
projection_matrix.translate (a_delta_x, 0)
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
center.set_x (a_x)
is_center_valid := True
end
set_y (a_y: INTEGER)
-- Set `y' to `an_y'.
local
a_delta_y: INTEGER
do
a_delta_y := a_y - y
if a_delta_y /= 0 then
projection_matrix.translate (0, a_delta_y)
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
center.set_y (a_y)
is_center_valid := True
end
set_x_y (a_x, a_y: INTEGER)
-- Set `x' to `an_x'.
local
a_delta_x, a_delta_y: INTEGER
do
a_delta_x := a_x - x
a_delta_y := a_y - y
if a_delta_x /= 0 or a_delta_y /= 0 then
projection_matrix.translate (a_delta_x, a_delta_y)
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
center.set (a_x, a_y)
is_center_valid := True
end
set_point_position (a_x, a_y: INTEGER)
-- Set position of `point' to (`a_x', `a_y').
local
a_delta_x, a_delta_y: DOUBLE
do
a_delta_x := a_x - point_array [0].x_precise
a_delta_y := a_y - point_array [0].y_precise
if a_delta_x /= 0 or a_delta_y /= 0 then
projection_matrix.translate (a_delta_x, a_delta_y)
recursive_transform (projection_matrix)
if is_center_valid then
center_invalidate
end
end
end
ungroup
-- Ungroup all `figures'.
require
is_grouped: is_grouped
not_is_in_group: not is_in_group
do
from
start
until
after
loop
item.set_group (Void)
forth
end
is_grouped := False
current_angle := 0
center_invalidate
ensure
is_ungrouped: not is_grouped
none_is_in_a_group: not there_exists (agent {EV_MODEL}.is_in_group)
center_is_invalid: not is_center_valid
end
regroup
-- Group the `figures' in the group.
require
not_is_grouped: not is_grouped
not_is_in_group: not is_in_group
do
from
start
until
after
loop
item.set_group (Current)
forth
end
is_grouped := True
center_invalidate
ensure
is_grouped: is_grouped
all_grouped: for_all (agent {EV_MODEL}.is_in_group)
center_is_invalid: not is_center_valid
end
-- jjj send_backward (a_figure: EV_MODEL)
send_backward (a_figure: like item) -- so I could redefine `item' in my worlds
-- Send `a_figure' one layer backwards.
require
a_figure_in_figures: has (a_figure)
do
if first /= a_figure then
start
search (a_figure)
swap (index - 1)
end
full_redraw
ensure
a_figure_in_group: a_figure.group = Current
a_figure_in_current: has (a_figure)
end
-- jjj bring_forward (a_figure: EV_MODEL)
bring_forward (a_figure: like item) -- so I could redefine `item' in my worlds
-- Bring `a_figure' one layer forwards.
require
a_figure_in_figures: has (a_figure)
do
if last /= a_figure then
start
search (a_figure)
swap (index + 1)
end
full_redraw
ensure
a_figure_in_group: a_figure.group = Current
a_figure_in_current: has (a_figure)
end
-- jjj send_to_back (a_figure: EV_MODEL)
send_to_back (a_figure: like item)
-- Send `a_figure' to the bottom most layer.
-- I redefined this because `item' gets redefined in
-- my classes.
require
a_figure_in_figures: has (a_figure)
do
if first /= a_figure then
start
search (a_figure)
remove
put_front (a_figure)
end
full_redraw
ensure
is_at_front: first = a_figure
a_figure_in_group: a_figure.group = Current
a_figure_in_current: has (a_figure)
end
-- jjj bring_to_front (a_figure: EV_MODEL)
bring_to_front (a_figure: like item)
-- Bring `a_figure' to the top most layer
-- I redefined this because `item' gets redefined in
-- my classes.
require
a_figure_in_figures: has (a_figure)
do
if last /= a_figure then
start
search (a_figure)
remove
extend (a_figure)
end
full_redraw
ensure
is_last: last = a_figure
a_figure_in_group: a_figure.group = Current
a_figure_in_current: has (a_figure)
end
-- jjj added feature
send_to_index (fig: like item; a_index: INTEGER)
-- Feature added to this override class that places `a_figure'
-- at the position indicated by `a_index'
require
a_figure_in_figures: has (fig)
do
if a_index <= 1 then
send_to_back (fig)
elseif a_index >= count then
bring_to_front (fig)
else
start
search (fig)
remove
-- The following code was copied from `extend'; instead of
-- callin Precursor {ARRAYED_LIST} we use `put_i_th'
lookup_table.put (fig, fig.id)
-- Precursor {ARRAYED_LIST} (fig)
go_i_th (a_index)
put_left (fig)
fig.set_group (Current)
center_invalidate
invalidate
full_redraw
end
end
rotate (an_angle: DOUBLE)
-- Rotate around the center for `an_angle'.
do
current_angle := current_angle + an_angle
Precursor {EV_MODEL} (an_angle)
ensure then
angle_equal_an_angle: angle = old angle + an_angle
end
feature -- List change
insert (fig: like item; i: INTEGER)
-- Add `fig' to the group.
do
lookup_table.put (fig, fig.id)
Precursor {ARRAYED_LIST} (fig, i)
fig.set_group (Current)
center_invalidate
invalidate
full_redraw
ensure then
fig_in_lookup_table: fig /= Void implies lookup_table.has (fig.id)
fig_in_group: fig /= Void implies fig.group = Current
end
extend (fig: like item)
-- Add `fig' to the group.
do
lookup_table.put (fig, fig.id)
Precursor {ARRAYED_LIST} (fig)
fig.set_group (Current)
center_invalidate
invalidate
full_redraw
ensure then
fig_in_lookup_table: fig /= Void implies lookup_table.has (fig.id)
fig_in_group: fig /= Void implies fig.group = Current
end
force (fig: like item)
-- Add `fig' to the group.
do
lookup_table.put (fig, fig.id)
Precursor {ARRAYED_LIST} (fig)
fig.set_group (Current)
center_invalidate
invalidate
full_redraw
ensure then
fig_in_lookup_table: fig /= Void implies lookup_table.has (fig.id)
fig_in_group: fig /= Void implies fig.group = Current
end
replace (fig: like item)
-- Replace current item by `fig'.
do
item.unreference_group
lookup_table.remove (item.id)
Precursor {ARRAYED_LIST} (fig)
fig.set_group (Current)
lookup_table.put (fig, fig.id)
center_invalidate
invalidate
full_redraw
ensure then
fig_in_lookup_table: fig /= Void implies lookup_table.has (fig.id)
item_not_in_lookup_table: not lookup_table.has (old item.id)
fig_in_group: fig /= Void implies fig.group = Current
item_not_in_group: not (old item).is_in_group
end
remove
-- Remove `item' from figure.
do
item.unreference_group
lookup_table.remove (item.id)
Precursor {ARRAYED_LIST}
center_invalidate
invalidate
full_redraw
ensure then
item_not_in_lookup_table: not lookup_table.has (old item.id)
item_not_in_group: not (old item).is_in_group
end
prune_all (fig: like item)
-- Remove `fig' from the group.
do
if has (fig) then
fig.unreference_group
lookup_table.remove (fig.id)
end
Precursor {ARRAYED_LIST} (fig)
center_invalidate
invalidate
full_redraw
ensure then
item_not_in_lookup_table: fig /= Void implies not lookup_table.has (fig.id)
end
merge_left (other: ARRAYED_LIST [EV_MODEL])
-- Merge `other' into group before cursor.
-- `other' will be empty afterwards.
do
insert_list_to_table (other)
change_group (other)
Precursor {ARRAYED_LIST} (other)
center_invalidate
invalidate
full_redraw
end
merge_right (other: ARRAYED_LIST [EV_MODEL])
-- Merge `other' into group after cursor.
-- `other' will be empty afterwards.
do
insert_list_to_table (other)
change_group (other)
Precursor {ARRAYED_LIST} (other)
center_invalidate
invalidate
full_redraw
end
wipe_out
-- Remove all items.
do
from start until after loop
if item.is_in_group then
item.unreference_group
end
forth
end
Precursor {ARRAYED_LIST}
lookup_table.wipe_out
center_invalidate
invalidate
full_redraw
end
append (s: SEQUENCE [EV_MODEL])
-- append (s: SEQUENCE [like item])
-- Append a copy of `s'.
local
l: like s
l_cursor: CURSOR
do
if s = Current then
l := s.twin
else
l := s
end
from
resize (count + s.count)
l_cursor := cursor
l.start
until
l.exhausted
loop
extend (l.item)
lookup_table.put (l.item, l.item.id)
l.forth
end
go_to (l_cursor)
center_invalidate
invalidate
full_redraw
end
-- jjj make_from_array (a: ARRAY [EV_MODEL])
make_from_array (a: ARRAY [like item])
-- Create list from array `a'.
local
i: INTEGER
do
wipe_out
resize (a.count)
from
i := a.lower
until
i > a.upper
loop
if a.item (i) /= Void then
extend (a.item (i))
end
i := i + 1
end
center_invalidate
invalidate
full_redraw
end
swap (i: INTEGER)
-- Exchange item at `i'-th position with item
-- at cursor position.
local
old_item: like item
do
old_item := item
Precursor {ARRAYED_LIST} (i)
lookup_table.put (old_item, old_item.id)
old_item.set_group (Current)
ensure then
item_in_lookup_table: lookup_table.has (old item.id)
i_in_lookup_table: lookup_table.has (i_th (i).id)
item_in_group: (old item).group = Current
i_th_in_group: i_th (i).group = Current
end
feature -- Status settings
invalidate
-- Some property of `Current' has changed.
local
l_area: like area
i, nb: INTEGER
do
if valid then
Precursor {EV_MODEL}
from
l_area := area
i := 0
nb := count - 1
until
i > nb
loop
if l_area [i].valid then
l_area [i].invalidate
end
i := i + 1
end
else
Precursor {EV_MODEL}
end
end
validate
-- Validate `Current'.
local
l_area: like area
i, nb: INTEGER
l_figure: EV_MODEL
l_rect: detachable EV_RECTANGLE
do
if not valid then
if count > 0 then
create l_rect
l_area := area
from
i := 0
nb := count - 1
until
i > nb
loop
l_figure := l_area.item (i)
if not l_figure.valid then
l_figure.validate
end
i := i + 1
end
if world = Current then
-- We do not want the origin of the world to be included in the update.
l_rect := calculated_bounding_box
if l_rect = Void then
create l_rect
end
else
update_rectangle_to_bounding_box (l_rect)
end
if internal_invalid_rectangle /= Void then
internal_invalid_rectangle.copy (l_rect)
else
internal_invalid_rectangle := l_rect
end
elseif internal_invalid_rectangle /= Void then
-- Reset any previous invalid rectangle.
internal_invalid_rectangle.move_and_resize (0, 0, 0, 0)
end
valid := True
end
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.
-- Always returns `False', but descendants can override
-- it to improve efficiency.
do
Result := False
end
bounding_box: EV_RECTANGLE
-- Smallest orthogonal rectangular area `Current' fits in.
local
l_result: detachable EV_RECTANGLE
do
if attached internal_bounding_box as l_internal_bounding_box and then l_internal_bounding_box.has_area then
Result := l_internal_bounding_box.twin
else
l_result := calculated_bounding_box
if world = Current then
-- If `Current' is the world then we need then we need the origin to be included so that its size is remembered.
create Result.make (point_x, point_y, 0, 0)
if l_result /= Void then
Result.merge (l_result)
end
else
if l_result /= Void then
Result := l_result
else
create Result
end
end
if attached internal_bounding_box as l_internal_bounding_box then
l_internal_bounding_box.copy (Result)
else
internal_bounding_box := Result.twin
end
end
end
feature {EV_MODEL_GROUP} -- Figure group
recursive_transform (a_transformation: EV_MODEL_TRANSFORMATION)
-- Same as transform but without precondition
-- is_transformable and without invalidating
-- groups center
local
l_area: like area
i, nb: INTEGER
do
a_transformation.project (point_array.item (0))
if is_grouped then
from
i := 0
nb := count - 1
l_area := area
until
i > nb
loop
l_area.item (i).recursive_transform (a_transformation)
i := i + 1
end
end
invalidate
is_center_valid := False
end
feature {NONE} -- Implementation
calculated_bounding_box: detachable EV_RECTANGLE
-- Smallest orthogonal rectangular area `Current' fits in.
do
if is_grouped then
create Result
update_rectangle_to_calculated_bounding_box (Result)
end
end
update_rectangle_to_calculated_bounding_box (a_rectangle: EV_RECTANGLE)
-- Smallest orthogonal rectangular area `Current' fits in.
local
l_area: like area
i, nb: INTEGER
l_bbox: like internal_bounding_box
l_initial: BOOLEAN
do
if is_grouped then
from
create l_bbox
l_initial := True
l_area := area
i := 0
nb := count - 1
until
i > nb
loop
if l_area.item (i).is_show_requested then
l_area [i].update_rectangle_to_bounding_box (l_bbox)
if l_bbox.width > 0 and then l_bbox.height > 0 then
if l_initial then
a_rectangle.copy (l_bbox)
l_initial := False
else
a_rectangle.merge (l_bbox)
end
end
end
i := i + 1
end
end
end
current_angle: DOUBLE
-- The rotating angle.
set_center
-- Set x and y such that they are in the
-- center of the group.
local
l_bbox: like bounding_box
do
if is_grouped then
l_bbox := bounding_box
center.set_precise (l_bbox.left + l_bbox.width /2, l_bbox.top + l_bbox.height / 2)
else
center.set (0, 0)
end
is_center_valid := True
end
initiale_size: INTEGER = 5
-- Initialize size of `Current'.
change_group (other: ARRAYED_LIST [EV_MODEL])
-- Change group of all figures in `other' to Current.
-- Used by `merge_left' and `merge_right'.
local
n: INTEGER
do
from
n := 1
until
n > other.count
loop
other.i_th (n).set_group (Current)
n := n + 1
end
end
full_redraw
-- Request `invalid_rectangle' to be ignored.
local
w: detachable EV_MODEL_WORLD
do
w := world
if w /= Void then
w.full_redraw
end
end
lookup_table: HASH_TABLE [EV_MODEL, INTEGER]
-- Lookup table to search faster.
insert_list_to_table (list: ARRAYED_LIST [EV_MODEL])
-- Insert list element to lookup_table.
do
from
list.start
until
list.after
loop
lookup_table.put (list.item, list.item.id)
list.forth
end
end
invariant
is_grouped_implies_all_grouped: is_grouped implies for_all (agent {EV_MODEL}.is_in_group)
angle_equal_current_angle: angle = current_angle
not_is_grouped_implies_angel_equals_zero: not is_grouped implies (angle = 0)
note
copyright: "Copyright (c) 1984-2006, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
356 Storke Road, 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_GROUP