Skip to content

Commit 0aeaece

Browse files
committed
Merge remote-tracking branch 'origin/feature/packetmine-rewrite' into feature/packetmine-rewrite
2 parents 4492a04 + 7c8f3f3 commit 0aeaece

File tree

3 files changed

+364
-4
lines changed

3 files changed

+364
-4
lines changed

common/src/main/kotlin/com/lambda/interaction/request/rotation/visibilty/PlaceDirection.kt

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ import net.minecraft.util.math.Direction
2323
import net.minecraft.util.math.MathHelper
2424
import net.minecraft.util.math.MathHelper.wrapDegrees
2525
import net.minecraft.util.math.Vec3i
26+
import kotlin.math.abs
27+
import kotlin.math.atan
28+
import kotlin.math.cos
29+
import kotlin.math.sin
2630

2731
enum class PlaceDirection(
2832
val rotation: Rotation,
@@ -50,20 +54,63 @@ enum class PlaceDirection(
5054
constructor(yaw: Double, pitch: Double, x: Int, y: Int, z: Int, yawRanges: List<ClosedRange<Double>>)
5155
: this(Rotation(yaw, pitch), Vec3i(x, y, z), yawRanges)
5256

53-
//ToDo: Add dynamic pitch border calculations to avoid excess rotation distance
5457
fun snapToArea(rot: Rotation): Rotation {
5558
if (isInArea(rot)) return rot
5659

5760
val normalizedYaw = wrapDegrees(rot.yaw)
5861
val clampedYaw = when {
59-
this.rotation.yaw != -180.0 -> normalizedYaw.coerceIn(yawRanges[0])
62+
rotation.yaw != -180.0 -> normalizedYaw.coerceIn(yawRanges[0])
6063
normalizedYaw < 0 -> normalizedYaw.coerceIn(yawRanges[0])
6164
else -> normalizedYaw.coerceIn(yawRanges[1])
6265
}
6366

64-
return Rotation(clampedYaw, this.rotation.pitch)
67+
// Calculate pitch boundaries based on the snapped yaw
68+
val snappedYawRad = Math.toRadians(clampedYaw)
69+
val pitchBoundaryEW = Math.toDegrees(atan(abs(sin(snappedYawRad))))
70+
val pitchBoundaryNS = Math.toDegrees(atan(abs(cos(snappedYawRad))))
71+
72+
// Determine the correct pitch boundary and snap pitch
73+
val snappedPitch = when {
74+
// Primary E/W Directions
75+
isEast() || isWest() -> {
76+
when {
77+
isUp() -> pitchBoundaryEW // Snap to lower edge of UP_E/UP_W area
78+
isDown() -> -pitchBoundaryEW // Snap to upper edge of DOWN_E/DOWN_W area
79+
else -> { // Horizontal E/W
80+
val isPitchWithinBounds = abs(rot.pitch - pitchBoundaryEW) < abs(rot.pitch - (-pitchBoundaryEW))
81+
if (isPitchWithinBounds) pitchBoundaryEW else -pitchBoundaryEW
82+
}
83+
}
84+
}
85+
// Primary N/S Directions
86+
isNorth() || isSouth() -> {
87+
when {
88+
isUp() -> pitchBoundaryNS // Snap to lower edge of UP_N/UP_S area
89+
isDown() -> -pitchBoundaryNS // Snap to upper edge of DOWN_N/DOWN_S area
90+
else -> { // Horizontal N/S
91+
val isWithinNorthernBoundary = abs(rot.pitch - pitchBoundaryNS) < abs(rot.pitch - (-pitchBoundaryNS))
92+
if (isWithinNorthernBoundary) pitchBoundaryNS else -pitchBoundaryNS
93+
}
94+
}
95+
}
96+
// Handle purely UP/DOWN directions
97+
else -> rotation.pitch
98+
}
99+
100+
// Clamp pitch to valid range
101+
val clampedPitch = snappedPitch.coerceIn(-90.0, 90.0)
102+
103+
return Rotation(clampedYaw, clampedPitch)
65104
}
66105

106+
// Helper functions to determine direction type
107+
private fun isEast(): Boolean = this == East || this == UpEast || this == DownEast
108+
private fun isWest(): Boolean = this == West || this == UpWest || this == DownWest
109+
private fun isNorth(): Boolean = this == North || this == UpNorth || this == DownNorth
110+
private fun isSouth(): Boolean = this == South || this == UpSouth || this == DownSouth
111+
private fun isUp(): Boolean = this == UpEast || this == UpWest || this == UpNorth || this == UpSouth || this == Up
112+
private fun isDown(): Boolean = this == DownEast || this == DownWest || this == DownNorth || this == DownSouth || this == Down
113+
67114
fun isInArea(rot: Rotation) = fromRotation(rot) == this
68115

69116
companion object {
@@ -120,4 +167,4 @@ const val FUDGE_FACTOR = 0.01
120167
val northYawRanges = listOf(-180.0..(-135.0 - FUDGE_FACTOR), (135.0 + FUDGE_FACTOR)..180.0)
121168
val southYawRange = ( -45.0 + FUDGE_FACTOR)..( 45.0 - FUDGE_FACTOR)
122169
val eastYawRange = (-135.0 + FUDGE_FACTOR)..(-45.0 - FUDGE_FACTOR)
123-
val westYawRange = ( 45.0 + FUDGE_FACTOR)..(135.0 - FUDGE_FACTOR)
170+
val westYawRange = ( 45.0 + FUDGE_FACTOR)..(135.0 - FUDGE_FACTOR)
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/*
2+
* Copyright 2025 Lambda
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
import com.lambda.interaction.request.rotation.Rotation
19+
import com.lambda.interaction.request.rotation.visibilty.PlaceDirection
20+
import kotlin.math.abs
21+
import kotlin.math.atan
22+
import kotlin.math.cos
23+
import kotlin.math.sin
24+
import kotlin.test.Test
25+
import kotlin.test.assertEquals
26+
import kotlin.test.assertTrue
27+
28+
class PlaceDirectionTest {
29+
30+
@Test
31+
fun `test pitch snapping for East direction`() {
32+
val direction = PlaceDirection.East
33+
34+
// Calculate expected pitch boundary for East at yaw -90.0
35+
val yawRad = Math.toRadians(-90.0)
36+
val expectedBoundary = Math.toDegrees(atan(abs(sin(yawRad))))
37+
38+
// Test rotation outside the area (pitch too high)
39+
val rotationOutsideHigh = Rotation(-90.0, expectedBoundary + 10.0)
40+
val snappedHigh = direction.snapToArea(rotationOutsideHigh)
41+
42+
// Test rotation outside the area (pitch too low)
43+
val rotationOutsideLow = Rotation(-90.0, -expectedBoundary - 10.0)
44+
val snappedLow = direction.snapToArea(rotationOutsideLow)
45+
46+
// Verify that the pitch is snapped to the boundary
47+
assertEquals(expectedBoundary, snappedHigh.pitch, 0.001, "Pitch should be snapped to the upper boundary")
48+
assertEquals(-expectedBoundary, snappedLow.pitch, 0.001, "Pitch should be snapped to the lower boundary")
49+
}
50+
51+
@Test
52+
fun `test pitch snapping for North direction`() {
53+
val direction = PlaceDirection.North
54+
55+
// Calculate expected pitch boundary for North at yaw -180.0
56+
val yawRad = Math.toRadians(-180.0)
57+
val expectedBoundary = Math.toDegrees(atan(abs(cos(yawRad))))
58+
59+
// Test rotation outside the area (pitch too high)
60+
val rotationOutsideHigh = Rotation(-180.0, expectedBoundary + 10.0)
61+
val snappedHigh = direction.snapToArea(rotationOutsideHigh)
62+
63+
// Test rotation outside the area (pitch too low)
64+
val rotationOutsideLow = Rotation(-180.0, -expectedBoundary - 10.0)
65+
val snappedLow = direction.snapToArea(rotationOutsideLow)
66+
67+
// Verify that the pitch is snapped to the boundary
68+
assertEquals(expectedBoundary, snappedHigh.pitch, 0.001, "Pitch should be snapped to the upper boundary")
69+
assertEquals(-expectedBoundary, snappedLow.pitch, 0.001, "Pitch should be snapped to the lower boundary")
70+
}
71+
72+
@Test
73+
fun `test pitch snapping for UpEast direction`() {
74+
val direction = PlaceDirection.UpEast
75+
76+
// Calculate expected pitch boundary for UpEast at yaw -90.0
77+
val yawRad = Math.toRadians(-90.0)
78+
val expectedBoundary = Math.toDegrees(atan(abs(sin(yawRad))))
79+
80+
// Test rotation outside the area (pitch too low)
81+
val rotationOutside = Rotation(-90.0, expectedBoundary - 10.0)
82+
val snapped = direction.snapToArea(rotationOutside)
83+
84+
// Verify that the pitch is snapped to the boundary
85+
assertEquals(expectedBoundary, snapped.pitch, 0.001, "Pitch should be snapped to the boundary")
86+
}
87+
88+
@Test
89+
fun `test pitch snapping for DownNorth direction`() {
90+
val direction = PlaceDirection.DownNorth
91+
92+
// Calculate expected pitch boundary for DownNorth at yaw -180.0
93+
val yawRad = Math.toRadians(-180.0)
94+
val expectedBoundary = Math.toDegrees(atan(abs(cos(yawRad))))
95+
96+
// Test rotation outside the area (pitch too high)
97+
val rotationOutside = Rotation(-180.0, -expectedBoundary + 10.0)
98+
val snapped = direction.snapToArea(rotationOutside)
99+
100+
// Verify that the pitch is snapped to the boundary
101+
assertEquals(-expectedBoundary, snapped.pitch, 0.001, "Pitch should be snapped to the boundary")
102+
}
103+
104+
@Test
105+
fun `test no snapping when rotation is already in area`() {
106+
val direction = PlaceDirection.East
107+
108+
// Create a rotation that should be in the East area
109+
val rotation = Rotation(-90.0, 0.0)
110+
111+
// Verify that the rotation is in the area
112+
assertTrue(direction.isInArea(rotation), "Rotation should be in the East area")
113+
114+
// Verify that snapToArea returns the same rotation
115+
val snapped = direction.snapToArea(rotation)
116+
assertEquals(rotation.yaw, snapped.yaw, 0.001, "Yaw should not change")
117+
assertEquals(rotation.pitch, snapped.pitch, 0.001, "Pitch should not change")
118+
}
119+
}

0 commit comments

Comments
 (0)