indexing description: "[ An object representing an axis-aligned rectangular subspace of R2. ]" author: "" date: "$Date$" revision: "$Revision$" class EM_INTERVAL_2D_REF inherit DOUBLE_MATH export {NONE} all end create make_from_tuple, make_from_reference, make_from_intervals, make, default_create convert make_from_tuple ({TUPLE [EM_INTERVAL, EM_INTERVAL]}) feature -- Initialization make_from_tuple (t: TUPLE [x, y: EM_INTERVAL]) is -- Make 2D interval from tuple do x := t.x y := t.y ensure set: x = t.x and y = t.y end make_from_reference (other: like Current) is -- do x := other.x y := other.y ensure set: x = other.x and y = other.y end make (p1, p2: like min) is -- Set boundary to be hull of values `p1' and `p2' do x.set (p1.x, p2.x) y.set (p1.y, p2.y) ensure -- set: ... containment: contains_value (p1) and contains_value (p2) end make_from_intervals (x_interval: like x; y_interval: like y) -- Set interval for each axis directly. do x := x_interval y := y_interval ensure set: x_interval = x and y_interval = y end feature -- Access x: EM_INTERVAL -- Projection of subspace onto x-axis y: EM_INTERVAL -- Projection of subspace onto y-axis feature -- Measurement min: EM_VECTOR2D is -- Lower boundary for each axis do Result.set (x.min, y.min) ensure definition: Result = [x.min, y.min] end max: like min is -- Upper boundary for each axis do Result.set (x.max, y.max) ensure definition: Result = [x.max, y.max] end size: like min is -- Size for each axis do Result := max - min ensure definition: Result = max - min end center: like min is -- Midpoint between `min' and `max' do Result.set (x.center, y.center) ensure definition: Result = [x.center, y.center] end width: DOUBLE is -- Size of `x' interval do Result := x.size ensure definition: Result = x.size end height: DOUBLE is -- Size of `y' interval do Result := y.size ensure definition: Result = y.size end feature -- Element change set_x (interval: like x) is -- Set `x' interval directly do x := interval ensure set: x = interval end set_y (interval: like y) is -- Set `y' interval directly do y := interval ensure set: y = interval end set_min (value: like min) is -- Set lower boundary for each axis require constraint: value.x <= max.x and value.y <= max.y do x.set_min (value.x) y.set_min (value.y) ensure set: min = value end set_max (value: like max) is -- Set upper boundary for each axis require constraint: value.x >= min.x and value.y >= min.y do x.set_max (value.x) y.set_max (value.y) ensure set: max = value end feature -- Basic queries contains_value (value: like min): BOOLEAN is -- Is `value' contained within current interval? do Result := x.contains_value (value.x) and y.contains_value (value.y) ensure end fully_contains (other: like Current): BOOLEAN is -- Is `other' interval fully contained within current interval? do Result := contains_value (other.min) and contains_value (other.max) ensure end intersects, partially_contains (other: like Current): BOOLEAN is -- Does `other' interval intersect with current? do Result := x.intersects_interval (other.x) and y.intersects_interval (other.y) ensure end feature -- Basic operations align_value (value: like min; alignment: EM_ALIGNMENT_2D): like min is -- Aligned `value' with `other' interval according to alignment do Result.set ( x.align_value (value.x, alignment.x), y.align_value (value.y, alignment.y) ) end clamp_value (value: like min): like min is -- Return element of interval with shortest distance to `value' do Result.set ( x.clamp_value (value.x), y.clamp_value (value.y) ) ensure contains_result: contains_value (Result) end feature -- Transformation accommodate_value (value: like min) is -- Expand current interval by as much as needed to accomodate -- `value'. No shrinking is allowed do x.accommodate_value (value.x) y.accommodate_value (value.y) ensure containment: contains_value (value) end accommodate_interval (other: like Current) is -- Expand current interval by as much as needed to accomodate -- `other' interval. No shrinking is allowed do accommodate_value (other.min) accommodate_value (other.max) ensure containment: fully_contains (other) end align_with (other: like Current; alignment: EM_ALIGNMENT_2D) is -- Align current interval with `other' interval according to -- `alignment' do x.align_with (other.x, alignment.x) y.align_with (other.y, alignment.y) ensure -- aligned_with_other: ... end align_with_value (value: like min; alignment: EM_ALIGNMENT_2D) is -- Align current interval with `value' according to `alignment' do x.align_with_value (value.x, alignment.x) y.align_with_value (value.y, alignment.y) end clamp_with (other: like Current) is -- Clamp boundaries of current interval to fit within `other' -- interval. May result in empty interval with size = 0 if -- `current' lies completely outside `other' interval. do x.clamp_with (other.x) y.clamp_with (other.y) ensure containment: other.fully_contains (Current) end resize (new_size: like size) is -- Set `size' to absolute value of `new_size' do x.resize (new_size.x) y.resize (new_size.y) end rotate (an_angle: DOUBLE; an_origin: like min) is -- Rotate bounding box by `an_angle' in radians around `an_origin' -- and fit boundaries to rotated bounding box (box may grow!) local c, s: DOUBLE p1, p2, p3, p4, pmin, pmax: like min do -- It could be so EASY p1 := min - an_origin p2 := max - an_origin p3 := [x.min, y.max] - an_origin p4 := [x.max, y.min] - an_origin c := cosine (an_angle) s := sine (an_angle) p1.set (c * p1.x - s * p1.y, s * p1.x + c * p1.y) p2.set (c * p2.x - s * p2.y, s * p2.x + c * p2.y) p3.set (c * p3.x - s * p3.y, s * p3.x + c * p3.y) p4.set (c * p4.x - s * p4.y, s * p4.x + c * p4.y) pmin := p1 pmin.ew_minimum (p2) pmin.ew_minimum (p3) pmin.ew_minimum (p4) pmax := p1 pmax.ew_maximum (p2) pmax.ew_maximum (p3) pmax.ew_maximum (p4) make (pmin + an_origin, pmax + an_origin) end pad (a_padding: like size) is -- Add `a_padding' to both sides of interval by adjusting -- both `min' and `max' such that `size' will always be positive do x.pad (a_padding.x) y.pad (a_padding.y) end aligned_resize (new_size: like size; alignment: EM_ALIGNMENT_2D) is -- Resize current interval to `new_size', and align result with -- old boundaries according to `alignment'. -- If `alignment'.is_user_specified, this operation is equal -- to a normal resize do x.aligned_resize (new_size.x, alignment.x) y.aligned_resize (new_size.y, alignment.y) end translate (distance: like min) -- Translate current interval by `distance' do x.translate (distance.x) y.translate (distance.y) ensure -- same_size: old size = size -- definition: min = old min + distance and max = old max + distance end invariant -- already covered by EM_INTERVAL for x, and y end