Skip to content

Commit

Permalink
Extract AR Marker
Browse files Browse the repository at this point in the history
  • Loading branch information
kylecorry31 committed Oct 30, 2023
1 parent 2017382 commit 92ede16
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 74 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.kylecorry.trail_sense.tools.augmented_reality

import com.kylecorry.andromeda.canvas.ICanvasDrawer
import com.kylecorry.trail_sense.shared.canvas.PixelCircle

interface ARMarker {
fun draw(drawer: ICanvasDrawer, anchor: PixelCircle)
fun getAngularDiameter(view: AugmentedRealityView): Float
fun getHorizonCoordinate(view: AugmentedRealityView): AugmentedRealityView.HorizonCoordinate
fun onFocused(): Boolean
fun onClick(): Boolean
}
Original file line number Diff line number Diff line change
@@ -1,45 +1,28 @@
package com.kylecorry.trail_sense.tools.augmented_reality

import android.graphics.Color
import androidx.annotation.ColorInt
import com.kylecorry.andromeda.canvas.ICanvasDrawer
import com.kylecorry.sol.units.Coordinate
import com.kylecorry.trail_sense.shared.camera.AugmentedRealityUtils
import com.kylecorry.trail_sense.shared.canvas.PixelCircle
import kotlin.math.hypot

