Skip to content

Commit ac73a1e

Browse files
committed
Add AbsoluteContinuousRoundedRectangle
1 parent 9cc05d9 commit ac73a1e

File tree

7 files changed

+320
-4
lines changed

7 files changed

+320
-4
lines changed

app/release/app-release.apk

-5 Bytes
Binary file not shown.
0 Bytes
Binary file not shown.
-1 Bytes
Binary file not shown.
Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
package com.kyant.capsule
2+
3+
import androidx.annotation.FloatRange
4+
import androidx.annotation.IntRange
5+
import androidx.compose.foundation.shape.CornerBasedShape
6+
import androidx.compose.foundation.shape.CornerSize
7+
import androidx.compose.foundation.shape.ZeroCornerSize
8+
import androidx.compose.runtime.Immutable
9+
import androidx.compose.runtime.Stable
10+
import androidx.compose.ui.geometry.Size
11+
import androidx.compose.ui.geometry.toRect
12+
import androidx.compose.ui.graphics.Outline
13+
import androidx.compose.ui.unit.Dp
14+
import androidx.compose.ui.unit.LayoutDirection
15+
import androidx.compose.ui.unit.dp
16+
import androidx.compose.ui.util.fastCoerceIn
17+
import kotlin.math.min
18+
19+
@Immutable
20+
open class AbsoluteContinuousRoundedRectangle(
21+
topLeft: CornerSize,
22+
topRight: CornerSize,
23+
bottomRight: CornerSize,
24+
bottomLeft: CornerSize,
25+
open val continuity: Continuity = Continuity.Default
26+
) : CornerBasedShape(
27+
topStart = topLeft,
28+
topEnd = topRight,
29+
bottomEnd = bottomRight,
30+
bottomStart = bottomLeft
31+
) {
32+
33+
override fun createOutline(
34+
size: Size,
35+
topStart: Float,
36+
topEnd: Float,
37+
bottomEnd: Float,
38+
bottomStart: Float,
39+
layoutDirection: LayoutDirection
40+
): Outline {
41+
// rectangle
42+
if (topStart + topEnd + bottomEnd + bottomStart == 0f) {
43+
return Outline.Rectangle(size.toRect())
44+
}
45+
46+
val maxRadius = min(size.width, size.height) * 0.5f
47+
val topLeft = topStart.fastCoerceIn(0f, maxRadius)
48+
val topRight = topEnd.fastCoerceIn(0f, maxRadius)
49+
val bottomRight = bottomEnd.fastCoerceIn(0f, maxRadius)
50+
val bottomLeft = bottomStart.fastCoerceIn(0f, maxRadius)
51+
52+
return continuity.createRoundedRectangleOutline(
53+
size = size,
54+
topLeft = topLeft,
55+
topRight = topRight,
56+
bottomRight = bottomRight,
57+
bottomLeft = bottomLeft
58+
)
59+
}
60+
61+
override fun copy(
62+
topStart: CornerSize,
63+
topEnd: CornerSize,
64+
bottomEnd: CornerSize,
65+
bottomStart: CornerSize
66+
): AbsoluteContinuousRoundedRectangle {
67+
return AbsoluteContinuousRoundedRectangle(
68+
topLeft = topStart,
69+
topRight = topEnd,
70+
bottomRight = bottomEnd,
71+
bottomLeft = bottomStart,
72+
continuity = continuity
73+
)
74+
}
75+
76+
fun copy(
77+
topLeft: CornerSize = this.topStart,
78+
topRight: CornerSize = this.topEnd,
79+
bottomRight: CornerSize = this.bottomEnd,
80+
bottomLeft: CornerSize = this.bottomStart,
81+
continuity: Continuity = this.continuity
82+
): AbsoluteContinuousRoundedRectangle {
83+
return AbsoluteContinuousRoundedRectangle(
84+
topLeft = topLeft,
85+
topRight = topRight,
86+
bottomRight = bottomRight,
87+
bottomLeft = bottomLeft,
88+
continuity = continuity
89+
)
90+
}
91+
92+
override fun equals(other: Any?): Boolean {
93+
if (this === other) return true
94+
if (other !is AbsoluteContinuousRoundedRectangle) return false
95+
96+
if (topStart != other.topStart) return false
97+
if (topEnd != other.topEnd) return false
98+
if (bottomEnd != other.bottomEnd) return false
99+
if (bottomStart != other.bottomStart) return false
100+
if (continuity != other.continuity) return false
101+
102+
return true
103+
}
104+
105+
override fun hashCode(): Int {
106+
var result = topStart.hashCode()
107+
result = 31 * result + topEnd.hashCode()
108+
result = 31 * result + bottomEnd.hashCode()
109+
result = 31 * result + bottomStart.hashCode()
110+
result = 31 * result + continuity.hashCode()
111+
return result
112+
}
113+
114+
override fun toString(): String {
115+
return "AbsoluteContinuousRoundedRectangle(topLeft=$topStart, topRight=$topEnd, bottomRight=$bottomEnd, " +
116+
"bottomLeft=$bottomStart, continuity=$continuity)"
117+
}
118+
}
119+
120+
@Stable
121+
val AbsoluteContinuousRectangle: AbsoluteContinuousRoundedRectangle = AbsoluteContinuousRectangleImpl()
122+
123+
@Suppress("FunctionName")
124+
@Stable
125+
fun AbsoluteContinuousRectangle(continuity: Continuity = Continuity.Default): AbsoluteContinuousRoundedRectangle =
126+
AbsoluteContinuousRectangleImpl(continuity)
127+
128+
@Immutable
129+
private data class AbsoluteContinuousRectangleImpl(
130+
override val continuity: Continuity = Continuity.Default
131+
) : AbsoluteContinuousRoundedRectangle(
132+
topLeft = ZeroCornerSize,
133+
topRight = ZeroCornerSize,
134+
bottomRight = ZeroCornerSize,
135+
bottomLeft = ZeroCornerSize,
136+
continuity = continuity
137+
) {
138+
139+
override fun toString(): String {
140+
return "AbsoluteContinuousRectangle(continuity=$continuity)"
141+
}
142+
}
143+
144+
private val FullCornerSize = CornerSize(50)
145+
146+
@Stable
147+
val AbsoluteContinuousCapsule: AbsoluteContinuousRoundedRectangle = AbsoluteContinuousCapsule()
148+
149+
@Suppress("FunctionName")
150+
@Stable
151+
fun AbsoluteContinuousCapsule(continuity: Continuity = Continuity.Default): AbsoluteContinuousRoundedRectangle =
152+
AbsoluteContinuousCapsuleImpl(continuity)
153+
154+
@Immutable
155+
private data class AbsoluteContinuousCapsuleImpl(
156+
override val continuity: Continuity = Continuity.Default
157+
) : AbsoluteContinuousRoundedRectangle(
158+
topLeft = FullCornerSize,
159+
topRight = FullCornerSize,
160+
bottomRight = FullCornerSize,
161+
bottomLeft = FullCornerSize,
162+
continuity = continuity
163+
) {
164+
165+
override fun createOutline(
166+
size: Size,
167+
topStart: Float,
168+
topEnd: Float,
169+
bottomEnd: Float,
170+
bottomStart: Float,
171+
layoutDirection: LayoutDirection
172+
): Outline = continuity.createCapsuleOutline(size)
173+
174+
override fun toString(): String {
175+
return "AbsoluteContinuousCapsule(continuity=$continuity)"
176+
}
177+
}
178+
179+
@Stable
180+
fun AbsoluteContinuousRoundedRectangle(
181+
corner: CornerSize,
182+
continuity: Continuity = Continuity.Default
183+
): AbsoluteContinuousRoundedRectangle =
184+
AbsoluteContinuousRoundedRectangle(
185+
topLeft = corner,
186+
topRight = corner,
187+
bottomRight = corner,
188+
bottomLeft = corner,
189+
continuity = continuity
190+
)
191+
192+
@Stable
193+
fun AbsoluteContinuousRoundedRectangle(
194+
size: Dp,
195+
continuity: Continuity = Continuity.Default
196+
): AbsoluteContinuousRoundedRectangle =
197+
AbsoluteContinuousRoundedRectangle(
198+
corner = CornerSize(size),
199+
continuity = continuity
200+
)
201+
202+
@Stable
203+
fun AbsoluteContinuousRoundedRectangle(
204+
@FloatRange(from = 0.0) size: Float,
205+
continuity: Continuity = Continuity.Default
206+
): AbsoluteContinuousRoundedRectangle =
207+
AbsoluteContinuousRoundedRectangle(
208+
corner = CornerSize(size),
209+
continuity = continuity
210+
)
211+
212+
@Stable
213+
fun AbsoluteContinuousRoundedRectangle(
214+
@IntRange(from = 0, to = 100) percent: Int,
215+
continuity: Continuity = Continuity.Default
216+
): AbsoluteContinuousRoundedRectangle =
217+
AbsoluteContinuousRoundedRectangle(
218+
corner = CornerSize(percent),
219+
continuity = continuity
220+
)
221+
222+
@Stable
223+
fun AbsoluteContinuousRoundedRectangle(
224+
topLeft: Dp = 0f.dp,
225+
topRight: Dp = 0f.dp,
226+
bottomRight: Dp = 0f.dp,
227+
bottomLeft: Dp = 0f.dp,
228+
continuity: Continuity = Continuity.Default
229+
): AbsoluteContinuousRoundedRectangle =
230+
AbsoluteContinuousRoundedRectangle(
231+
topLeft = CornerSize(topLeft),
232+
topRight = CornerSize(topRight),
233+
bottomRight = CornerSize(bottomRight),
234+
bottomLeft = CornerSize(bottomLeft),
235+
continuity = continuity
236+
)
237+
238+
@Stable
239+
fun AbsoluteContinuousRoundedRectangle(
240+
@FloatRange(from = 0.0) topLeft: Float = 0f,
241+
@FloatRange(from = 0.0) topRight: Float = 0f,
242+
@FloatRange(from = 0.0) bottomRight: Float = 0f,
243+
@FloatRange(from = 0.0) bottomLeft: Float = 0f,
244+
continuity: Continuity = Continuity.Default
245+
): AbsoluteContinuousRoundedRectangle =
246+
AbsoluteContinuousRoundedRectangle(
247+
topLeft = CornerSize(topLeft),
248+
topRight = CornerSize(topRight),
249+
bottomRight = CornerSize(bottomRight),
250+
bottomLeft = CornerSize(bottomLeft),
251+
continuity = continuity
252+
)
253+
254+
@Stable
255+
fun AbsoluteContinuousRoundedRectangle(
256+
@IntRange(from = 0, to = 100) topLeftPercent: Int = 0,
257+
@IntRange(from = 0, to = 100) topRightPercent: Int = 0,
258+
@IntRange(from = 0, to = 100) bottomRightPercent: Int = 0,
259+
@IntRange(from = 0, to = 100) bottomLeftPercent: Int = 0,
260+
continuity: Continuity = Continuity.Default
261+
): AbsoluteContinuousRoundedRectangle =
262+
AbsoluteContinuousRoundedRectangle(
263+
topLeft = CornerSize(topLeftPercent),
264+
topRight = CornerSize(topRightPercent),
265+
bottomRight = CornerSize(bottomRightPercent),
266+
bottomLeft = CornerSize(bottomLeftPercent),
267+
continuity = continuity
268+
)

