Skip to content

Commit 3aa18fb

Browse files
committed
Added Angle class instead of a lot of random angle functions and updated usages everywhere
1 parent 17e0b18 commit 3aa18fb

12 files changed

Lines changed: 235 additions & 188 deletions
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
package com.imsproject.watch.utils
2+
3+
import androidx.annotation.IntRange
4+
import com.imsproject.watch.UNDEFINED_ANGLE
5+
import kotlin.math.absoluteValue
6+
7+
/**
8+
* This class represents an angle in degrees in the range of (-180,180]
9+
*
10+
* The constructor also accepts a special value for undefined angle which is defined by [UNDEFINED_ANGLE]
11+
*/
12+
class Angle(
13+
val floatValue: Float
14+
) {
15+
16+
init {
17+
if(floatValue != UNDEFINED_ANGLE && (floatValue <= -180 || floatValue > 180)){
18+
throw IllegalArgumentException("Invalid angle value: $floatValue")
19+
}
20+
}
21+
22+
val doubleValue: Double
23+
get() = floatValue.toDouble()
24+
25+
operator fun minus(other: Angle): Angle {
26+
// handle the gap between 2nd and 3rd quadrants
27+
val diff = if(this.isInQuadrant(2) && other.isInQuadrant(3)){
28+
this.floatValue - (other.floatValue+360)
29+
}
30+
else if(this.isInQuadrant(3) && other.isInQuadrant(2)){
31+
(this.floatValue+360) - other.floatValue
32+
}
33+
// simple case
34+
else {
35+
this.floatValue - other.floatValue
36+
}
37+
return Angle(diff.absoluteValue)
38+
}
39+
40+
operator fun minus(other: Number): Angle {
41+
return minus(Angle(other.toFloat()))
42+
}
43+
44+
operator fun plus(other: Angle): Angle {
45+
var added = this.floatValue + other.floatValue
46+
47+
//handle the gap between 2nd and 3rd quadrants
48+
added = if(this.isInQuadrant(2) && other.floatValue > 0){
49+
if (added > 180){
50+
added - 360
51+
} else {
52+
added
53+
}
54+
} else if(this.isInQuadrant(3) && other.floatValue < 0){
55+
if (added <= -180){
56+
added + 360
57+
} else {
58+
added
59+
}
60+
}
61+
// simple case
62+
else {
63+
added
64+
}
65+
return Angle(added)
66+
}
67+
68+
operator fun plus(other: Number): Angle {
69+
return plus(Angle(other.toFloat()))
70+
}
71+
72+
operator fun compareTo(other: Angle): Int {
73+
return floatValue.compareTo(other.floatValue)
74+
}
75+
76+
operator fun compareTo(other: Number): Int {
77+
return compareTo(Angle(other.toFloat()))
78+
}
79+
80+
fun isBetweenInclusive(min: Float, max: Float) = min <= floatValue && floatValue <= max
81+
82+
override fun toString(): String {
83+
return floatValue.toString()
84+
}
85+
86+
override fun equals(other: Any?): Boolean {
87+
if (this === other) return true
88+
if (javaClass != other?.javaClass) return false
89+
90+
other as Angle
91+
92+
return floatValue == other.floatValue
93+
}
94+
95+
override fun hashCode(): Int {
96+
return floatValue.hashCode()
97+
}
98+
99+
/**
100+
* this function assumes that the angle is in the range of (-180,180]
101+
* and the quadrant is in the range of [1,4]
102+
*
103+
* the quadrant are defined as follows:
104+
* 1. 0 < angle <= 90
105+
* 2. 90 < angle <= 180
106+
* 3. -180 < angle <= -90
107+
* 4. -90 < angle <= 0
108+
*
109+
* meaning, clockwise side of the quadrant is inclusive and the counter-clockwise side is exclusive
110+
*/
111+
private fun isInQuadrant(@IntRange(1,4) quadrant: Int) : Boolean {
112+
return when(quadrant){
113+
1 -> 0f < floatValue && floatValue <= 90f
114+
2 -> 90f < floatValue && floatValue <= 180f
115+
3 -> -180f < floatValue && floatValue <= -90f
116+
4 -> -90f < floatValue && floatValue <= 0f
117+
else -> throw IllegalArgumentException("Invalid quadrant: $quadrant")
118+
}
119+
}
120+
121+
companion object {
122+
/**
123+
* This function assumes that the angles are in the range of (-180,180]
124+
*/
125+
fun isClockwise(
126+
from: Angle,
127+
to: Angle
128+
) : Boolean {
129+
//handle the gap between 2nd and 3rd quadrants
130+
return if(from.isInQuadrant(2) && to.isInQuadrant(3)){
131+
true
132+
} else if(from.isInQuadrant(3) && to.isInQuadrant(2)){
133+
false
134+
}
135+
// simple case
136+
else {
137+
from < to
138+
}
139+
}
140+
}
141+
}

watch/app/src/main/java/com/imsproject/watch/utils/ErrorReporter.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package com.imsproject.watch.utils
22

