Skip to main content

physdes/
merge_obj.rs

1use crate::generic::MinDist;
2use crate::interval::{Enlarge, Intersect};
3use crate::point::Point;
4use std::cmp;
5
6/// Represents a merge object that encapsulates a point with coordinates of type T1 and T2.
7///
8/// The MergeObj struct is used for geometric operations such as distance calculation,
9/// enlargement, intersection, and merging with other merge objects.
10///
11/// # Examples
12///
13/// ```
14/// use physdes::merge_obj::MergeObj;
15///
16/// let merge_obj = MergeObj::new(3, 4);
17/// let internal_point = merge_obj.get_impl();
18/// assert_eq!(internal_point.xcoord, 3);
19/// assert_eq!(internal_point.ycoord, 4);
20/// ```
21#[derive(PartialEq, Eq, Copy, Clone, Hash, Debug, Default)]
22pub struct MergeObj<T1, T2> {
23    impl_: Point<T1, T2>,
24}
25
26impl<T1, T2> MergeObj<T1, T2> {
27    /// Creates a new `MergeObj` with the given x and y coordinates.
28    ///
29    /// # Arguments
30    ///
31    /// * `xcoord` - The x-coordinate value
32    /// * `ycoord` - The y-coordinate value
33    ///
34    /// # Examples
35    ///
36    /// ```
37    /// use physdes::merge_obj::MergeObj;
38    /// let obj = MergeObj::new(3, 4);
39    /// assert_eq!(obj.get_impl().xcoord, 3);
40    /// ```
41    pub const fn new(xcoord: T1, ycoord: T2) -> MergeObj<T1, T2> {
42        MergeObj {
43            impl_: Point::new(xcoord, ycoord),
44        }
45    }
46
47    /// Constructs a `MergeObj<i32, i32>` from raw coordinates by applying
48    /// the transform `(x+y, x-y)` to the internal point:
49    ///
50    /// $$(x', y') = (x + y,\; x - y)$$
51    ///
52    /// This transform maps the point into a rotated coordinate space used
53    /// for Manhattan-distance-based merging operations.
54    ///
55    /// # Arguments
56    ///
57    /// * `xcoord` - The x-coordinate in the original space
58    /// * `ycoord` - The y-coordinate in the original space
59    ///
60    /// # Examples
61    ///
62    /// ```
63    /// use physdes::merge_obj::MergeObj;
64    /// let obj = MergeObj::<i32, i32>::construct(4, 5);
65    /// let internal = obj.get_impl();
66    /// assert_eq!(internal.xcoord, 9);  // 4 + 5
67    /// assert_eq!(internal.ycoord, -1); // 4 - 5
68    /// ```
69    pub const fn construct(xcoord: i32, ycoord: i32) -> MergeObj<i32, i32> {
70        let impl_ = Point::new(xcoord + ycoord, xcoord - ycoord);
71        MergeObj { impl_ }
72    }
73
74    /// Returns a reference to the internal Point of the MergeObj
75    ///
76    /// # Examples
77    ///
78    /// ```
79    /// use physdes::merge_obj::MergeObj;
80    ///
81    /// let merge_obj = MergeObj::new(3, 4);
82    /// let internal_point = merge_obj.get_impl();
83    /// assert_eq!(internal_point.xcoord, 3);
84    /// assert_eq!(internal_point.ycoord, 4);
85    /// ```
86    pub fn get_impl(&self) -> &Point<T1, T2> {
87        &self.impl_
88    }
89}
90
91impl<T1, T2> MergeObj<T1, T2>
92where
93    T1: MinDist<T1>,
94    T2: MinDist<T2>,
95{
96    /// Computes the minimum Manhattan distance between two `MergeObj` values.
97    ///
98    /// Returns the Chebyshev distance in rotated space, corresponding to
99    /// Manhattan distance in the original space:
100    ///
101    /// $$d = \max(|x_1 - x_2|,\; |y_1 - y_2|)$$
102    ///
103    /// # Arguments
104    ///
105    /// * `other` - The other merge object to measure distance to
106    ///
107    /// # Examples
108    ///
109    /// ```
110    /// use physdes::merge_obj::MergeObj;
111    /// let a = MergeObj::<i32, i32>::construct(0, 0);
112    /// let b = MergeObj::<i32, i32>::construct(3, 4);
113    /// assert_eq!(a.min_dist_with(&b), 7);
114    /// ```
115    pub fn min_dist_with(&self, other: &MergeObj<T1, T2>) -> u32 {
116        cmp::max(
117            self.impl_.xcoord.min_dist_with(&other.impl_.xcoord),
118            self.impl_.ycoord.min_dist_with(&other.impl_.ycoord),
119        )
120    }
121}
122
123impl<T1, T2> MergeObj<T1, T2>
124where
125    T1: MinDist<T1> + Enlarge<i32, Output = T1> + Intersect<T1, Output = T1>,
126    T2: MinDist<T2> + Enlarge<i32, Output = T2> + Intersect<T2, Output = T2>,
127{
128    /// Enlarges this merge object by a given margin, producing a new `MergeObj`
129    /// whose coordinates are expanded outward by `alpha` in all directions:
130    ///
131    /// $$x \to \[x - \alpha,\; x + \alpha\],\qquad y \to \[y - \alpha,\; y + \alpha\]$$
132    ///
133    /// # Arguments
134    ///
135    /// * `alpha` - The margin to add around each coordinate
136    pub fn enlarge_with(&self, alpha: i32) -> MergeObj<T1, T2> {
137        let xcoord = self.impl_.xcoord.enlarge_with(alpha);
138        let ycoord = self.impl_.ycoord.enlarge_with(alpha);
139        MergeObj::new(xcoord, ycoord)
140    }
141
142    /// Computes the intersection of this merge object with another.
143    ///
144    /// Returns a new `MergeObj` whose coordinates are the component-wise
145    /// intersection of the two operands.
146    ///
147    /// # Arguments
148    ///
149    /// * `other` - The other merge object to intersect with
150    pub fn intersect_with(&self, other: &MergeObj<T1, T2>) -> MergeObj<T1, T2> {
151        let point = self.impl_.intersect_with(&other.impl_);
152        MergeObj::new(point.xcoord, point.ycoord)
153    }
154
155    /// Merges this merge object with another by computing the midpoint
156    /// region between them.
157    ///
158    /// The merge is performed by:
159    /// 1. Computing the minimum distance $d$ between the two objects
160    /// 2. Enlarging each by a portion of that distance:
161    ///    $$\text{trr}_1 = \text{enlarge}(self,\; \alpha),\quad \text{trr}_2 = \text{enlarge}(other,\; d - \alpha)$$
162    /// 3. Intersecting the enlarged regions to find the merge result:
163    ///    $$\text{result} = \text{trr}_1 \cap \text{trr}_2$$
164    ///
165    /// This is the core operation of the DME (Deferred Merge Embedding) algorithm.
166    ///
167    /// # Arguments
168    ///
169    /// * `other` - The other merge object to merge with
170    pub fn merge_with(&self, other: &MergeObj<T1, T2>) -> MergeObj<T1, T2> {
171        let alpha = self.min_dist_with(other);
172        let half = alpha / 2;
173        let trr1 = self.enlarge_with(half as i32);
174        let trr2 = other.enlarge_with((alpha - half) as i32);
175        trr1.intersect_with(&trr2)
176    }
177}
178
179#[cfg(test)]
180mod test {
181    // #![allow(non_upper_case_globals)]
182
183    use super::*;
184    use crate::interval::Interval;
185    use crate::vector2::Vector2;
186
187    // use crate::generic::Overlap;
188    // use crate::interval::Interval;
189
190    // use core::i32;
191
192    #[test]
193    fn test_merge_obj() {
194        let obj1 = MergeObj::<i32, i32>::construct(4, 5);
195        let obj2 = MergeObj::<i32, i32>::construct(7, 9);
196
197        assert_ne!(obj1, obj2);
198        assert_eq!(obj1.min_dist_with(&obj2), 7);
199        // assert_eq!(min_dist(&obj1, &obj2), 7);
200    }
201
202    #[test]
203    fn test_merge() {
204        let obj1: MergeObj<Interval<i32>, Interval<i32>> =
205            MergeObj::new(Interval::new(200, 600), Interval::new(200, 600));
206        let obj2: MergeObj<Interval<i32>, Interval<i32>> =
207            MergeObj::new(Interval::new(500, 900), Interval::new(500, 900));
208        let merged = obj1.merge_with(&obj2);
209        println!("{:?}", merged);
210        assert_eq!(
211            merged,
212            MergeObj::new(Interval::new(500, 600), Interval::new(500, 600))
213        );
214    }
215
216    #[test]
217    fn test_merge_2() {
218        let mut obj1: MergeObj<Interval<i32>, Interval<i32>> =
219            MergeObj::new(Interval::new(4, 5), Interval::new(4, 5));
220        let obj2: MergeObj<Interval<i32>, Interval<i32>> =
221            MergeObj::new(Interval::new(7, 9), Interval::new(7, 9));
222        let vec = Vector2::new(Interval::new(2, 3), Interval::new(2, 3));
223        obj1.impl_.xcoord.lb += vec.x_.lb;
224        obj1.impl_.xcoord.ub += vec.x_.ub;
225        obj1.impl_.ycoord.lb += vec.y_.lb;
226        obj1.impl_.ycoord.ub += vec.y_.ub;
227        obj1.impl_.xcoord.lb -= vec.x_.lb;
228        obj1.impl_.xcoord.ub -= vec.x_.ub;
229        obj1.impl_.ycoord.lb -= vec.y_.lb;
230        obj1.impl_.ycoord.ub -= vec.y_.ub;
231        assert_eq!(
232            obj1,
233            MergeObj::new(Interval::new(4, 5), Interval::new(4, 5))
234        );
235        let result1 = obj1.enlarge_with(3);
236        assert_eq!(
237            result1,
238            MergeObj::new(Interval::new(1, 8), Interval::new(1, 8))
239        );
240        let result2 = obj2.enlarge_with(4);
241        assert_eq!(
242            result2,
243            MergeObj::new(Interval::new(3, 13), Interval::new(3, 13))
244        );
245        let result3 = result1.intersect_with(&result2);
246        assert_eq!(
247            result3,
248            MergeObj::new(Interval::new(3, 8), Interval::new(3, 8))
249        );
250    }
251
252    #[test]
253    fn test_min_dist_with_more_cases() {
254        let obj1 = MergeObj::<i32, i32>::construct(0, 0);
255        let obj2 = MergeObj::<i32, i32>::construct(3, 4);
256        assert_eq!(obj1.min_dist_with(&obj2), 7);
257
258        let obj3 = MergeObj::<i32, i32>::construct(-3, -4);
259        assert_eq!(obj1.min_dist_with(&obj3), 7);
260    }
261
262    #[test]
263    fn test_enlarge_with_more_cases() {
264        let obj1: MergeObj<Interval<i32>, Interval<i32>> =
265            MergeObj::new(Interval::new(200, 600), Interval::new(200, 600));
266        let enlarged = obj1.enlarge_with(100);
267        assert_eq!(
268            enlarged,
269            MergeObj::new(Interval::new(100, 700), Interval::new(100, 700))
270        );
271    }
272
273    #[test]
274    fn test_intersect_with_more_cases() {
275        let obj1: MergeObj<Interval<i32>, Interval<i32>> =
276            MergeObj::new(Interval::new(200, 600), Interval::new(200, 600));
277        let obj2: MergeObj<Interval<i32>, Interval<i32>> =
278            MergeObj::new(Interval::new(500, 900), Interval::new(500, 900));
279        let intersected = obj1.intersect_with(&obj2);
280        assert_eq!(
281            intersected,
282            MergeObj::new(Interval::new(500, 600), Interval::new(500, 600))
283        );
284
285        let obj3 = MergeObj::new(Interval::new(700, 900), Interval::new(700, 900));
286        let intersected2 = obj1.intersect_with(&obj3);
287        assert!(intersected2.impl_.xcoord.is_invalid());
288        assert!(intersected2.impl_.ycoord.is_invalid());
289    }
290
291    #[test]
292    fn test_merge_with_more_cases() {
293        let obj1: MergeObj<Interval<i32>, Interval<i32>> =
294            MergeObj::new(Interval::new(0, 100), Interval::new(0, 100));
295        let obj2: MergeObj<Interval<i32>, Interval<i32>> =
296            MergeObj::new(Interval::new(100, 200), Interval::new(100, 200));
297        let merged = obj1.merge_with(&obj2);
298        assert_eq!(
299            merged,
300            MergeObj::new(Interval::new(100, 100), Interval::new(100, 100))
301        );
302    }
303
304    #[test]
305    fn test_get_impl() {
306        let mo = MergeObj::new(3, 5);
307        let impl_ref = mo.get_impl();
308        assert_eq!(impl_ref.xcoord, 3);
309        assert_eq!(impl_ref.ycoord, 5);
310    }
311}