Skip to content

Commit 21b44a4

Browse files
authored
Add new core methods (#600)
* Implement rotate_toward * Change VariantArray::remove to VariantArray::removeAt
1 parent db3590b commit 21b44a4

File tree

9 files changed

+180
-28
lines changed

9 files changed

+180
-28
lines changed

harness/tests/src/main/kotlin/godot/tests/Invocation.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,7 @@ class Invocation : Node3D() {
511511
fun removeNullableNavMesh(navigationMesh: NavigationMesh?) = nullableArray.remove(navigationMesh)
512512

513513
@RegisterFunction
514-
fun removeNullableNavMeshWithIndex(index: Int) = nullableArray.remove(index)
514+
fun removeNullableNavMeshWithIndex(index: Int) = nullableArray.removeAt(index)
515515

516516
// TODO: This will fail to register as we cannot register nullable return type
517517
// @RegisterFunction
@@ -530,7 +530,7 @@ class Invocation : Node3D() {
530530
fun removeNavMesh(navigationMesh: NavigationMesh) = navMeshes.remove(navigationMesh)
531531

532532
@RegisterFunction
533-
fun removeNavMeshWithIndex(index: Int) = navMeshes.remove(index)
533+
fun removeNavMeshWithIndex(index: Int) = navMeshes.removeAt(index)
534534

535535
@RegisterFunction
536536
fun getNavMeshFromArray(index: Int) = navMeshes[index]

kt/godot-library/src/main/kotlin/godot/core/bridge/Dictionary.kt

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -358,27 +358,29 @@ class Dictionary<K, V> : NativeCoreType, MutableMap<K, V> {
358358
external fun engine_call_operator_set(_handle: VoidPtr)
359359
external fun engine_call_equals(_handle: VoidPtr)
360360
}
361-
}
362361

363-
@Suppress("FunctionName")
364-
inline fun <reified K, reified V> Dictionary(): Dictionary<K, V> {
365-
val keyVariantType = variantMapper[K::class]
366-
checkNotNull(keyVariantType) {
367-
"Can't create a Dictionary with generic key ${K::class}."
368-
}
369-
val valVariantType = variantMapper[V::class]
370-
checkNotNull(valVariantType) {
371-
"Can't create a Dictionary with generic value ${V::class}."
362+
363+
companion object {
364+
inline operator fun <reified K, reified V> invoke(): Dictionary<K, V> {
365+
val keyVariantType = variantMapper[K::class]
366+
checkNotNull(keyVariantType) {
367+
"Can't create a Dictionary with generic key ${K::class}."
368+
}
369+
val valVariantType = variantMapper[V::class]
370+
checkNotNull(valVariantType) {
371+
"Can't create a Dictionary with generic value ${V::class}."
372+
}
373+
return Dictionary(keyVariantType, valVariantType)
374+
}
372375
}
373-
return Dictionary<K, V>(keyVariantType, valVariantType)
374376
}
375377

376378
inline fun <reified K, reified V> dictionaryOf(vararg args: Pair<K, V>) = Dictionary<K, V>().also {
377379
it.putAll(args)
378380
}
379381

380382
/**
381-
* Convert an Map into a Dictionary
383+
* Convert a Map into a Dictionary
382384
*/
383385
inline fun <reified K, reified V> Map<K, V>.toDictionary() = Dictionary<K, V>().also {
384386
it.putAll(this)

kt/godot-library/src/main/kotlin/godot/core/bridge/VariantArray.kt

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ class VariantArray<T> : NativeCoreType, MutableCollection<T> {
130130
/**
131131
* Removes an element from the array by index.
132132
*/
133-
fun remove(position: Int) {
133+
fun removeAt(position: Int) {
134134
TransferContext.writeArguments(VariantType.JVM_INT to position)
135135
Bridge.engine_call_removeAt(_handle)
136136
}
@@ -552,7 +552,7 @@ class VariantArray<T> : NativeCoreType, MutableCollection<T> {
552552
}
553553

554554
override fun iterator(): MutableIterator<T> {
555-
return IndexedIterator(this::size, this::get, this::remove)
555+
return IndexedIterator(this::size, this::get, this::removeAt)
556556
}
557557

558558
/**
@@ -628,20 +628,19 @@ class VariantArray<T> : NativeCoreType, MutableCollection<T> {
628628
external fun engine_call_operator_set(_handle: VoidPtr)
629629
external fun engine_call_operator_get(_handle: VoidPtr)
630630
}
631-
}
632-
633631

634-
//CONSTRUCTOR
635-
@Suppress("FunctionName")
636-
inline fun <reified T> VariantArray(): VariantArray<T> {
637-
val variantType = variantMapper[T::class]
638-
checkNotNull(variantType) {
639-
"Can't create a VariantArray with generic ${T::class}."
632+
companion object{
633+
inline operator fun <reified T> invoke(): VariantArray<T> {
634+
val variantType = variantMapper[T::class]
635+
checkNotNull(variantType) {
636+
"Can't create a VariantArray with generic ${T::class}."
637+
}
638+
return VariantArray(
639+
variantType,
640+
T::class
641+
)
642+
}
640643
}
641-
return VariantArray<T>(
642-
variantType,
643-
T::class
644-
)
645644
}
646645

647646
//HELPER

kt/godot-library/src/main/kotlin/godot/core/math/Basis.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,19 @@ class Basis() : CoreType {
677677
this._z = ret._z
678678
}
679679

680+
/**
681+
* Assuming that the matrix is a proper rotation matrix, returns the result of rotating toward [to] by [delta] (in radians).
682+
* Passing a negative [delta] will rotate toward the inverse of [to].
683+
*/
684+
fun rotateToward(to: Basis, delta: RealT): Basis {
685+
return Basis(
686+
getRotationQuaternion().rotateToward(
687+
to.getRotationQuaternion(),
688+
delta
689+
)
690+
)
691+
}
692+
680693
/**
681694
* Introduce an additional scaling specified by the given 3D scaling factor.
682695
*/

kt/godot-library/src/main/kotlin/godot/core/math/Quaternion.kt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,32 @@ class Quaternion(
214214
w /= l
215215
}
216216

217+
/**
218+
* Returns the result of rotating toward [to] by [delta] (in radians). Passing a negative [delta] will rotate toward the inverse of [to].
219+
*
220+
* **Note:** Both quaternions must be normalized.
221+
*/
222+
fun rotateToward(to: Quaternion, delta: Double): Quaternion {
223+
val unsignedDelta: RealT
224+
val unsignedTo: Quaternion
225+
226+
if (delta < 0.0) {
227+
unsignedDelta = -delta
228+
unsignedTo = to.inverse()
229+
} else {
230+
unsignedDelta = delta
231+
unsignedTo = to
232+
}
233+
234+
val angle = angleTo(unsignedTo)
235+
236+
return if (angle < unsignedDelta) {
237+
unsignedTo
238+
} else {
239+
slerp(unsignedTo, unsignedDelta / angle)
240+
}
241+
}
242+
217243
/**
218244
* Sets the quaternion to a rotation which rotates around axis by the specified angle, in radians. The axis must be a normalized vector.
219245
*/

kt/godot-library/src/main/kotlin/godot/core/math/Vector2.kt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import godot.util.bezierInterpolate
99
import godot.util.cubicInterpolateInTime
1010
import godot.util.fposmod
1111
import godot.util.isEqualApprox
12+
import godot.util.isZeroApprox
1213
import godot.util.lerp
1314
import godot.util.snapped
1415
import godot.util.toRealT
@@ -423,6 +424,37 @@ class Vector2(
423424
y = sin(radians)
424425
}
425426

427+
/**
428+
* Returns the result of rotating this vector toward [to], by increment [delta] (in radians).
429+
* Passing a negative [delta] will rotate toward the opposite of [to].
430+
* This method supports vectors of different length, with the same behavior as [slerp].
431+
* If the vectors are colinear, this method behaves like [moveToward]. If [to] has a length of zero, this method behaves like [lerp].
432+
*/
433+
fun rotateToward(to: Vector2, delta: RealT): Vector2 {
434+
val unsignedDelta: RealT
435+
val unsignedTo: Vector2
436+
437+
if (delta < 0.0) {
438+
unsignedDelta = -delta
439+
unsignedTo = -to
440+
} else {
441+
unsignedDelta = delta
442+
unsignedTo = to
443+
}
444+
445+
val angle = abs(angleTo(to))
446+
447+
if (angle < unsignedDelta) {
448+
if (unsignedTo.lengthSquared().isZeroApprox()) {
449+
// Prevent locking up when to is (0, 0).
450+
return lerp(unsignedTo, unsignedDelta)
451+
}
452+
return moveToward(unsignedTo, unsignedDelta * unsignedTo.length())
453+
}
454+
455+
return slerp(unsignedTo, unsignedDelta / angle)
456+
}
457+
426458
/**
427459
* Returns the vector with all components rounded to the nearest integer, with halfway cases rounded away from zero.
428460
*/

kt/godot-library/src/main/kotlin/godot/core/math/Vector3.kt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import godot.util.bezierInterpolate
99
import godot.util.cubicInterpolateInTime
1010
import godot.util.fposmod
1111
import godot.util.isEqualApprox
12+
import godot.util.isZeroApprox
1213
import godot.util.lerp
1314
import godot.util.snapped
1415
import godot.util.toRealT
@@ -494,6 +495,37 @@ class Vector3(
494495
this.z = ret.z
495496
}
496497

498+
/**
499+
* Returns the result of rotating this vector toward [to], by increment [delta] (in radians).
500+
* Passing a negative [delta] will rotate toward the opposite of [to].
501+
* This method supports vectors of different length, with the same behavior as [slerp].
502+
* If the vectors are colinear, this method behaves like [moveToward]. If [to] has a length of zero, this method behaves like [lerp].
503+
*/
504+
fun rotateToward(to: Vector3, delta: RealT): Vector3 {
505+
val unsignedDelta: RealT
506+
val unsignedTo: Vector3
507+
508+
if (delta < 0.0) {
509+
unsignedDelta = -delta
510+
unsignedTo = -to
511+
} else {
512+
unsignedDelta = delta
513+
unsignedTo = to
514+
}
515+
516+
val angle = abs(angleTo(to))
517+
518+
if (angle < unsignedDelta) {
519+
if (unsignedTo.lengthSquared().isZeroApprox()) {
520+
// Prevent locking up when to is (0, 0).
521+
return lerp(unsignedTo, unsignedDelta)
522+
}
523+
return moveToward(unsignedTo, unsignedDelta * unsignedTo.length())
524+
}
525+
526+
return slerp(unsignedTo, unsignedDelta / angle)
527+
}
528+
497529
/**
498530
* Returns the vector with all components rounded to the nearest integer, with halfway cases rounded away from zero.
499531
*/

kt/godot-library/src/test/kotlin/godot/core/Vector2Test.kt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,32 @@ class TestVector2 {
9494

9595
val moveTowardResult = Vector2(1, 0).moveToward(Vector2(10, 0), 3.0)
9696
checkMessage(moveTowardResult == Vector2(4, 0)) { "Vector2 move_toward should work as expected." }
97+
98+
checkMessage(
99+
Vector2(0, 1).rotateToward(
100+
Vector2(0, -1),
101+
PI * 0.5
102+
).isEqualApprox(Vector2(1, 0))
103+
) { "Vector2 rotate_toward should work as expected." }
104+
105+
checkMessage(
106+
Vector2(1, 0).rotateToward(
107+
Vector2(0, 1),
108+
-PI * 0.5
109+
).isEqualApprox(Vector2(0, -1))
110+
) { "Vector2 rotate_toward with negative delta should behave as expected." }
111+
checkMessage(
112+
Vector2(1, 1).rotateToward(
113+
Vector2(10, 10),
114+
0.5
115+
).isEqualApprox(Vector2(6, 6))
116+
) { "Vector2 rotate_toward with colinear inputs should behave as expected." }
117+
checkMessage(
118+
Vector2(10, 10).rotateToward(
119+
Vector2(0, 0),
120+
0.5
121+
).isEqualApprox(Vector2(5, 5))
122+
) { "Vector2 rotate_toward with second input as zero should behave as expected." }
97123
}
98124

99125
@Test

kt/godot-library/src/test/kotlin/godot/core/Vector3Test.kt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,28 @@ class TestVector3 {
110110
0.0
111111
)
112112
) { "moveToward() should return the expected result." }
113+
114+
checkMessage(
115+
Vector3(1, 0, 0).rotateToward(
116+
Vector3(0, 1, 0),
117+
TAU / 8.0
118+
).isEqualApprox(Vector3(SQRT12, SQRT12, 0))
119+
) { "Vector3 rotate_toward should work as expected." }
120+
checkMessage(
121+
Vector3(1, 0, 0).rotateToward(
122+
Vector3(0, 1, 0), -TAU / 8.0
123+
).isEqualApprox(Vector3(SQRT12, -SQRT12, 0))
124+
) { "Vector3 rotate_toward with negative delta should behave as expected." }
125+
checkMessage(
126+
Vector3(1, 1, 1).rotateToward(
127+
Vector3(10, 10, 10), 0.5
128+
).isEqualApprox(Vector3(6, 6, 6))
129+
) { "Vector3 rotate_toward with colinear inputs should behave as expected." }
130+
checkMessage(
131+
Vector3(10, 10, 10).rotateToward(
132+
Vector3(0, 0, 0), 0.5
133+
).isEqualApprox(Vector3(5, 5, 5))
134+
) { "Vector3 rotate_toward with second input as zero should behave as expected." }
113135
}
114136

115137
@Test

0 commit comments

Comments
 (0)