33
import android.util.Log
4-
import com.imsproject.common.utils.Response
5-
import com.imsproject.common.utils.fromJson
64
import com.imsproject.watch.model.MainModel
75
import com.imsproject.watch.model.REST_SCHEME
86
import com.imsproject.watch.model.SERVER_ERROR_REPORTS_PORT

watch/app/src/main/java/com/imsproject/watch/utils/FrequencyTracker.kt

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
package com.imsproject.watch.utils
22

33
import com.imsproject.watch.FREQUENCY_HISTORY_MILLISECONDS
4-
import kotlinx.coroutines.DelicateCoroutinesApi
5-
import kotlinx.coroutines.GlobalScope
6-
import kotlinx.coroutines.launch
7-
import java.util.concurrent.Executors
84

95
private const val SAMPLES_PER_SECOND = 1000 / 16f // 60 fps
106

@@ -18,16 +14,16 @@ class FrequencyTracker {
1814
private val samples = Array(samplesHistoryCount) {0f}
1915
private var sum : Float = 0f
2016
private var lastSampleTime : Long = 0
21-
private var lastSampleAngle : Float = 0f
17+
private var lastSampleAngle : Angle = 0f.toAngle()
2218
private var index : Int = 0
2319
private var sampleCount : Int = 0
2420

25-
fun addSample(angle: Float) {
21+
fun addSample(angle: Angle) {
2622
val currentTime = System.currentTimeMillis()
2723
val timeDiff = currentTime - lastSampleTime
2824
if(timeDiff == 0L) return
29-
val angleDiff = calculateAngleDiff(lastSampleAngle,angle)
30-
val radiansDiff = Math.toRadians(angleDiff.toDouble())
25+
val angleDiff = lastSampleAngle - angle
26+
val radiansDiff = Math.toRadians(angleDiff.doubleValue)
3127
val omega = radiansDiff / (timeDiff / 1000.0)
3228
val frequency = (omega / (2 * Math.PI)).toFloat()
3329
samples[index] = frequency
@@ -42,7 +38,7 @@ class FrequencyTracker {
4238
fun reset() {
4339
sum = 0f
4440
lastSampleTime = 0
45-
lastSampleAngle = 0f
41+
lastSampleAngle = 0f.toAngle()
4642
index = 0
4743
samples.fill(0f)
4844
}

watch/app/src/main/java/com/imsproject/watch/utils/LatencyTracker.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@ package com.imsproject.watch.utils
22

33
import android.os.SystemClock
44
import com.imsproject.common.networking.UdpClient
5-
import kotlinx.coroutines.*
5+
import kotlinx.coroutines.CoroutineScope
66
import kotlinx.coroutines.Dispatchers
7+
import kotlinx.coroutines.Job
8+
import kotlinx.coroutines.delay
9+
import kotlinx.coroutines.isActive
10+
import kotlinx.coroutines.launch
711
import kotlinx.coroutines.sync.Mutex
812
import kotlinx.coroutines.sync.withLock
913
import java.net.SocketTimeoutException

watch/app/src/main/java/com/imsproject/watch/utils/RestApiClient.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
package com.imsproject.watch.utils
22

3-
import okhttp3.*
43
import okhttp3.MediaType.Companion.toMediaType
4+
import okhttp3.OkHttpClient
5+
import okhttp3.Request
56
import okhttp3.RequestBody.Companion.toRequestBody
67
import java.io.IOException
78
import java.net.URLEncoder
Lines changed: 10 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,34 @@
11
package com.imsproject.watch.utils
22

33
import androidx.annotation.FloatRange
4-
import androidx.annotation.IntRange
54
import com.imsproject.watch.SCREEN_CENTER
6-
import kotlin.math.absoluteValue
75
import kotlin.math.atan2
86
import kotlin.math.cos
97
import kotlin.math.pow
108
import kotlin.math.sin
119
import kotlin.math.sqrt
1210

13-
/**
14-
* this function assumes that the angle is in the range of (-180,180]
15-
* and the quadrant is in the range of [1,4]
16-
*
17-
* the quadrant are defined as follows:
18-
* 1. 0 < angle <= 90
19-
* 2. 90 < angle <= 180
20-
* 3. -180 < angle <= -90
21-
* 4. -90 < angle <= 0
22-
*
23-
* meaning, clockwise side of the quadrant is inclusive and the counter-clockwise side is exclusive
24-
*/
25-
fun Float.isInQuadrant(@IntRange(1,4) quadrant: Int) : Boolean {
26-
return when(quadrant){
27-
1 -> 0f < this && this <= 90f
28-
2 -> 90f < this && this <= 180f
29-
3 -> -180f < this && this <= -90f
30-
4 -> -90f < this && this <= 0f
31-
else -> throw IllegalArgumentException("Invalid quadrant: $quadrant")
32-
}
33-
}
34-
3511
/**
3612
* @return Pair of `<distance,angle>` where angle in the range of (-180,180]
3713
*/
38-
fun cartesianToPolar(x: Double, y:Double) : Pair<Float,Float> {
39-
val distance = sqrt(
40-
(x - SCREEN_CENTER.x).pow(2) + (y - SCREEN_CENTER.y).pow(2)
41-
).toFloat()
14+
fun cartesianToPolar(x: Float, y:Float) : Pair<Float,Angle> {
15+
val distance = sqrt((x - SCREEN_CENTER.x).pow(2) + (y - SCREEN_CENTER.y).pow(2))
4216
val angle = Math.toDegrees(
4317
atan2(
4418
y - SCREEN_CENTER.y,
4519
x - SCREEN_CENTER.x
46-
)
20+
).toDouble()
4721
).toFloat()
48-
return distance to angle
22+
return distance to angle.toAngle()
4923
}
5024

5125
/**
5226
* This function assumes that the angles are in the range of (-180,180]
5327
* @return Pair of `<x,y>`
5428
*/
55-
fun polarToCartesian(
56-
distanceFromCenter: Float,
57-
@FloatRange(-180.0,180.0,false,true) angle: Float
58-
) : Pair<Float, Float> {
29+
fun polarToCartesian(distanceFromCenter: Float, angle: Angle) : Pair<Float, Float> {
5930
// Convert angle to radians
60-
val angleRadians = Math.toRadians(angle.toDouble())
31+
val angleRadians = Math.toRadians(angle.doubleValue)
6132

6233
// Calculate coordinates
6334
val (centerX, centerY) = SCREEN_CENTER
@@ -67,76 +38,6 @@ fun polarToCartesian(
6738
return x.toFloat() to y.toFloat()
6839
}
6940

70-
/**
71-
* This function assumes that the angles are in the range of (-180,180]
72-
*/
73-
fun calculateAngleDiff(
74-
@FloatRange(-180.0,180.0,false,true) angle1: Float,
75-
@FloatRange(-180.0,180.0,false,true) angle2: Float
76-
) : Float {
77-
// handle the gap between 2nd and 3rd quadrants
78-
val diff = if(angle1.isInQuadrant(2) && angle2.isInQuadrant(3)){
79-
angle1 - (angle2+360)
80-
}
81-
else if(angle1.isInQuadrant(3) && angle2.isInQuadrant(2)){
82-
(angle1+360) - angle2
83-
}
84-
// simple case
85-
else {
86-
angle1 - angle2
87-
}
88-
return diff.absoluteValue
89-
}
90-
91-
/**
92-
* This function assumes that the angles are in the range of (-180,180]
93-
*/
94-
fun isClockwise(
95-
@FloatRange(-180.0,180.0,false,true) previousAngle: Float,
96-
@FloatRange(-180.0,180.0,false,true) newAngle: Float
97-
) : Boolean {
98-
//handle the gap between 2nd and 3rd quadrants
99-
return if(previousAngle.isInQuadrant(2) && newAngle.isInQuadrant(3)){
100-
true
101-
} else if(previousAngle.isInQuadrant(3) && newAngle.isInQuadrant(2)){
102-
false
103-
}
104-
// simple case
105-
else {
106-
previousAngle < newAngle
107-
}
108-
}
109-
110-
/**
111-
* This function assumes that the angles are in the range of (-180,180]
112-
* and the addition is in the range of [-180,180]
113-
*/
114-
fun addToAngle(
115-
@FloatRange(-180.0,180.0,false,true) angle: Float,
116-
@FloatRange(-180.0,180.0,true,true) addition: Float
117-
) : Float {
118-
val added = angle + addition
119-
120-
//handle the gap between 2nd and 3rd quadrants
121-
if(angle.isInQuadrant(2) && addition > 0){
122-
return if (added > 180){
123-
added - 360
124-
} else {
125-
added
126-
}
127-
} else if(angle.isInQuadrant(3) && addition < 0){
128-
return if (added <= -180){
129-
added + 360
130-
} else {
131-
added
132-
}
133-
}
134-
// simple case
135-
else {
136-
return added
137-
}
138-
}
139-
14041
/**
14142
* this function calculates the third point of a triangle given two points and the distance from the second point
14243
*
@@ -172,7 +73,7 @@ fun calculateTriangleThirdPoint(
17273
}
17374

17475
@Suppress("NOTHING_TO_INLINE")
175-
inline fun Float.isBetweenInclusive(min: Float, max: Float) = min <= this && this <= max
76+
inline fun Float.isBetweenInclusive(min: Float, max: Float) = min <= this && this <= max
17677

17778
@Suppress("NOTHING_TO_INLINE")
17879
inline fun Float.sign() = if(this < 0) -1 else if (this > 0) 1 else 0
@@ -190,3 +91,6 @@ inline fun Int.fastCoerceIn(min: Int, max: Int) = when {
19091
else -> this
19192
}
19293

94+
@Suppress("NOTHING_TO_INLINE")
95+
inline fun Float.toAngle() = Angle(this)
96+

0 commit comments

Comments
 (0)