Files
sml-projects/functional-core/app/click-points.sml

128 lines
3.8 KiB
Standard ML
Raw Normal View History

signature CLICK_POINTS =
sig
val generate: int * int * int -> Real32.real vector
val getClickPositionFromMouse: AppType.app_type -> (int * int) option
val getDrawDot: Real32.real * Real32.real * int * int -> Real32.real vector
val getDrawDotRgb:
Real32.real
* Real32.real
* Real32.real
* Real32.real
* Real32.real
* int
* int
-> Real32.real vector
(* two below functions convert pixel coordinates to normalised device coordinates *)
val xposToNdc: Real32.real * int * int * Real32.real -> Real32.real
val yposToNdc: Real32.real * int * int * Real32.real -> Real32.real
end
structure ClickPoints :> CLICK_POINTS =
2024-08-08 00:18:03 +01:00
struct
fun generate (start, finish, numPoints) =
2024-08-08 00:18:03 +01:00
let
val difference = finish - start
val increment = Real32.fromInt difference / Real32.fromInt numPoints
2024-08-08 00:18:03 +01:00
val start = Real32.fromInt start
in
Vector.tabulate (numPoints + 1, fn idx =>
(Real32.fromInt idx * increment) + start)
2024-08-08 00:18:03 +01:00
end
(*
* Range to detect from clickable position.
* For example, if we have a clickable position at (x, y) = (500, 500),
* with a range of 15, we can detect clicks targeting this position
* from top left at (485, 485) to bottom right at (515, 515).
* *)
val range = 15.0
2024-08-08 00:18:03 +01:00
fun getClickPos (clickPoints, mousePos, idx) =
if idx = Vector.length clickPoints then
NONE
else
let
val curPos = Vector.sub (clickPoints, idx)
in
if mousePos < curPos - range orelse mousePos > curPos + range then
getClickPos (clickPoints, mousePos, idx + 1)
else
SOME idx
end
2024-08-08 00:18:03 +01:00
fun getClickPositionFromMouse (app: AppType.app_type) =
case getClickPos (#xClickPoints app, #mouseX app, 0) of
SOME hIdx =>
(case getClickPos (#yClickPoints app, #mouseY app, 0) of
SOME vIdx => SOME (hIdx, vIdx)
| NONE => NONE)
| NONE => NONE
fun getDrawDot (xpos, ypos, windowWidth, windowHeight) =
let
(* calculate normalised device coordinates *)
val halfWidth = Real32.fromInt (windowWidth div 2)
val halfHeight = Real32.fromInt (windowHeight div 2)
val hpos = xpos - halfWidth
val vpos = ~(ypos - halfHeight)
(* coordinates to form small box around clicked area *)
val left = (hpos - 5.0) / halfWidth
val right = (hpos + 5.0) / halfWidth
val bottom = (vpos - 5.0) / halfHeight
val top = (vpos + 5.0) / halfHeight
in
Ndc.ltrbToVertex (left, top, right, bottom)
end
fun getDrawDotRgb (xpos, ypos, r, g, b, windowWidth, windowHeight) =
let
(* calculate normalised device coordinates *)
val halfWidth = Real32.fromInt (windowWidth div 2)
val halfHeight = Real32.fromInt (windowHeight div 2)
val hpos = xpos - halfWidth
val vpos = ~(ypos - halfHeight)
(* coordinates to form small box around clicked area *)
val left = (hpos - 5.0) / halfWidth
val right = (hpos + 5.0) / halfWidth
val bottom = (vpos - 5.0) / halfHeight
val top = (vpos + 5.0) / halfHeight
in
Ndc.ltrbToVertexRgb (left, top, right, bottom, r, g, b)
end
fun xposToNdc (xpos, windowWidth, windowHeight, halfWidth) =
let
val xpos = xpos - halfWidth
in
if windowWidth > windowHeight then
let
val difference = windowWidth - windowHeight
val offset = Real32.fromInt (difference div 2)
in
xpos / (halfWidth - offset)
end
else
xpos / halfWidth
end
fun yposToNdc (ypos, windowWidth, windowHeight, halfHeight) =
let
val ypos = ~(ypos - halfHeight)
in
if windowHeight > windowWidth then
let
val difference = windowHeight - windowWidth
val offset = Real32.fromInt (difference div 2)
in
ypos / (halfHeight - offset)
end
else
ypos / halfHeight
end
2024-08-08 00:18:03 +01:00
end