2025-09-06 19:06:30 +03:00
# include "WorldGrid.hpp"
# include "GridManager.hpp"
2025-09-15 16:58:57 +03:00
# include "Helpers/MathExtras.hpp"
2025-09-06 19:06:30 +03:00
# include "MainProgram.hpp"
2025-09-20 18:44:09 +03:00
# include <include/core/SkFontMetrics.h>
2025-09-06 19:06:30 +03:00
sk_sp < SkRuntimeEffect > WorldGrid : : circlePointEffect ;
sk_sp < SkRuntimeEffect > WorldGrid : : squarePointEffect ;
sk_sp < SkRuntimeEffect > WorldGrid : : squareLinesEffect ;
sk_sp < SkRuntimeEffect > WorldGrid : : ruledEffect ;
Vector2f WorldGrid : : oldWindowSize = Vector2f { 0.0f , 0.0f } ;
2025-09-25 10:21:47 +03:00
unsigned WorldGrid : : GRID_UNIT_PIXEL_SIZE = 50 ;
2025-09-06 19:06:30 +03:00
// NOTE: Skia shaders return premultiplied alpha colors
const char * circlePointShaderCode = R " V(
uniform vec4 gridColor ;
2025-09-15 16:58:57 +03:00
uniform float mainGridScale ;
uniform vec2 mainGridClosestPoint ;
uniform float mainGridPointSize ;
uniform float divGridAlphaFrac ;
uniform float divGridScale ;
uniform vec2 divGridClosestPoint ;
uniform float divGridPointSize ;
float get_color_val_circle_point ( float2 fCoord , float gScale , float gPointSize , float2 gClosestPoint ) {
float2 modCoord = mod ( fCoord - gClosestPoint + float2 ( 0.5 * gScale ) , float2 ( gScale ) ) ;
2025-09-16 10:16:07 +03:00
return 1.0 - smoothstep ( gPointSize , gPointSize + 1.0 , distance ( modCoord , float2 ( 0.5 * gScale ) ) ) ;
2025-09-15 16:58:57 +03:00
}
2025-09-06 19:06:30 +03:00
vec4 main ( float2 fragCoord ) {
2025-09-15 16:58:57 +03:00
float mainGridColor = get_color_val_circle_point ( fragCoord , mainGridScale , mainGridPointSize , mainGridClosestPoint ) ;
float divGridColor = get_color_val_circle_point ( fragCoord , divGridScale , divGridPointSize , divGridClosestPoint ) * divGridAlphaFrac ;
float fin = max ( mainGridColor , divGridColor ) ;
2025-09-06 19:06:30 +03:00
vec4 pointColorUnmultiplied = vec4 ( gridColor . rgb , 1.0 ) ;
return pointColorUnmultiplied * gridColor . a * fin ;
} ) V " ;
const char * squarePointShaderCode = R " V(
uniform vec4 gridColor ;
2025-09-15 16:58:57 +03:00
uniform float mainGridScale ;
uniform vec2 mainGridClosestPoint ;
uniform float mainGridPointSize ;
uniform float divGridAlphaFrac ;
uniform float divGridScale ;
uniform vec2 divGridClosestPoint ;
uniform float divGridPointSize ;
float get_color_val_square_point ( float2 fCoord , float gScale , float gPointSize , float2 gClosestPoint ) {
float2 modCoord = mod ( fCoord - gClosestPoint + float2 ( 0.5 * gScale ) , float2 ( gScale ) ) ;
float2 difToPointCenter = abs ( modCoord - float2 ( 0.5 * gScale ) ) ;
2025-09-16 10:16:07 +03:00
float m1 = 1.0 - smoothstep ( gPointSize , gPointSize + 1.0 , difToPointCenter . x ) ;
float m2 = 1.0 - smoothstep ( gPointSize , gPointSize + 1.0 , difToPointCenter . y ) ;
2025-09-15 16:58:57 +03:00
return m1 * m2 ;
}
2025-09-06 19:06:30 +03:00
vec4 main ( float2 fragCoord ) {
2025-09-15 16:58:57 +03:00
float mainGridColor = get_color_val_square_point ( fragCoord , mainGridScale , mainGridPointSize , mainGridClosestPoint ) ;
float divGridColor = get_color_val_square_point ( fragCoord , divGridScale , divGridPointSize , divGridClosestPoint ) * divGridAlphaFrac ;
float fin = max ( mainGridColor , divGridColor ) ;
2025-09-06 19:06:30 +03:00
vec4 pointColorUnmultiplied = vec4 ( gridColor . rgb , 1.0 ) ;
return pointColorUnmultiplied * gridColor . a * fin ;
} ) V " ;
const char * squareLinesShaderCode = R " V(
uniform vec4 gridColor ;
2025-09-15 16:58:57 +03:00
uniform float mainGridScale ;
uniform vec2 mainGridClosestPoint ;
uniform float mainGridPointSize ;
uniform float divGridAlphaFrac ;
uniform float divGridScale ;
uniform vec2 divGridClosestPoint ;
uniform float divGridPointSize ;
float get_color_val_square_lines ( float2 fCoord , float gScale , float gPointSize , float2 gClosestPoint ) {
float2 modCoord = mod ( fCoord - gClosestPoint + float2 ( 0.5 * gScale ) , float2 ( gScale ) ) ;
float2 difToPointCenter = abs ( modCoord - float2 ( 0.5 * gScale ) ) ;
2025-09-16 10:16:07 +03:00
float m1 = 1.0 - smoothstep ( gPointSize , gPointSize + 1.0 , difToPointCenter . x ) ;
float m2 = 1.0 - smoothstep ( gPointSize , gPointSize + 1.0 , difToPointCenter . y ) ;
2025-09-15 16:58:57 +03:00
return max ( m1 , m2 ) ;
}
2025-09-06 19:06:30 +03:00
vec4 main ( float2 fragCoord ) {
2025-09-15 16:58:57 +03:00
float mainGridColor = get_color_val_square_lines ( fragCoord , mainGridScale , mainGridPointSize , mainGridClosestPoint ) ;
float divGridColor = get_color_val_square_lines ( fragCoord , divGridScale , divGridPointSize , divGridClosestPoint ) * divGridAlphaFrac ;
float fin = max ( mainGridColor , divGridColor ) ;
2025-09-06 19:06:30 +03:00
vec4 pointColorUnmultiplied = vec4 ( gridColor . rgb , 1.0 ) ;
return pointColorUnmultiplied * gridColor . a * fin ;
} ) V " ;
const char * ruledShaderCode = R " V(
uniform vec4 gridColor ;
2025-09-15 16:58:57 +03:00
uniform float mainGridScale ;
uniform vec2 mainGridClosestPoint ;
uniform float mainGridPointSize ;
2025-09-06 19:06:30 +03:00
2025-09-15 16:58:57 +03:00
uniform float divGridAlphaFrac ;
uniform float divGridScale ;
uniform vec2 divGridClosestPoint ;
uniform float divGridPointSize ;
float get_color_val_ruled ( float2 fCoord , float gScale , float gPointSize , float2 gClosestPoint ) {
float2 modCoord = mod ( fCoord - gClosestPoint + float2 ( 0.5 * gScale ) , float2 ( gScale ) ) ;
float2 difToPointCenter = abs ( modCoord - float2 ( 0.5 * gScale ) ) ;
2025-09-16 10:16:07 +03:00
return 1.0 - smoothstep ( gPointSize , gPointSize + 1.0 , difToPointCenter . y ) ;
2025-09-15 16:58:57 +03:00
}
2025-09-06 19:06:30 +03:00
vec4 main ( float2 fragCoord ) {
2025-09-15 16:58:57 +03:00
float mainGridColor = get_color_val_ruled ( fragCoord , mainGridScale , mainGridPointSize , mainGridClosestPoint ) ;
float divGridColor = get_color_val_ruled ( fragCoord , divGridScale , divGridPointSize , divGridClosestPoint ) * divGridAlphaFrac ;
float fin = max ( mainGridColor , divGridColor ) ;
2025-09-06 19:06:30 +03:00
vec4 pointColorUnmultiplied = vec4 ( gridColor . rgb , 1.0 ) ;
return pointColorUnmultiplied * gridColor . a * fin ;
} ) V " ;
sk_sp < SkRuntimeEffect > WorldGrid : : compile_effect_shader_init ( const char * shaderName , const char * shaderCode ) {
auto [ effect , err ] = SkRuntimeEffect : : MakeForShader ( SkString ( shaderCode ) ) ;
if ( ! err . isEmpty ( ) ) {
std : : cout < < " [GridManager::compile_effect_shader_init] " < < shaderName < < " shader construction error \n " ;
std : : cout < < err . c_str ( ) < < std : : endl ;
throw std : : runtime_error ( " Shader Compile Failure " ) ;
}
return effect ;
}
2025-09-15 16:58:57 +03:00
sk_sp < SkShader > WorldGrid : : get_shader ( GridType gType , const ShaderData & shaderData ) {
2025-09-06 19:06:30 +03:00
sk_sp < SkRuntimeEffect > runtimeEffect ;
switch ( gType ) {
case GridType : : CIRCLE_POINTS :
runtimeEffect = circlePointEffect ;
break ;
case GridType : : SQUARE_POINTS :
runtimeEffect = squarePointEffect ;
break ;
case GridType : : SQUARE_LINES :
runtimeEffect = squareLinesEffect ;
break ;
case GridType : : RULED :
runtimeEffect = ruledEffect ;
break ;
}
SkRuntimeShaderBuilder builder ( runtimeEffect ) ;
2025-09-15 16:58:57 +03:00
builder . uniform ( " gridColor " ) = shaderData . gridColor ;
builder . uniform ( " mainGridScale " ) = std : : min ( shaderData . mainGridScale , 100000.0f ) ; // Limit placed so that having a massive floating point value doesnt tank precision in the shader
builder . uniform ( " mainGridClosestPoint " ) . set ( shaderData . mainGridClosestPoint . data ( ) , 2 ) ;
builder . uniform ( " divGridAlphaFrac " ) = shaderData . divGridAlphaFrac ;
builder . uniform ( " divGridScale " ) = std : : min ( shaderData . divGridScale , 100000.0f ) ;
builder . uniform ( " divGridClosestPoint " ) . set ( shaderData . divGridClosestPoint . data ( ) , 2 ) ;
2025-09-13 14:31:29 +03:00
switch ( gType ) {
case GridType : : CIRCLE_POINTS :
case GridType : : SQUARE_POINTS :
2025-09-15 16:58:57 +03:00
builder . uniform ( " mainGridPointSize " ) = shaderData . mainGridPointSize ;
builder . uniform ( " divGridPointSize " ) = shaderData . divGridPointSize ;
2025-09-13 14:31:29 +03:00
break ;
case GridType : : SQUARE_LINES :
case GridType : : RULED :
2025-09-15 16:58:57 +03:00
builder . uniform ( " mainGridPointSize " ) = 0.5f ;
builder . uniform ( " divGridPointSize " ) = 0.5f ;
2025-09-13 14:31:29 +03:00
break ;
}
2025-09-06 19:06:30 +03:00
sk_sp < SkShader > s = builder . makeShader ( ) ;
return s ;
}
2025-09-15 16:58:57 +03:00
Vector2f WorldGrid : : get_closest_grid_point ( const WorldVec & gridOffset , const WorldScalar & gridSize , const DrawData & drawData ) {
WorldVec fracCamPosOnGrid ;
2025-09-17 17:17:43 +03:00
fracCamPosOnGrid . x ( ) = ( drawData . cam . c . pos . x ( ) - gridOffset . x ( ) ) % gridSize ;
fracCamPosOnGrid . y ( ) = ( drawData . cam . c . pos . y ( ) - gridOffset . y ( ) ) % gridSize ;
2025-09-15 16:58:57 +03:00
WorldVec closestGridPoint ;
closestGridPoint . x ( ) = ( fracCamPosOnGrid . x ( ) < gridSize / WorldScalar ( 2 ) ) ? WorldScalar ( 0 ) : gridSize ;
closestGridPoint . y ( ) = ( fracCamPosOnGrid . y ( ) < gridSize / WorldScalar ( 2 ) ) ? WorldScalar ( 0 ) : gridSize ;
Vector2f closestGridPointScreenPos ;
closestGridPointScreenPos . x ( ) = static_cast < float > ( ( closestGridPoint . x ( ) - fracCamPosOnGrid . x ( ) ) / drawData . cam . c . inverseScale ) ;
closestGridPointScreenPos . y ( ) = static_cast < float > ( ( closestGridPoint . y ( ) - fracCamPosOnGrid . y ( ) ) / drawData . cam . c . inverseScale ) ;
return closestGridPointScreenPos ;
}
2025-09-16 09:35:03 +03:00
void WorldGrid : : set_remove_divisions_outwards ( bool v ) {
removeDivisionsOutwards = v ;
if ( removeDivisionsOutwards )
2025-09-23 19:53:45 +03:00
subdivisions = std : : max < uint32_t > ( subdivisions , 2 ) ;
2025-09-16 09:35:03 +03:00
}
void WorldGrid : : set_subdivisions ( uint32_t v ) {
subdivisions = v ;
if ( removeDivisionsOutwards )
2025-09-23 19:53:45 +03:00
subdivisions = std : : max < uint32_t > ( subdivisions , 2 ) ;
2025-09-16 09:35:03 +03:00
}
2025-09-24 13:32:50 +03:00
std : : string WorldGrid : : get_display_name ( ) {
return name . empty ( ) ? " New Grid " : name ;
2025-09-16 09:35:03 +03:00
}
2025-09-06 19:06:30 +03:00
void WorldGrid : : draw ( GridManager & gMan , SkCanvas * canvas , const DrawData & drawData ) {
2025-09-25 10:21:47 +03:00
coordinatesAxisOnBounds = false ;
coordinatesWillBeDrawn = false ;
2025-09-23 19:53:45 +03:00
if ( ! visible | | ( bounds . has_value ( ) & & ! SCollision : : collide ( drawData . cam . viewingAreaGenerousCollider , bounds . value ( ) ) ) )
2025-09-10 15:03:06 +03:00
return ;
2025-09-06 19:06:30 +03:00
if ( ! circlePointEffect ) {
circlePointEffect = compile_effect_shader_init ( " Circle Point " , circlePointShaderCode ) ;
squarePointEffect = compile_effect_shader_init ( " Square Point " , squarePointShaderCode ) ;
squareLinesEffect = compile_effect_shader_init ( " Square Lines " , squareLinesShaderCode ) ;
ruledEffect = compile_effect_shader_init ( " Ruled " , ruledShaderCode ) ;
}
2025-09-17 17:17:43 +03:00
WorldScalar sizeDetermineSubdivisionsToRemove = ( drawData . cam . c . inverseScale / size ) * WorldScalar ( GRID_UNIT_PIXEL_SIZE ) ;
2025-09-15 16:58:57 +03:00
SkPaint linePaint ;
2025-09-21 10:48:47 +03:00
WorldScalar divSize ;
2025-09-20 18:44:09 +03:00
WorldScalar gridCoordDivSize ;
2025-09-16 10:58:32 +03:00
if ( sizeDetermineSubdivisionsToRemove > WorldScalar ( 1 ) ) {
2025-09-15 16:58:57 +03:00
double divLogDouble , divLogFraction ;
2025-09-23 19:53:45 +03:00
divLogFraction = std : : modf ( static_cast < double > ( FixedPoint : : log ( sizeDetermineSubdivisionsToRemove , WorldScalar ( subdivisions = = 1 ? 2 : subdivisions ) ) ) , & divLogDouble ) ;
2025-09-15 16:58:57 +03:00
uint64_t divLog = divLogDouble ;
2025-09-16 10:58:32 +03:00
if ( divLog > = 2 & & ! removeDivisionsOutwards )
return ;
2025-09-23 19:53:45 +03:00
WorldScalar logMultiplier = FixedPoint : : exp_int_accurate < WorldScalar > ( divLog , subdivisions ) ;
2025-09-13 14:31:29 +03:00
2025-09-16 09:35:03 +03:00
WorldScalar subDivSize = size * logMultiplier ;
2025-09-23 19:53:45 +03:00
divSize = subDivSize * WorldScalar ( subdivisions ) ;
2025-09-16 09:35:03 +03:00
WorldScalar subDivWorldSize = subDivSize / drawData . cam . c . inverseScale ;
2025-09-15 16:58:57 +03:00
WorldScalar divWorldSize = divSize / drawData . cam . c . inverseScale ;
2025-09-06 19:06:30 +03:00
2025-09-21 10:48:47 +03:00
float floatDivWorldSize = static_cast < float > ( divWorldSize ) ;
2025-09-16 09:35:03 +03:00
float floatSubDivWorldSize = static_cast < float > ( subDivWorldSize ) ;
2025-09-12 09:54:49 +03:00
2025-09-21 10:48:47 +03:00
Vector2f closestGridPointScreenPos = get_closest_grid_point ( offset , divSize , drawData ) ;
2025-09-16 09:35:03 +03:00
Vector2f subClosestGridPointScreenPos = get_closest_grid_point ( offset , subDivSize , drawData ) ;
2025-09-06 19:06:30 +03:00
2025-09-24 07:28:28 +03:00
SkColor4f pointColor = convert_vec4 < SkColor4f > ( color_mul_alpha ( color , removeDivisionsOutwards ? 1.0f : std : : clamp ( floatDivWorldSize / 100.0f , 0.0f , 1.0f ) ) ) ;
2025-09-15 16:58:57 +03:00
float gridPointSize = 5.0f ;
2025-09-25 10:21:47 +03:00
float finalDivMultiplierValue = ( 1.0 - divLogFraction ) * ( ( divLog = = 0 ) ? 0.5 : 1.0 ) ;
float finalDivMultiplierSqrt = std : : sqrt ( finalDivMultiplierValue ) ;
float finalDivMultiplierSqrd = std : : pow ( finalDivMultiplierValue , 2 ) ;
2025-09-15 16:58:57 +03:00
2025-09-16 10:58:32 +03:00
if ( removeDivisionsOutwards | | divLog = = 0 ) {
linePaint . setShader ( get_shader ( gridType , {
. gridColor = pointColor ,
. mainGridScale = floatDivWorldSize ,
. mainGridClosestPoint = closestGridPointScreenPos ,
. mainGridPointSize = gridPointSize ,
2025-09-23 22:08:24 +03:00
. divGridAlphaFrac = 0.3f ,
2025-09-16 10:58:32 +03:00
. divGridScale = floatSubDivWorldSize ,
. divGridClosestPoint = subClosestGridPointScreenPos ,
2025-09-25 10:21:47 +03:00
. divGridPointSize = gridPointSize * finalDivMultiplierSqrd
2025-09-16 10:58:32 +03:00
} ) ) ;
}
else {
2025-09-25 10:21:47 +03:00
pointColor . fA * = finalDivMultiplierSqrt ;
2025-09-16 10:58:32 +03:00
linePaint . setShader ( get_shader ( gridType , {
. gridColor = pointColor ,
. mainGridScale = floatSubDivWorldSize ,
. mainGridClosestPoint = subClosestGridPointScreenPos ,
2025-09-25 10:21:47 +03:00
. mainGridPointSize = gridPointSize * finalDivMultiplierSqrt ,
2025-09-16 10:58:32 +03:00
. divGridAlphaFrac = 0.0f ,
. divGridScale = floatSubDivWorldSize ,
. divGridClosestPoint = subClosestGridPointScreenPos ,
2025-09-25 10:21:47 +03:00
. divGridPointSize = gridPointSize * finalDivMultiplierSqrd
2025-09-16 10:58:32 +03:00
} ) ) ;
}
2025-09-20 18:44:09 +03:00
2025-09-23 19:53:45 +03:00
gridCoordDivSize = logMultiplier * WorldScalar ( subdivisions ) ;
2025-09-15 16:58:57 +03:00
}
else {
2025-09-16 09:35:03 +03:00
WorldScalar subDivSize = size ;
2025-09-23 19:53:45 +03:00
divSize = subDivSize * WorldScalar ( subdivisions ) ;
2025-09-16 09:35:03 +03:00
WorldScalar subDivWorldSize = subDivSize / drawData . cam . c . inverseScale ;
2025-09-15 16:58:57 +03:00
WorldScalar divWorldSize = divSize / drawData . cam . c . inverseScale ;
2025-09-21 10:48:47 +03:00
float floatDivWorldSize = static_cast < float > ( divWorldSize ) ;
2025-09-16 09:35:03 +03:00
float floatSubDivWorldSize = static_cast < float > ( subDivWorldSize ) ;
2025-09-15 16:58:57 +03:00
2025-09-21 10:48:47 +03:00
Vector2f closestGridPointScreenPos = get_closest_grid_point ( offset , divSize , drawData ) ;
2025-09-16 09:35:03 +03:00
Vector2f subClosestGridPointScreenPos = get_closest_grid_point ( offset , subDivSize , drawData ) ;
2025-09-15 16:58:57 +03:00
2025-09-16 16:44:33 +03:00
SkColor4f pointColor = convert_vec4 < SkColor4f > ( color_mul_alpha ( color , std : : clamp ( floatDivWorldSize / 100.0f , 0.0f , 1.0f ) ) ) ;
2025-09-16 09:35:03 +03:00
float gridPointSize = std : : clamp ( floatDivWorldSize / 10.0f , 0.0f , 5.0f ) ;
2025-09-15 16:58:57 +03:00
linePaint . setShader ( get_shader ( gridType , {
. gridColor = pointColor ,
2025-09-16 09:35:03 +03:00
. mainGridScale = floatDivWorldSize ,
2025-09-15 16:58:57 +03:00
. mainGridClosestPoint = closestGridPointScreenPos ,
. mainGridPointSize = gridPointSize ,
2025-09-23 22:08:24 +03:00
. divGridAlphaFrac = 0.3f ,
2025-09-16 09:35:03 +03:00
. divGridScale = floatSubDivWorldSize ,
. divGridClosestPoint = subClosestGridPointScreenPos ,
2025-09-25 10:21:47 +03:00
. divGridPointSize = gridPointSize * 0.25f
2025-09-15 16:58:57 +03:00
} ) ) ;
2025-09-20 18:44:09 +03:00
2025-09-23 19:53:45 +03:00
gridCoordDivSize = WorldScalar ( subdivisions ) ;
2025-09-06 19:06:30 +03:00
}
2025-09-15 16:58:57 +03:00
canvas - > save ( ) ;
2025-09-20 13:28:28 +03:00
if ( bounds . has_value ( ) ) {
const auto & b = bounds . value ( ) ;
2025-09-24 16:40:22 +03:00
Vector2f b1 = drawData . cam . c . to_space ( b . min ) ;
Vector2f b2 = drawData . cam . c . to_space ( b . top_right ( ) ) ;
Vector2f b3 = drawData . cam . c . to_space ( b . max ) ;
Vector2f b4 = drawData . cam . c . to_space ( b . bottom_left ( ) ) ;
SkPath clipPath ;
clipPath . moveTo ( b1 . x ( ) , b1 . y ( ) ) ;
clipPath . lineTo ( b2 . x ( ) , b2 . y ( ) ) ;
clipPath . lineTo ( b3 . x ( ) , b3 . y ( ) ) ;
clipPath . lineTo ( b4 . x ( ) , b4 . y ( ) ) ;
clipPath . close ( ) ;
canvas - > clipPath ( clipPath ) ;
2025-09-20 13:28:28 +03:00
}
2025-09-15 16:58:57 +03:00
canvas - > rotate ( - drawData . cam . c . rotation * 180.0 / std : : numbers : : pi ) ;
canvas - > drawPaint ( linePaint ) ;
canvas - > restore ( ) ;
2025-09-25 10:21:47 +03:00
if ( drawData . cam . c . rotation = = 0.0 & & ! drawData . main - > takingScreenshot ) {
coordinatesWillBeDrawn = true ;
WorldVec worldWindowEndPos = drawData . cam . c . pos + drawData . cam . c . dir_from_space ( drawData . cam . viewingArea ) ;
coordinatesAxisOnBounds = bounds . has_value ( ) & & ! bounds . value ( ) . fully_contains_aabb ( SCollision : : AABB < WorldScalar > ( drawData . cam . c . pos , worldWindowEndPos ) ) ;
coordinatesDivWorldSize = divSize ;
coordinatesGridCoordDivSize = gridCoordDivSize ;
}
2025-09-06 19:06:30 +03:00
}
2025-09-20 18:44:09 +03:00
2025-09-25 10:21:47 +03:00
void WorldGrid : : draw_coordinates ( SkCanvas * canvas , const DrawData & drawData , Vector2f & axisOffset ) {
auto & divWorldSize = coordinatesDivWorldSize ;
auto & gridCoordDivSize = coordinatesGridCoordDivSize ;
2025-09-23 19:53:45 +03:00
WorldVec worldWindowEndPos = drawData . cam . c . pos + drawData . cam . c . dir_from_space ( drawData . cam . viewingArea ) ;
2025-09-25 10:21:47 +03:00
const WorldVec & worldWindowTopLeftClipPos = coordinatesAxisOnBounds ? bounds . value ( ) . min : drawData . cam . c . pos ;
2025-09-23 20:22:46 +03:00
SCollision : : AABB < float > boundsCamSpace ;
2025-09-23 19:53:45 +03:00
float coordFontSize = drawData . main - > toolbar . final_gui_scale ( ) * drawData . main - > toolbar . io - > fontSize ;
2025-09-25 10:21:47 +03:00
if ( coordinatesAxisOnBounds )
2025-09-23 20:22:46 +03:00
boundsCamSpace = SCollision : : AABB < float > ( drawData . cam . c . to_space ( bounds . value ( ) . min ) , drawData . cam . c . to_space ( bounds . value ( ) . max ) ) ;
else {
float toolbarXLength = drawData . main - > toolbar . final_gui_scale ( ) * 80.0f ;
2025-09-25 10:21:47 +03:00
boundsCamSpace = SCollision : : AABB < float > ( { toolbarXLength + axisOffset . x ( ) , 0.0f } , { drawData . cam . viewingArea . x ( ) , drawData . cam . viewingArea . y ( ) - axisOffset . y ( ) } ) ;
if ( boundsCamSpace . width ( ) < 100.0f | | boundsCamSpace . height ( ) < 100.0f )
return ;
2025-09-23 20:22:46 +03:00
}
2025-09-25 10:21:47 +03:00
if ( coordinatesAxisOnBounds ) {
2025-09-23 19:53:45 +03:00
float fontSizeBoundsMultiplier = std : : min ( std : : min ( boundsCamSpace . width ( ) / drawData . cam . viewingArea . x ( ) , boundsCamSpace . height ( ) / drawData . cam . viewingArea . y ( ) ) * 4.0f , 1.0f ) ;
coordFontSize * = fontSizeBoundsMultiplier ;
2025-09-23 20:22:46 +03:00
if ( coordFontSize < = 3.0f )
2025-09-23 19:53:45 +03:00
return ;
}
SkFont f = drawData . main - > toolbar . io - > get_font ( coordFontSize ) ;
2025-09-20 18:44:09 +03:00
SkFontMetrics metrics ;
f . getMetrics ( & metrics ) ;
2025-09-23 15:05:14 +03:00
float fontHeight = ( - metrics . fAscent + metrics . fDescent ) ;
2025-09-20 18:44:09 +03:00
2025-09-21 10:48:47 +03:00
WorldVec worldWindowBeginPos ;
2025-09-23 12:44:55 +03:00
WorldVec coordMultiplier ;
2025-09-23 19:53:45 +03:00
2025-09-23 12:44:55 +03:00
auto calculateCoordMultipliers = [ & ] ( ) {
2025-09-23 20:22:46 +03:00
coordMultiplier . x ( ) = FixedPoint : : trunc ( ( drawData . cam . c . pos . x ( ) - offset . x ( ) ) / divWorldSize ) ;
2025-09-23 12:44:55 +03:00
worldWindowBeginPos . x ( ) = coordMultiplier . x ( ) * divWorldSize + offset . x ( ) ;
2025-09-23 20:22:46 +03:00
coordMultiplier . y ( ) = - FixedPoint : : trunc ( ( offset . y ( ) - drawData . cam . c . pos . y ( ) + divWorldSize ) / divWorldSize ) ;
2025-09-23 12:44:55 +03:00
worldWindowBeginPos . y ( ) = coordMultiplier . y ( ) * divWorldSize + offset . y ( ) ;
} ;
calculateCoordMultipliers ( ) ;
2025-09-20 18:44:09 +03:00
2025-09-23 12:44:55 +03:00
struct NumberTextData {
2025-09-23 15:05:14 +03:00
std : : string mainText ;
float mainPos ; // X or Y pos depending on axis
float mainLength ; // X length
std : : string exponentText ;
float exponentPos ;
float exponentLength ;
float totalLength ;
2025-09-23 12:44:55 +03:00
} ;
std : : vector < NumberTextData > xAxisLabels , yAxisLabels ;
2025-09-23 15:05:14 +03:00
bool exponentExistsInXAxis ;
2025-09-23 12:44:55 +03:00
2025-09-23 20:22:46 +03:00
float yAxisMaxXLength = 0.0f ;
2025-09-23 12:44:55 +03:00
for ( int maxRepeats = 0 ; maxRepeats < 3 ; maxRepeats + + ) {
xAxisLabels . clear ( ) ;
2025-09-23 15:05:14 +03:00
exponentExistsInXAxis = false ;
2025-09-23 12:44:55 +03:00
WorldScalar xGridCoord = coordMultiplier . x ( ) * gridCoordDivSize ;
2025-09-20 18:44:09 +03:00
2025-09-21 10:48:47 +03:00
for ( WorldScalar xWorldCoord = worldWindowBeginPos . x ( ) ; xWorldCoord < worldWindowEndPos . x ( ) ; xWorldCoord + = divWorldSize ) {
2025-09-23 12:44:55 +03:00
NumberTextData label ;
2025-09-21 10:48:47 +03:00
Vector2f gridPointScreenPos = drawData . cam . c . to_space ( WorldVec { xWorldCoord , worldWindowBeginPos . y ( ) } ) ;
2025-09-23 15:05:14 +03:00
label . mainText = xGridCoord . display_int_str ( 4 , false , & label . exponentText ) ;
if ( ! label . exponentText . empty ( ) ) {
label . mainText + = " × 10" ;
exponentExistsInXAxis = true ;
}
label . mainLength = f . measureText ( label . mainText . c_str ( ) , label . mainText . length ( ) , SkTextEncoding : : kUTF8 , nullptr ) ;
label . exponentLength = f . measureText ( label . exponentText . c_str ( ) , label . exponentText . length ( ) , SkTextEncoding : : kUTF8 , nullptr ) ;
label . totalLength = label . mainLength + label . exponentLength ;
label . mainPos = gridPointScreenPos . x ( ) - ( label . mainLength * 0.5f + label . exponentLength * 0.5f ) ;
label . exponentPos = label . mainPos + label . mainLength ;
2025-09-21 10:48:47 +03:00
xGridCoord + = gridCoordDivSize ;
2025-09-23 12:44:55 +03:00
2025-09-23 20:22:46 +03:00
if ( xWorldCoord > worldWindowTopLeftClipPos . x ( ) )
xAxisLabels . emplace_back ( label ) ;
2025-09-23 12:44:55 +03:00
}
bool labelsTooClose = false ;
for ( size_t i = 1 ; i < xAxisLabels . size ( ) ; i + + ) {
2025-09-23 15:05:14 +03:00
if ( xAxisLabels [ i - 1 ] . mainPos + xAxisLabels [ i - 1 ] . totalLength > = xAxisLabels [ i ] . mainPos - 10.0f * drawData . main - > toolbar . final_gui_scale ( ) ) {
2025-09-23 12:44:55 +03:00
divWorldSize * = WorldScalar ( 2 ) ;
gridCoordDivSize * = WorldScalar ( 2 ) ;
calculateCoordMultipliers ( ) ;
labelsTooClose = true ;
break ;
}
2025-09-21 10:48:47 +03:00
}
2025-09-23 12:44:55 +03:00
if ( ! labelsTooClose )
break ;
2025-09-20 18:44:09 +03:00
}
2025-09-25 10:21:47 +03:00
if ( ! coordinatesAxisOnBounds )
axisOffset . y ( ) + = ( exponentExistsInXAxis ? fontHeight * 1.25f : fontHeight ) ;
2025-09-23 20:22:46 +03:00
float xAxisYPosRect = boundsCamSpace . max . y ( ) - ( exponentExistsInXAxis ? fontHeight * 1.25f : fontHeight ) ;
2025-09-21 10:48:47 +03:00
{
WorldScalar yGridCoord = FixedPoint : : trunc ( ( offset . y ( ) - drawData . cam . c . pos . y ( ) ) / divWorldSize ) * gridCoordDivSize + gridCoordDivSize ;
for ( WorldScalar yWorldCoord = worldWindowBeginPos . y ( ) ; yWorldCoord < worldWindowEndPos . y ( ) ; yWorldCoord + = divWorldSize ) {
2025-09-23 12:44:55 +03:00
NumberTextData label ;
2025-09-21 10:48:47 +03:00
2025-09-23 12:44:55 +03:00
Vector2f gridPointScreenPos = drawData . cam . c . to_space ( WorldVec { worldWindowBeginPos . x ( ) , yWorldCoord } ) ;
2025-09-21 10:48:47 +03:00
2025-09-23 15:05:14 +03:00
label . mainText = yGridCoord . display_int_str ( 4 , false , & label . exponentText ) ;
if ( ! label . exponentText . empty ( ) )
label . mainText + = " × 10" ;
label . mainLength = f . measureText ( label . mainText . c_str ( ) , label . mainText . length ( ) , SkTextEncoding : : kUTF8 , nullptr ) ;
label . exponentLength = f . measureText ( label . exponentText . c_str ( ) , label . exponentText . length ( ) , SkTextEncoding : : kUTF8 , nullptr ) ;
label . totalLength = label . mainLength + label . exponentLength ;
label . mainPos = gridPointScreenPos . y ( ) + metrics . fDescent ;
label . exponentPos = label . mainPos - fontHeight * 0.25f ;
2025-09-21 10:48:47 +03:00
yGridCoord - = gridCoordDivSize ;
2025-09-23 12:44:55 +03:00
2025-09-23 20:22:46 +03:00
if ( yWorldCoord > worldWindowTopLeftClipPos . y ( ) & & label . mainPos < xAxisYPosRect & & label . mainPos - fontHeight * 1.25f > boundsCamSpace . min . y ( ) ) {
yAxisMaxXLength = std : : max ( yAxisMaxXLength , label . totalLength ) ;
yAxisLabels . emplace_back ( label ) ;
}
2025-09-21 10:48:47 +03:00
}
2025-09-20 18:44:09 +03:00
}
2025-09-23 12:44:55 +03:00
2025-09-25 10:21:47 +03:00
if ( ! coordinatesAxisOnBounds )
axisOffset . x ( ) + = yAxisMaxXLength ;
2025-09-23 12:44:55 +03:00
SkPaint labelColor ( SkColor4f { color . x ( ) , color . y ( ) , color . z ( ) , 1.0f } ) ;
2025-09-23 20:22:46 +03:00
float xAxisYPosLabels = boundsCamSpace . max . y ( ) - metrics . fDescent ;
2025-09-23 12:44:55 +03:00
2025-09-25 10:21:47 +03:00
if ( coordinatesAxisOnBounds ) {
2025-09-23 19:53:45 +03:00
canvas - > save ( ) ;
canvas - > clipRect ( SkRect : : MakeLTRB ( boundsCamSpace . min . x ( ) , boundsCamSpace . min . y ( ) , boundsCamSpace . max . x ( ) , boundsCamSpace . max . y ( ) ) ) ;
2025-09-23 20:22:46 +03:00
}
2025-09-23 12:44:55 +03:00
2025-09-23 20:22:46 +03:00
canvas - > drawRect ( SkRect : : MakeXYWH ( boundsCamSpace . min . x ( ) , boundsCamSpace . min . y ( ) , yAxisMaxXLength , boundsCamSpace . max . y ( ) - boundsCamSpace . min . y ( ) ) , SkPaint { color_mul_alpha ( drawData . main - > toolbar . io - > theme - > backColor1 , 0.9f ) } ) ;
canvas - > drawRect ( SkRect : : MakeLTRB ( boundsCamSpace . min . x ( ) + yAxisMaxXLength , xAxisYPosRect , boundsCamSpace . max . x ( ) , boundsCamSpace . max . y ( ) ) , SkPaint { color_mul_alpha ( drawData . main - > toolbar . io - > theme - > backColor1 , 0.9f ) } ) ;
2025-09-23 19:53:45 +03:00
2025-09-23 20:22:46 +03:00
for ( auto & l : xAxisLabels ) {
if ( l . mainPos > boundsCamSpace . min . x ( ) + yAxisMaxXLength & & l . mainPos + l . totalLength < boundsCamSpace . max . x ( ) ) {
canvas - > drawSimpleText ( l . mainText . c_str ( ) , l . mainText . length ( ) , SkTextEncoding : : kUTF8 , l . mainPos , xAxisYPosLabels , f , labelColor ) ;
canvas - > drawSimpleText ( l . exponentText . c_str ( ) , l . exponentText . length ( ) , SkTextEncoding : : kUTF8 , l . exponentPos , xAxisYPosLabels - fontHeight * 0.25f , f , labelColor ) ;
2025-09-23 19:53:45 +03:00
}
2025-09-23 12:44:55 +03:00
}
2025-09-23 19:53:45 +03:00
2025-09-23 20:22:46 +03:00
for ( auto & l : yAxisLabels ) {
float mainTextXPos = boundsCamSpace . min . x ( ) + yAxisMaxXLength * 0.5f - l . totalLength * 0.5f ;
canvas - > drawSimpleText ( l . mainText . c_str ( ) , l . mainText . length ( ) , SkTextEncoding : : kUTF8 , mainTextXPos , l . mainPos , f , labelColor ) ;
canvas - > drawSimpleText ( l . exponentText . c_str ( ) , l . exponentText . length ( ) , SkTextEncoding : : kUTF8 , mainTextXPos + l . mainLength , l . exponentPos , f , labelColor ) ;
2025-09-23 12:51:08 +03:00
}
2025-09-23 20:22:46 +03:00
2025-09-25 10:21:47 +03:00
if ( coordinatesAxisOnBounds )
2025-09-23 20:22:46 +03:00
canvas - > restore ( ) ;
2025-09-20 18:44:09 +03:00
}