1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.ink.geometry
18 
19 import androidx.annotation.FloatRange
20 import androidx.annotation.RestrictTo
21 import kotlin.math.max
22 import kotlin.math.min
23 
24 /**
25  * A mutable axis-aligned rectangle. See [ImmutableBox] for an immutable version.
26  *
27  * Note that unlike [android.graphics.RectF], this does not express an opinion about axis direction
28  * (e.g. the positive Y axis being "down"), because it is intended to be used with any coordinate
29  * system rather than just Android screen/View space.
30  */
31 public class MutableBox private constructor(x1: Float, y1: Float, x2: Float, y2: Float) : Box() {
32 
33     /** The lower bound in the `X` direction. */
34     override var xMin: Float = min(x1, x2)
35         private set
36 
37     /** The lower bound in the `Y` direction. */
38     override var yMin: Float = min(y1, y2)
39         private set
40 
41     /** The upper bound in the `X` direction. */
42     override var xMax: Float = max(x1, x2)
43         private set
44 
45     /** The upper bound in the `Y` direction. */
46     override var yMax: Float = max(y1, y2)
47         private set
48 
49     /**
50      * Sets the lower and upper bounds in the `X` direction to new values. The minimum value becomes
51      * `xMin`, and the maximum value becomes `xMax`. Returns the same instance to chain function
52      * calls.
53      */
setXBoundsnull54     public fun setXBounds(x1: Float, x2: Float): MutableBox {
55         xMin = min(x1, x2)
56         xMax = max(x1, x2)
57         return this
58     }
59 
60     /**
61      * Sets the lower and upper bounds in the `Y` direction to new values. The minimum value becomes
62      * `yMin`, and the maximum value becomes `yMax`. Returns the same instance to chain function
63      * calls.
64      */
setYBoundsnull65     public fun setYBounds(y1: Float, y2: Float): MutableBox {
66         yMin = min(y1, y2)
67         yMax = max(y1, y2)
68         return this
69     }
70 
71     /**
72      * Constructs a [MutableBox] without any initial data. Fill with the appropriate setters or
73      * factory functions.
74      */
75     public constructor() : this(0f, 0f, 0f, 0f)
76 
77     /** Constructs the smallest [MutableBox] containing the two given points. */
populateFromTwoPointsnull78     public fun populateFromTwoPoints(point1: Vec, point2: Vec): MutableBox {
79         setXBounds(point1.x, point2.x)
80         setYBounds(point1.y, point2.y)
81         return this
82     }
83 
84     /**
85      * Constructs a [MutableBox] with a given [center], [width], and [height]. [width] and [height]
86      * must be non-negative numbers.
87      */
populateFromCenterAndDimensionsnull88     public fun populateFromCenterAndDimensions(
89         center: Vec,
90         @FloatRange(from = 0.0) width: Float,
91         @FloatRange(from = 0.0) height: Float,
92     ): MutableBox {
93         require(width >= 0f && height >= 0f)
94         setXBounds(center.x - width / 2, center.x + width / 2)
95         setYBounds(center.y - height / 2, center.y + height / 2)
96         return this
97     }
98 
99     /** Fills this [MutableBox] with the same values contained in [input]. */
populateFromnull100     public fun populateFrom(input: Box): MutableBox {
101         xMin = input.xMin
102         yMin = input.yMin
103         xMax = input.xMax
104         yMax = input.yMax
105         return this
106     }
107 
108     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
asImmutablenull109     override fun asImmutable(): ImmutableBox =
110         ImmutableBox(x1 = xMin, y1 = yMin, x2 = xMax, y2 = yMax)
111 
112     override fun equals(other: Any?): Boolean =
113         other === this || (other is Box && Box.areEquivalent(this, other))
114 
115     // NOMUTANTS -- not testing exact hashCode values, just that equality implies same hashCode
116     override fun hashCode(): Int = Box.hash(this)
117 
118     override fun toString(): String = "Mutable${Box.string(this)}"
119 }
120