class CircleARMarker private constructor(
// TODO: Is the interface even needed?
class ARMarkerImpl private constructor(
private val position: AugmentedRealityView.HorizonCoordinate?,
private val angularDiameter: Float?,
private val location: Coordinate?,
private val elevation: Float?,
private val actualDiameter: Float?,
@ColorInt
private val color: Int,
@ColorInt
private val strokeColor: Int? = null,
private val opacity: Int = 255,
private val strokeWeight: Float = 0.5f,
private val canvasObject: CanvasObject,
private val onFocusedFn: (() -> Boolean) = { false },
private val onClickFn: () -> Boolean = { false }
) {
fun draw(drawer: ICanvasDrawer, anchor: PixelCircle) {
val size = anchor.radius * 2f
drawer.noTint()
if (strokeColor != null && strokeColor != Color.TRANSPARENT) {
drawer.stroke(strokeColor)
drawer.strokeWeight(drawer.dp(strokeWeight))
} else {
drawer.noStroke()
}
if (color != Color.TRANSPARENT) {
drawer.fill(color)
drawer.opacity(opacity)
drawer.circle(anchor.center.x, anchor.center.y, size)
}
) : ARMarker {

override fun draw(drawer: ICanvasDrawer, anchor: PixelCircle) {
canvasObject.draw(drawer, anchor)
}

fun getAngularDiameter(view: AugmentedRealityView): Float {
override fun getAngularDiameter(view: AugmentedRealityView): Float {
if (actualDiameter != null && location != null) {
val distance = hypot(
view.location.distanceTo(location),
Expand All @@ -50,7 +33,7 @@ class CircleARMarker private constructor(
return angularDiameter ?: 1f
}

fun getHorizonCoordinate(view: AugmentedRealityView): AugmentedRealityView.HorizonCoordinate {
override fun getHorizonCoordinate(view: AugmentedRealityView): AugmentedRealityView.HorizonCoordinate {
if (location != null) {
return AugmentedRealityUtils.getHorizonCoordinate(
view.location,
Expand All @@ -64,35 +47,29 @@ class CircleARMarker private constructor(
}


fun onFocused(): Boolean {
override fun onFocused(): Boolean {
return onFocusedFn()
}

fun onClick(): Boolean {
override fun onClick(): Boolean {
return onClickFn()
}

companion object {
fun horizon(
position: AugmentedRealityView.HorizonCoordinate?,
angularDiameter: Float = 12f,
@ColorInt color: Int,
@ColorInt strokeColor: Int? = null,
opacity: Int = 255,
strokeWeight: Float = 0.5f,
canvasObject: CanvasObject,
onFocusedFn: (() -> Boolean) = { false },
onClickFn: () -> Boolean = { false }
): CircleARMarker {
return CircleARMarker(
): ARMarker {
return ARMarkerImpl(
position,
angularDiameter,
null,
null,
null,
color,
strokeColor,
opacity,
strokeWeight,
canvasObject,
onFocusedFn,
onClickFn
)
Expand All @@ -102,23 +79,17 @@ class CircleARMarker private constructor(
location: Coordinate,
elevation: Float,
actualDiameter: Float,
@ColorInt color: Int,
@ColorInt strokeColor: Int? = null,
opacity: Int = 255,
strokeWeight: Float = 0.5f,
canvasObject: CanvasObject,
onFocusedFn: (() -> Boolean) = { false },
onClickFn: () -> Boolean = { false }
): CircleARMarker {
return CircleARMarker(
): ARMarker {
return ARMarkerImpl(
null,
null,
location,
elevation,
actualDiameter,
color,
strokeColor,
opacity,
strokeWeight,
canvasObject,
onFocusedFn,
onClickFn
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,24 @@ import com.kylecorry.trail_sense.shared.canvas.PixelCircle

class ARMarkerLayer : ARLayer {

private val markers = mutableListOf<CircleARMarker>()
private val markers = mutableListOf<ARMarker>()
private val lock = Any()
private val potentialFocusPoints = mutableListOf<Pair<CircleARMarker, PixelCircle>>()
private val potentialFocusPoints = mutableListOf<Pair<ARMarker, PixelCircle>>()

fun setMarkers(markers: List<CircleARMarker>) {
fun setMarkers(markers: List<ARMarker>) {
synchronized(lock) {
this.markers.clear()
this.markers.addAll(markers)
}
}

fun addMarker(marker: CircleARMarker) {
fun addMarker(marker: ARMarker) {
synchronized(lock) {
markers.add(marker)
}
}

fun removeMarker(marker: CircleARMarker) {
fun removeMarker(marker: ARMarker) {
synchronized(lock) {
markers.remove(marker)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ import java.time.Duration
import java.time.LocalDate
import java.time.ZoneId
import java.time.ZonedDateTime
import kotlin.math.sqrt

// TODO: Support arguments for default layer visibility (ex. coming from astronomy, enable only sun/moon)
class AugmentedRealityFragment : BoundFragment<FragmentAugmentedRealityBinding>() {
Expand Down Expand Up @@ -70,12 +69,12 @@ class AugmentedRealityFragment : BoundFragment<FragmentAugmentedRealityBinding>(
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

// TODO: Show paths
observeFlow(beaconRepo.getBeacons()) {
beaconLayer.setBeacons(it)
}

binding.camera.setScaleType(PreviewView.ScaleType.FIT_CENTER)
binding.camera.setShowTorch(false)

// TODO: Show azimuth / altitude
binding.linearCompass.showAzimuthArrow = false
Expand All @@ -91,9 +90,6 @@ class AugmentedRealityFragment : BoundFragment<FragmentAugmentedRealityBinding>(
binding.arView.start()

// TODO: Move this to the AR view
// TODO: Allow user to turn camera off
// TODO: Allow zoom when camera is off
// TODO: Allow use without sensors
requestCamera {
if (it) {
binding.camera.start(
Expand Down Expand Up @@ -148,46 +144,65 @@ class AugmentedRealityFragment : BoundFragment<FragmentAugmentedRealityBinding>(
val locationSubsystem = LocationSubsystem.getInstance(requireContext())
val location = locationSubsystem.location

val moonBeforePathObject = CircleCanvasObject(
Color.WHITE,
opacity = 20
)

val moonAfterPathObject = CircleCanvasObject(
Color.WHITE,
opacity = 127
)

val sunBeforePathObject = CircleCanvasObject(
AppColor.Yellow.color,
opacity = 20
)

val sunAfterPathObject = CircleCanvasObject(
AppColor.Yellow.color,
opacity = 127
)

val moonPositions = Time.getReadings(
LocalDate.now(),
ZoneId.systemDefault(),
Duration.ofMinutes(15)
) {
val alpha = if (it.isBefore(ZonedDateTime.now())) {
20
val obj = if (it.isBefore(ZonedDateTime.now())) {
moonBeforePathObject
} else {
127
moonAfterPathObject
}

CircleARMarker.horizon(
ARMarkerImpl.horizon(
AugmentedRealityView.HorizonCoordinate(
astro.getMoonAzimuth(location, it).value,
astro.getMoonAltitude(location, it),
true
),
1f,
Color.WHITE,
opacity = alpha
obj
)
}.map { it.value }

val sunPositions = Time.getReadings(
LocalDate.now(), ZoneId.systemDefault(), Duration.ofMinutes(15)
) {
val alpha = if (it.isBefore(ZonedDateTime.now())) {
20
val obj = if (it.isBefore(ZonedDateTime.now())) {
sunBeforePathObject
} else {
127
sunAfterPathObject
}
CircleARMarker.horizon(

ARMarkerImpl.horizon(
AugmentedRealityView.HorizonCoordinate(
astro.getSunAzimuth(location, it).value,
astro.getSunAltitude(location, it),
true
),
1f,
AppColor.Yellow.color,
opacity = alpha
obj
)
}.map { it.value }

Expand All @@ -197,28 +212,28 @@ class AugmentedRealityFragment : BoundFragment<FragmentAugmentedRealityBinding>(
val sunAltitude = astro.getSunAltitude(location)
val sunAzimuth = astro.getSunAzimuth(location).value

val moon = CircleARMarker.horizon(
val moon = ARMarkerImpl.horizon(
AugmentedRealityView.HorizonCoordinate(
moonAzimuth,
moonAltitude,
true
),
2f,
Color.WHITE,
CircleCanvasObject(Color.WHITE),
onFocusedFn = {
binding.arView.focusText = getString(R.string.moon)
true
}
)

val sun = CircleARMarker.horizon(
val sun = ARMarkerImpl.horizon(
AugmentedRealityView.HorizonCoordinate(
sunAzimuth,
sunAltitude,
true
),
2f,
AppColor.Yellow.color,
CircleCanvasObject(AppColor.Yellow.color),
onFocusedFn = {
binding.arView.focusText = getString(R.string.sun)
true
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.kylecorry.trail_sense.tools.augmented_reality

import android.graphics.Color
import androidx.annotation.ColorInt
import com.kylecorry.andromeda.canvas.ICanvasDrawer
import com.kylecorry.trail_sense.shared.canvas.PixelCircle

interface CanvasObject {
// TODO: Use a pixel region instead of a circle
fun draw(drawer: ICanvasDrawer, area: PixelCircle)
}

class CircleCanvasObject(
@ColorInt
private val color: Int,
@ColorInt
private val strokeColor: Int? = null,
private val opacity: Int = 255,
private val strokeWeight: Float = 0.5f,
) : CanvasObject {
override fun draw(drawer: ICanvasDrawer, area: PixelCircle) {
val size = area.radius * 2f
drawer.noTint()
if (strokeColor != null && strokeColor != Color.TRANSPARENT) {
drawer.stroke(strokeColor)
drawer.strokeWeight(drawer.dp(strokeWeight))
} else {
drawer.noStroke()
}
if (color != Color.TRANSPARENT) {
drawer.fill(color)
drawer.opacity(opacity)
drawer.circle(area.center.x, area.center.y, size)
}
}
}

0 comments on commit 92ede16

Please sign in to comment.