capsule/src/main/java/com/kyant/capsule/ConcentricContinuousRoundedRectangle.kt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,16 @@ fun ContinuousRoundedRectangle.concentricOutset(padding: Dp): ContinuousRoundedR
1818
return ConcentricContinuousRoundedRectangle(this, -padding)
1919
}
2020

21+
@Stable
22+
fun AbsoluteContinuousRoundedRectangle.concentricInset(padding: Dp): AbsoluteContinuousRoundedRectangle {
23+
return ConcentricAbsoluteContinuousRoundedRectangle(this, padding)
24+
}
25+
26+
@Stable
27+
fun AbsoluteContinuousRoundedRectangle.concentricOutset(padding: Dp): AbsoluteContinuousRoundedRectangle {
28+
return ConcentricAbsoluteContinuousRoundedRectangle(this, -padding)
29+
}
30+
2131
@Immutable
2232
private data class ConcentricContinuousRoundedRectangle(
2333
val containerShape: ContinuousRoundedRectangle,
@@ -30,6 +40,18 @@ private data class ConcentricContinuousRoundedRectangle(
3040
continuity = containerShape.continuity
3141
)
3242

43+
@Immutable
44+
private data class ConcentricAbsoluteContinuousRoundedRectangle(
45+
val containerShape: AbsoluteContinuousRoundedRectangle,
46+
val padding: Dp
47+
) : AbsoluteContinuousRoundedRectangle(
48+
topLeft = ConcentricCornerSize(containerShape.topStart, padding),
49+
topRight = ConcentricCornerSize(containerShape.topEnd, padding),
50+
bottomRight = ConcentricCornerSize(containerShape.bottomEnd, padding),
51+
bottomLeft = ConcentricCornerSize(containerShape.bottomStart, padding),
52+
continuity = containerShape.continuity
53+
)
54+
3355
@Immutable
3456
private data class ConcentricCornerSize(
3557
private val containerCornerSize: CornerSize,

capsule/src/main/java/com/kyant/capsule/ContinuousRoundedRectangle.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -222,10 +222,10 @@ fun ContinuousRoundedRectangle(
222222

223223
@Stable
224224
fun ContinuousRoundedRectangle(
225-
topStart: Dp = 0.dp,
226-
topEnd: Dp = 0.dp,
227-
bottomEnd: Dp = 0.dp,
228-
bottomStart: Dp = 0.dp,
225+
topStart: Dp = 0f.dp,
226+
topEnd: Dp = 0f.dp,
227+
bottomEnd: Dp = 0f.dp,
228+
bottomStart: Dp = 0f.dp,
229229
continuity: Continuity = Continuity.Default
230230
): ContinuousRoundedRectangle =
231231
ContinuousRoundedRectangle(

capsule/src/main/java/com/kyant/capsule/LerpContinuousRoundedRectangle.kt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,15 @@ fun lerp(
1717
return LerpContinuousRoundedRectangle(start, stop, fraction)
1818
}
1919

20+
@Stable
21+
fun lerp(
22+
start: AbsoluteContinuousRoundedRectangle,
23+
stop: AbsoluteContinuousRoundedRectangle,
24+
fraction: Float
25+
): AbsoluteContinuousRoundedRectangle {
26+
return LerpAbsoluteContinuousRoundedRectangle(start, stop, fraction)
27+
}
28+
2029
@Immutable
2130
private data class LerpContinuousRoundedRectangle(
2231
val start: ContinuousRoundedRectangle,
@@ -34,6 +43,23 @@ private data class LerpContinuousRoundedRectangle(
3443
}
3544
)
3645

46+
@Immutable
47+
private data class LerpAbsoluteContinuousRoundedRectangle(
48+
val start: AbsoluteContinuousRoundedRectangle,
49+
val stop: AbsoluteContinuousRoundedRectangle,
50+
val fraction: Float
51+
) : AbsoluteContinuousRoundedRectangle(
52+
topLeft = LerpCornerSize(start.topStart, stop.topStart, fraction),
53+
topRight = LerpCornerSize(start.topEnd, stop.topEnd, fraction),
54+
bottomRight = LerpCornerSize(start.bottomEnd, stop.bottomEnd, fraction),
55+
bottomLeft = LerpCornerSize(start.bottomStart, stop.bottomStart, fraction),
56+
continuity = when (fraction) {
57+
0f -> start.continuity
58+
1f -> stop.continuity
59+
else -> start.continuity.lerp(stop.continuity, fraction.toDouble())
60+
}
61+
)
62+
3763
@Immutable
3864
private data class LerpCornerSize(
3965
private val start: CornerSize,

0 commit comments

Comments
 (0)