2024-03-19 05:36:58 -07:00
|
|
|
/* Space
|
|
|
|
|
|
|
|
Provides various definitions for converting from one standard of measurement to another.
|
|
|
|
|
|
|
|
Ultimately the user's window ppcm (pixels-per-centimeter) determins how all virtual metric conventions are handled.
|
|
|
|
*/
|
2024-02-10 00:40:53 -08:00
|
|
|
package sectr
|
|
|
|
|
|
|
|
import rl "vendor:raylib"
|
|
|
|
|
|
|
|
// The points to pixels and pixels to points are our only reference to accurately converting
|
|
|
|
// an object from world space to screen-space.
|
2024-03-19 05:36:58 -07:00
|
|
|
// This prototype engine will have all its spacial unit base for distances in virtual pixels.
|
2024-02-10 00:40:53 -08:00
|
|
|
|
2024-02-13 14:16:39 -08:00
|
|
|
Inches_To_CM :: cast(f32) 2.54
|
|
|
|
Points_Per_CM :: cast(f32) 28.3465
|
|
|
|
CM_Per_Point :: cast(f32) 1.0 / DPT_DPCM
|
|
|
|
CM_Per_Pixel :: cast(f32) 1.0 / DPT_PPCM
|
|
|
|
DPT_DPCM :: cast(f32) 72.0 * Inches_To_CM // 182.88 points/dots per cm
|
|
|
|
DPT_PPCM :: cast(f32) 96.0 * Inches_To_CM // 243.84 pixels per cm
|
2024-02-10 00:40:53 -08:00
|
|
|
|
|
|
|
when ODIN_OS == OS_Type.Windows {
|
2024-02-13 14:16:39 -08:00
|
|
|
op_default_dpcm :: 72.0 * Inches_To_CM
|
|
|
|
os_default_ppcm :: 96.0 * Inches_To_CM
|
2024-02-22 18:19:29 -08:00
|
|
|
// 1 inch = 2.54 cm, 96 inch * 2.54 = 243.84 DPCM
|
2024-02-10 00:40:53 -08:00
|
|
|
}
|
|
|
|
|
2024-02-12 17:52:56 -08:00
|
|
|
//region Unit Conversion Impl
|
|
|
|
|
2024-02-23 06:36:23 -08:00
|
|
|
// cm_to_points :: proc( cm : f32 ) -> f32 {
|
2024-02-12 17:52:56 -08:00
|
|
|
|
|
|
|
// }
|
|
|
|
|
2024-02-11 21:35:22 -08:00
|
|
|
// points_to_cm :: proc( points : f32 ) -> f32 {
|
2024-02-12 17:52:56 -08:00
|
|
|
// screen_dpc := get_state().app_window.dpc
|
|
|
|
// cm_per_pixel := 1.0 / screen_dpc
|
|
|
|
// pixels := points * DPT_DPC * cm_per_pixel
|
|
|
|
// return points *
|
2024-02-11 21:35:22 -08:00
|
|
|
// }
|
|
|
|
|
2024-03-14 21:02:28 -07:00
|
|
|
f32_cm_to_pixels :: #force_inline proc "contextless"(cm: f32) -> f32 {
|
2024-02-13 14:16:39 -08:00
|
|
|
screen_ppcm := get_state().app_window.ppcm
|
|
|
|
return cm * screen_ppcm
|
2024-02-10 00:40:53 -08:00
|
|
|
}
|
|
|
|
|
2024-03-14 21:02:28 -07:00
|
|
|
f32_pixels_to_cm :: #force_inline proc "contextless"(pixels: f32) -> f32 {
|
2024-02-13 14:16:39 -08:00
|
|
|
screen_ppcm := get_state().app_window.ppcm
|
|
|
|
cm_per_pixel := 1.0 / screen_ppcm
|
2024-02-12 17:52:56 -08:00
|
|
|
return pixels * cm_per_pixel
|
2024-02-10 00:40:53 -08:00
|
|
|
}
|
|
|
|
|
2024-03-14 21:02:28 -07:00
|
|
|
f32_points_to_pixels :: #force_inline proc "contextless"(points: f32) -> f32 {
|
2024-02-13 14:16:39 -08:00
|
|
|
screen_ppcm := get_state().app_window.ppcm
|
|
|
|
cm_per_pixel := 1.0 / screen_ppcm
|
|
|
|
return points * DPT_PPCM * cm_per_pixel
|
2024-02-10 00:40:53 -08:00
|
|
|
}
|
|
|
|
|
2024-03-14 21:02:28 -07:00
|
|
|
f32_pixels_to_points :: #force_inline proc "contextless"(pixels: f32) -> f32 {
|
2024-02-13 14:16:39 -08:00
|
|
|
screen_ppcm := get_state().app_window.ppcm
|
|
|
|
cm_per_pixel := 1.0 / screen_ppcm
|
2024-02-12 17:52:56 -08:00
|
|
|
return pixels * cm_per_pixel * Points_Per_CM
|
|
|
|
}
|
|
|
|
|
2024-03-14 21:02:28 -07:00
|
|
|
vec2_cm_to_pixels :: #force_inline proc "contextless"(v: Vec2) -> Vec2 {
|
2024-02-13 14:16:39 -08:00
|
|
|
screen_ppcm := get_state().app_window.ppcm
|
|
|
|
return v * screen_ppcm
|
2024-02-12 17:52:56 -08:00
|
|
|
}
|
|
|
|
|
2024-03-14 21:02:28 -07:00
|
|
|
vec2_pixels_to_cm :: #force_inline proc "contextless"(v: Vec2) -> Vec2 {
|
2024-02-13 14:16:39 -08:00
|
|
|
screen_ppcm := get_state().app_window.ppcm
|
|
|
|
cm_per_pixel := 1.0 / screen_ppcm
|
2024-02-12 17:52:56 -08:00
|
|
|
return v * cm_per_pixel
|
2024-02-10 00:40:53 -08:00
|
|
|
}
|
|
|
|
|
2024-03-14 21:02:28 -07:00
|
|
|
vec2_points_to_pixels :: #force_inline proc "contextless"(vpoints: Vec2) -> Vec2 {
|
2024-02-13 14:16:39 -08:00
|
|
|
screen_ppcm := get_state().app_window.ppcm
|
|
|
|
cm_per_pixel := 1.0 / screen_ppcm
|
|
|
|
return vpoints * DPT_PPCM * cm_per_pixel
|
2024-02-11 20:00:06 -08:00
|
|
|
}
|
|
|
|
|
2024-03-14 21:02:28 -07:00
|
|
|
range2_cm_to_pixels :: #force_inline proc "contextless"( range : Range2 ) -> Range2 {
|
2024-03-02 07:24:09 -08:00
|
|
|
screen_ppcm := get_state().app_window.ppcm
|
|
|
|
result := Range2 { pts = { range.min * screen_ppcm, range.max * screen_ppcm }}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2024-03-14 21:02:28 -07:00
|
|
|
range2_pixels_to_cm :: #force_inline proc "contextless"( range : Range2 ) -> Range2 {
|
2024-03-02 07:24:09 -08:00
|
|
|
screen_ppcm := get_state().app_window.ppcm
|
|
|
|
cm_per_pixel := 1.0 / screen_ppcm
|
|
|
|
result := Range2 { pts = { range.min * cm_per_pixel, range.max * cm_per_pixel }}
|
|
|
|
return result
|
|
|
|
}
|
2024-02-12 17:52:56 -08:00
|
|
|
|
2024-02-23 06:36:23 -08:00
|
|
|
// vec2_points_to_cm :: proc( vpoints : Vec2 ) -> Vec2 {
|
2024-02-12 17:52:56 -08:00
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
//endregion
|
|
|
|
|
2024-02-10 00:40:53 -08:00
|
|
|
Camera :: rl.Camera2D
|
|
|
|
|
2024-03-08 00:34:21 -08:00
|
|
|
CameraZoomMode :: enum u32 {
|
|
|
|
Digital,
|
|
|
|
Smooth,
|
|
|
|
}
|
|
|
|
|
2024-02-12 17:52:56 -08:00
|
|
|
// TODO(Ed) : I'm not sure making the size and extent types distinct has made things easier or more difficult in Odin..
|
|
|
|
// The lack of operator overloads is going to make any sort of nice typesystem
|
|
|
|
// for doing lots of math or phyiscs more error prone or filled with proc wrappers
|
2024-02-11 20:00:06 -08:00
|
|
|
AreaSize :: distinct Vec2
|
|
|
|
|
|
|
|
Bounds2 :: struct {
|
2024-02-12 17:52:56 -08:00
|
|
|
top_left, bottom_right: Vec2,
|
2024-02-11 20:00:06 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
BoundsCorners2 :: struct {
|
2024-02-12 17:52:56 -08:00
|
|
|
top_left, top_right, bottom_left, bottom_right: Vec2,
|
2024-02-11 20:00:06 -08:00
|
|
|
}
|
|
|
|
|
2024-02-13 14:16:39 -08:00
|
|
|
Extents2 :: distinct Vec2
|
2024-02-11 20:00:06 -08:00
|
|
|
Extents2i :: distinct Vec2i
|
|
|
|
|
2024-02-12 17:52:56 -08:00
|
|
|
WS_Pos :: struct {
|
|
|
|
tile_id : Vec2i,
|
|
|
|
rel : Vec2,
|
|
|
|
}
|
|
|
|
|
|
|
|
bounds2_radius :: proc(bounds: Bounds2) -> f32 {
|
2024-02-13 14:16:39 -08:00
|
|
|
return max( bounds.bottom_right.x, bounds.top_left.y )
|
2024-02-11 20:00:06 -08:00
|
|
|
}
|
|
|
|
|
2024-02-12 17:52:56 -08:00
|
|
|
extent_from_size :: proc(size: AreaSize) -> Extents2 {
|
2024-02-13 14:16:39 -08:00
|
|
|
return transmute(Extents2) size * 2.0
|
2024-02-11 20:00:06 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
screen_size :: proc "contextless" () -> AreaSize {
|
|
|
|
extent := get_state().app_window.extent
|
2024-02-13 14:16:39 -08:00
|
|
|
return transmute(AreaSize) ( extent * 2.0 )
|
2024-02-11 20:00:06 -08:00
|
|
|
}
|
|
|
|
|
2024-05-07 23:26:39 -07:00
|
|
|
screen_get_bounds :: #force_inline proc "contextless" () -> Range2 {
|
|
|
|
state := get_state(); using state
|
2024-05-09 01:02:33 -07:00
|
|
|
surface_extent := state.app_window.extent
|
|
|
|
bottom_left := Vec2 { -surface_extent.x, -surface_extent.y}
|
|
|
|
top_right := Vec2 { surface_extent.x, surface_extent.y}
|
2024-05-07 23:26:39 -07:00
|
|
|
return range2( bottom_left, top_right )
|
|
|
|
}
|
|
|
|
|
2024-03-14 21:02:28 -07:00
|
|
|
screen_get_corners :: #force_inline proc "contextless"() -> BoundsCorners2 {
|
2024-03-07 14:58:28 -08:00
|
|
|
state := get_state(); using state
|
2024-02-11 20:00:06 -08:00
|
|
|
screen_extent := state.app_window.extent
|
2024-02-13 14:16:39 -08:00
|
|
|
top_left := Vec2 { -screen_extent.x, screen_extent.y }
|
|
|
|
top_right := Vec2 { screen_extent.x, screen_extent.y }
|
|
|
|
bottom_left := Vec2 { -screen_extent.x, -screen_extent.y }
|
|
|
|
bottom_right := Vec2 { screen_extent.x, -screen_extent.y }
|
|
|
|
return { top_left, top_right, bottom_left, bottom_right }
|
2024-02-10 00:40:53 -08:00
|
|
|
}
|
|
|
|
|
2024-05-09 01:02:33 -07:00
|
|
|
// TODO(Ed): Use a cam/workspace context instead (when multiple workspaces viewproting supported)
|
2024-03-14 21:02:28 -07:00
|
|
|
view_get_bounds :: #force_inline proc "contextless"() -> Range2 {
|
|
|
|
state := get_state(); using state
|
|
|
|
cam := & project.workspace.cam
|
|
|
|
screen_extent := state.app_window.extent
|
|
|
|
cam_zoom_ratio := 1.0 / cam.zoom
|
|
|
|
bottom_left := Vec2 { cam.target.x, -cam.target.y } + Vec2 { -screen_extent.x, -screen_extent.y} * cam_zoom_ratio
|
|
|
|
top_right := Vec2 { cam.target.x, -cam.target.y } + Vec2 { screen_extent.x, screen_extent.y} * cam_zoom_ratio
|
|
|
|
return range2( bottom_left, top_right )
|
2024-02-11 20:00:06 -08:00
|
|
|
}
|
|
|
|
|
2024-05-09 01:02:33 -07:00
|
|
|
// TODO(Ed): Use a cam/workspace context instead (when multiple workspace viewproting)
|
2024-03-14 21:02:28 -07:00
|
|
|
view_get_corners :: #force_inline proc "contextless"() -> BoundsCorners2 {
|
2024-02-13 14:16:39 -08:00
|
|
|
state := get_state(); using state
|
|
|
|
cam := & project.workspace.cam
|
2024-02-11 20:00:06 -08:00
|
|
|
cam_zoom_ratio := 1.0 / cam.zoom
|
2024-02-13 14:16:39 -08:00
|
|
|
screen_extent := state.app_window.extent * cam_zoom_ratio
|
|
|
|
top_left := cam.target + Vec2 { -screen_extent.x, screen_extent.y }
|
|
|
|
top_right := cam.target + Vec2 { screen_extent.x, screen_extent.y }
|
|
|
|
bottom_left := cam.target + Vec2 { -screen_extent.x, -screen_extent.y }
|
|
|
|
bottom_right := cam.target + Vec2 { screen_extent.x, -screen_extent.y }
|
|
|
|
return { top_left, top_right, bottom_left, bottom_right }
|
2024-02-11 20:00:06 -08:00
|
|
|
}
|
|
|
|
|
2024-05-09 01:02:33 -07:00
|
|
|
render_to_surface_pos :: #force_inline proc "contextless" (pos : Vec2) -> Vec2 {
|
|
|
|
extent := & get_state().app_window.extent
|
|
|
|
result := Vec2 {
|
|
|
|
pos.x - extent.x,
|
|
|
|
pos.y * -1 + extent.y
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
render_to_ws_view_pos :: #force_inline proc "contextless" (pos : Vec2) -> Vec2 {
|
|
|
|
return {}
|
|
|
|
}
|
|
|
|
|
|
|
|
surface_to_ws_view_pos :: #force_inline proc "contextless" (pos: Vec2) -> Vec2 {
|
2024-02-13 14:16:39 -08:00
|
|
|
state := get_state(); using state
|
|
|
|
cam := & project.workspace.cam
|
2024-05-09 01:02:33 -07:00
|
|
|
result := Vec2 { cam.target.x, -cam.target.y} + Vec2 { pos.x, pos.y } * (1 / cam.zoom)
|
2024-03-02 07:24:09 -08:00
|
|
|
return result
|
2024-02-11 21:35:22 -08:00
|
|
|
}
|
|
|
|
|
2024-05-09 01:02:33 -07:00
|
|
|
// (Surface) Centered screen space to conventional screen space used for rendering
|
|
|
|
surface_to_render_pos :: #force_inline proc "contextless" (pos : Vec2) -> Vec2 {
|
|
|
|
screen_extent := transmute(Vec2) get_state().app_window.extent
|
|
|
|
return pos * {1, -1} + { screen_extent.x, screen_extent.y }
|
2024-02-11 20:00:06 -08:00
|
|
|
}
|
|
|
|
|
2024-05-09 01:02:33 -07:00
|
|
|
// TODO(Ed): These should assume a cam_context or have the ability to provide it in params
|
|
|
|
|
|
|
|
// Extent of workspace view (currently hardcoded to the app window's extent, eventually will be based on a viewport object's extent field)
|
|
|
|
// TODO(Ed): Support a position which would not be centered on the screen if in a viewport
|
|
|
|
ws_view_extent :: #force_inline proc "contextless"() -> Extents2 {
|
2024-02-13 14:16:39 -08:00
|
|
|
state := get_state(); using state
|
2024-02-11 20:00:06 -08:00
|
|
|
cam_zoom_ratio := 1.0 / project.workspace.cam.zoom
|
|
|
|
return app_window.extent * cam_zoom_ratio
|
|
|
|
}
|
|
|
|
|
2024-05-09 01:02:33 -07:00
|
|
|
// Workspace view to surface space position
|
|
|
|
// TODO(Ed): Support a position which would not be centered on the screen if in a viewport
|
|
|
|
ws_view_to_surface_pos :: #force_inline proc "contextless"(position: Vec2) -> Vec2 {
|
|
|
|
return position
|
|
|
|
}
|
|
|
|
|
|
|
|
ws_view_to_render_pos :: #force_inline proc "contextless"(position: Vec2) -> Vec2 {
|
2024-02-13 14:16:39 -08:00
|
|
|
return { position.x, position.y * -1 }
|
2024-02-10 00:40:53 -08:00
|
|
|
}
|
|
|
|
|
2024-05-09 01:02:33 -07:00
|
|
|
// Workspace view to surface space position (zoom agnostic)
|
|
|
|
// TODO(Ed): Support a position which would not be centered on the screen if in a viewport
|
|
|
|
ws_view_to_surface_pos_no_zoom :: #force_inline proc "contextless"(position: Vec2) -> Vec2 {
|
|
|
|
state := get_state(); using state
|
|
|
|
cam_zoom_ratio := 1.0 / state.project.workspace.cam.zoom
|
|
|
|
return { position.x, position.y } * cam_zoom_ratio
|
|
|
|
}
|
|
|
|
|
|
|
|
// Workspace view to render space position (zoom agnostic)
|
|
|
|
// TODO(Ed): Support a position which would not be centered on the screen if in a viewport
|
|
|
|
ws_view_to_render_pos_no_zoom :: #force_inline proc "contextless"(position: Vec2) -> Vec2 {
|
2024-02-13 14:16:39 -08:00
|
|
|
state := get_state(); using state
|
2024-02-11 20:00:06 -08:00
|
|
|
cam_zoom_ratio := 1.0 / state.project.workspace.cam.zoom
|
2024-05-09 01:02:33 -07:00
|
|
|
return { position.x, position.y } * cam_zoom_ratio
|
2024-02-10 00:40:53 -08:00
|
|
|
}
|