-module(spatial@collider). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/spatial/collider.gleam"). -export([box/2, box_from_center/2, sphere/2, capsule/3, cylinder/3, center/1, size/1, from_rotation/4, contains_point/2, intersects/2]). -export_type([internal_collider/0]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. ?MODULEDOC( " Collision volumes for spatial queries and collision detection.\n" "\n" " Provides Box, Sphere, Capsule, and Cylinder colliders with intersection tests\n" " and rotation-aware collision volume computation.\n" ). -type internal_collider() :: {box, vec@vec3:vec3(float()), vec@vec3:vec3(float())} | {sphere, vec@vec3:vec3(float()), float()} | {capsule, vec@vec3:vec3(float()), vec@vec3:vec3(float()), float()} | {cylinder, vec@vec3:vec3(float()), float(), float()}. -file("src/spatial/collider.gleam", 38). ?DOC( " Create a box collider from min and max points.\n" "\n" " ## Example\n" " ```gleam\n" " let bounds = collider.box(\n" " min: vec3.Vec3(-1.0, -1.0, -1.0),\n" " max: vec3.Vec3(1.0, 1.0, 1.0),\n" " )\n" " ```\n" ). -spec box(vec@vec3:vec3(float()), vec@vec3:vec3(float())) -> internal_collider(). box(Min, Max) -> {box, Min, Max}. -file("src/spatial/collider.gleam", 51). ?DOC( " Create a box collider from center and half-extents.\n" "\n" " ## Example\n" " ```gleam\n" " let bounds = collider.box_from_center(\n" " center: vec3.Vec3(0.0, 5.0, 0.0),\n" " half_extents: vec3.Vec3(2.0, 1.0, 2.0),\n" " )\n" " ```\n" ). -spec box_from_center(vec@vec3:vec3(float()), vec@vec3:vec3(float())) -> internal_collider(). box_from_center(Center, Half_extents) -> {box, vec@vec3f:subtract(Center, Half_extents), vec@vec3f:add(Center, Half_extents)}. -file("src/spatial/collider.gleam", 70). ?DOC( " Create a sphere collider from center and radius.\n" "\n" " ## Example\n" " ```gleam\n" " let bounds = collider.sphere(\n" " center: vec3.Vec3(0.0, 0.0, 0.0),\n" " radius: 2.5,\n" " )\n" " ```\n" ). -spec sphere(vec@vec3:vec3(float()), float()) -> internal_collider(). sphere(Center, Radius) -> {sphere, Center, Radius}. -file("src/spatial/collider.gleam", 86). ?DOC( " Create a capsule collider from start point, end point, and radius.\n" "\n" " A capsule is a line segment with a radius - perfect for character controllers.\n" "\n" " ## Example\n" " ```gleam\n" " let character = collider.capsule(\n" " start: vec3.Vec3(0.0, 0.0, 0.0),\n" " end: vec3.Vec3(0.0, 2.0, 0.0),\n" " radius: 0.5,\n" " )\n" " ```\n" ). -spec capsule(vec@vec3:vec3(float()), vec@vec3:vec3(float()), float()) -> internal_collider(). capsule(Start, End, Radius) -> {capsule, Start, End, Radius}. -file("src/spatial/collider.gleam", 106). ?DOC( " Create a cylinder collider from center, radius, and height.\n" "\n" " The cylinder is aligned along the Y axis in local space.\n" "\n" " ## Example\n" " ```gleam\n" " let pillar = collider.cylinder(\n" " center: vec3.Vec3(0.0, 5.0, 0.0),\n" " radius: 1.0,\n" " height: 10.0,\n" " )\n" " ```\n" ). -spec cylinder(vec@vec3:vec3(float()), float(), float()) -> internal_collider(). cylinder(Center, Radius, Height) -> {cylinder, Center, Radius, Height}. -file("src/spatial/collider.gleam", 292). ?DOC(" Get the center of a collider.\n"). -spec center(internal_collider()) -> vec@vec3:vec3(float()). center(Collider) -> case Collider of {box, Min, Max} -> {vec3, (erlang:element(2, Min) + erlang:element(2, Max)) / 2.0, (erlang:element(3, Min) + erlang:element(3, Max)) / 2.0, (erlang:element(4, Min) + erlang:element(4, Max)) / 2.0}; {sphere, Center, _} -> Center; {capsule, Start, End, _} -> {vec3, (erlang:element(2, Start) + erlang:element(2, End)) / 2.0, (erlang:element(3, Start) + erlang:element(3, End)) / 2.0, (erlang:element(4, Start) + erlang:element(4, End)) / 2.0}; {cylinder, Center@1, _, _} -> Center@1 end. -file("src/spatial/collider.gleam", 314). ?DOC( " Get the size (dimensions) of a collider.\n" "\n" " Returns the bounding box dimensions for all collider types.\n" ). -spec size(internal_collider()) -> vec@vec3:vec3(float()). size(Collider) -> case Collider of {box, Min, Max} -> vec@vec3f:subtract(Max, Min); {sphere, _, Radius} -> Diameter = Radius * 2.0, {vec3, Diameter, Diameter, Diameter}; {capsule, Start, End, Radius@1} -> Length = vec@vec3f:distance(Start, End), Diameter@1 = Radius@1 * 2.0, {vec3, Diameter@1, Length + Diameter@1, Diameter@1}; {cylinder, _, Radius@2, Height} -> Diameter@2 = Radius@2 * 2.0, {vec3, Diameter@2, Height, Diameter@2} end. -file("src/spatial/collider.gleam", 445). -spec transform_point( vec@vec3:vec3(float()), vec@vec3:vec3(float()), quaternion:quaternion(), vec@vec3:vec3(float()) ) -> vec@vec3:vec3(float()). transform_point(Point, Position, Rotation, Scale) -> Scaled = {vec3, erlang:element(2, Point) * erlang:element(2, Scale), erlang:element(3, Point) * erlang:element(3, Scale), erlang:element(4, Point) * erlang:element(4, Scale)}, Rotated = quaternion:rotate(Rotation, Scaled), vec@vec3f:add(Rotated, Position). -file("src/spatial/collider.gleam", 362). ?DOC( " Create a new collider from a local-space collider with position, rotation, and scale.\n" "\n" " For Box: Computes a new axis-aligned bounding box that encompasses\n" " all 8 corners after rotation, translation, and scaling.\n" "\n" " For Sphere: Transforms the center and scales the radius by the maximum\n" " scale component (since spheres remain spherical under uniform scaling).\n" "\n" " **Time Complexity**: O(1) - transforms a constant number of points (8 for Box).\n" "\n" " ## Example\n" " ```gleam\n" " // Local space box\n" " let local_box = collider.box(\n" " min: vec3.Vec3(-1.0, -1.0, -1.0),\n" " max: vec3.Vec3(1.0, 1.0, 1.0),\n" " )\n" "\n" " // Rotated 45 degrees around Y axis\n" " let rotation = q.from_axis_angle(vec3.Vec3(0.0, 1.0, 0.0), 0.785)\n" "\n" " // Get world-space collider\n" " let world_box = collider.from_rotation(\n" " local_box,\n" " position: vec3.Vec3(5.0, 0.0, 0.0),\n" " rotation: rotation,\n" " scale: vec3.Vec3(1.0, 1.0, 1.0),\n" " )\n" " ```\n" ). -spec from_rotation( internal_collider(), vec@vec3:vec3(float()), quaternion:quaternion(), vec@vec3:vec3(float()) ) -> internal_collider(). from_rotation(Collider, Position, Rotation, Scale) -> case Collider of {box, Min, Max} -> Corners = [{vec3, erlang:element(2, Min), erlang:element(3, Min), erlang:element(4, Min)}, {vec3, erlang:element(2, Max), erlang:element(3, Min), erlang:element(4, Min)}, {vec3, erlang:element(2, Min), erlang:element(3, Max), erlang:element(4, Min)}, {vec3, erlang:element(2, Max), erlang:element(3, Max), erlang:element(4, Min)}, {vec3, erlang:element(2, Min), erlang:element(3, Min), erlang:element(4, Max)}, {vec3, erlang:element(2, Max), erlang:element(3, Min), erlang:element(4, Max)}, {vec3, erlang:element(2, Min), erlang:element(3, Max), erlang:element(4, Max)}, {vec3, erlang:element(2, Max), erlang:element(3, Max), erlang:element(4, Max)}], Transformed_corners = gleam@list:map( Corners, fun(Corner) -> transform_point(Corner, Position, Rotation, Scale) end ), Init_min = {vec3, 1.0e10, 1.0e10, 1.0e10}, Init_max = {vec3, -1.0e10, -1.0e10, -1.0e10}, {New_min, New_max} = gleam@list:fold( Transformed_corners, {Init_min, Init_max}, fun(Acc, Point) -> {Current_min, Current_max} = Acc, {{vec3, gleam@float:min( erlang:element(2, Current_min), erlang:element(2, Point) ), gleam@float:min( erlang:element(3, Current_min), erlang:element(3, Point) ), gleam@float:min( erlang:element(4, Current_min), erlang:element(4, Point) )}, {vec3, gleam@float:max( erlang:element(2, Current_max), erlang:element(2, Point) ), gleam@float:max( erlang:element(3, Current_max), erlang:element(3, Point) ), gleam@float:max( erlang:element(4, Current_max), erlang:element(4, Point) )}} end ), {box, New_min, New_max}; {sphere, Center, Radius} -> New_center = transform_point(Center, Position, Rotation, Scale), Max_scale = gleam@float:max( erlang:element(2, Scale), gleam@float:max( erlang:element(3, Scale), erlang:element(4, Scale) ) ), New_radius = Radius * Max_scale, {sphere, New_center, New_radius}; {capsule, Start, End, Radius@1} -> New_start = transform_point(Start, Position, Rotation, Scale), New_end = transform_point(End, Position, Rotation, Scale), Max_scale@1 = gleam@float:max( erlang:element(2, Scale), gleam@float:max( erlang:element(3, Scale), erlang:element(4, Scale) ) ), New_radius@1 = Radius@1 * Max_scale@1, {capsule, New_start, New_end, New_radius@1}; {cylinder, Center@1, Radius@2, Height} -> New_center@1 = transform_point(Center@1, Position, Rotation, Scale), Max_radial_scale = gleam@float:max( erlang:element(2, Scale), erlang:element(4, Scale) ), New_radius@2 = Radius@2 * Max_radial_scale, New_height = Height * erlang:element(3, Scale), {cylinder, New_center@1, New_radius@2, New_height} end. -file("src/spatial/collider.gleam", 464). -spec point_to_line_segment_distance( vec@vec3:vec3(float()), vec@vec3:vec3(float()), vec@vec3:vec3(float()) ) -> float(). point_to_line_segment_distance(Point, Start, End) -> Line_vec = vec@vec3f:subtract(End, Start), Point_vec = vec@vec3f:subtract(Point, Start), Line_len_sq = vec@vec3f:dot(Line_vec, Line_vec), case Line_len_sq < 0.0001 of true -> spatial_ffi:distance(Point, Start); false -> T = gleam@float:clamp(case Line_len_sq of +0.0 -> +0.0; -0.0 -> -0.0; Gleam@denominator -> vec@vec3f:dot(Point_vec, Line_vec) / Gleam@denominator end, +0.0, 1.0), Projection = vec@vec3f:add(Start, vec@vec3f:scale(Line_vec, T)), spatial_ffi:distance(Point, Projection) end. -file("src/spatial/collider.gleam", 119). ?DOC( " Check if a point is inside a collider.\n" "\n" " Works for Box, Sphere, Capsule, and Cylinder colliders.\n" "\n" " **Time Complexity**: O(1) - constant time geometric calculation.\n" ). -spec contains_point(internal_collider(), vec@vec3:vec3(float())) -> boolean(). contains_point(Collider, Point) -> case Collider of {box, Min, Max} -> spatial_ffi:box_contains_point(Min, Max, Point); {sphere, Center, Radius} -> spatial_ffi:distance_squared(Center, Point) =< (Radius * Radius); {capsule, Start, End, Radius@1} -> Distance = point_to_line_segment_distance(Point, Start, End), Distance =< Radius@1; {cylinder, Center@1, Radius@2, Height} -> Half_height = Height / 2.0, Dx = erlang:element(2, Point) - erlang:element(2, Center@1), Dz = erlang:element(4, Point) - erlang:element(4, Center@1), Radial_dist_sq = (Dx * Dx) + (Dz * Dz), Y_in_range = (erlang:element(3, Point) >= (erlang:element( 3, Center@1 ) - Half_height)) andalso (erlang:element(3, Point) =< (erlang:element(3, Center@1) + Half_height)), (Radial_dist_sq =< (Radius@2 * Radius@2)) andalso Y_in_range end. -file("src/spatial/collider.gleam", 484). -spec line_segment_to_line_segment_distance( vec@vec3:vec3(float()), vec@vec3:vec3(float()), vec@vec3:vec3(float()), vec@vec3:vec3(float()) ) -> float(). line_segment_to_line_segment_distance(Start_a, End_a, Start_b, End_b) -> D1 = vec@vec3f:subtract(End_a, Start_a), D2 = vec@vec3f:subtract(End_b, Start_b), R = vec@vec3f:subtract(Start_a, Start_b), A = vec@vec3f:dot(D1, D1), E = vec@vec3f:dot(D2, D2), F = vec@vec3f:dot(D2, R), case (A < 0.0001) andalso (E < 0.0001) of true -> spatial_ffi:distance(Start_a, Start_b); false -> case A < 0.0001 of true -> T = gleam@float:clamp(case E of +0.0 -> +0.0; -0.0 -> -0.0; Gleam@denominator -> F / Gleam@denominator end, +0.0, 1.0), Point_on_b = vec@vec3f:add(Start_b, vec@vec3f:scale(D2, T)), spatial_ffi:distance(Start_a, Point_on_b); false -> case E < 0.0001 of true -> S = gleam@float:clamp(case A of +0.0 -> +0.0; -0.0 -> -0.0; Gleam@denominator@1 -> vec@vec3f:dot(D1, R) / Gleam@denominator@1 end, +0.0, 1.0), Point_on_a = vec@vec3f:add( Start_a, vec@vec3f:scale(D1, S) ), spatial_ffi:distance(Point_on_a, Start_b); false -> C = vec@vec3f:dot(D1, R), B = vec@vec3f:dot(D1, D2), Denom = (A * E) - (B * B), S@1 = case Denom < 0.0001 of true -> +0.0; false -> gleam@float:clamp(case Denom of +0.0 -> +0.0; -0.0 -> -0.0; Gleam@denominator@2 -> ((B * F) - (C * E)) / Gleam@denominator@2 end, +0.0, 1.0) end, T@1 = case E of +0.0 -> +0.0; -0.0 -> -0.0; Gleam@denominator@3 -> ((B * S@1) + F) / Gleam@denominator@3 end, T_clamped = gleam@float:clamp(T@1, +0.0, 1.0), Point_on_a@1 = vec@vec3f:add( Start_a, vec@vec3f:scale(D1, S@1) ), Point_on_b@1 = vec@vec3f:add( Start_b, vec@vec3f:scale(D2, T_clamped) ), spatial_ffi:distance(Point_on_a@1, Point_on_b@1) end end end. -file("src/spatial/collider.gleam", 538). -spec line_segment_intersects_box( vec@vec3:vec3(float()), vec@vec3:vec3(float()), vec@vec3:vec3(float()), vec@vec3:vec3(float()) ) -> boolean(). line_segment_intersects_box(Start, End, Min, Max) -> Dir = vec@vec3f:subtract(End, Start), T_min = +0.0, T_max = 1.0, Inv_dir_x = case gleam@float:absolute_value(erlang:element(2, Dir)) < 0.0001 of true -> 1.0e10; false -> case erlang:element(2, Dir) of +0.0 -> +0.0; -0.0 -> -0.0; Gleam@denominator -> 1.0 / Gleam@denominator end end, Tx1 = (erlang:element(2, Min) - erlang:element(2, Start)) * Inv_dir_x, Tx2 = (erlang:element(2, Max) - erlang:element(2, Start)) * Inv_dir_x, T_min@1 = gleam@float:max(T_min, gleam@float:min(Tx1, Tx2)), T_max@1 = gleam@float:min(T_max, gleam@float:max(Tx1, Tx2)), case T_max@1 < T_min@1 of true -> false; false -> Inv_dir_y = case gleam@float:absolute_value(erlang:element(3, Dir)) < 0.0001 of true -> 1.0e10; false -> case erlang:element(3, Dir) of +0.0 -> +0.0; -0.0 -> -0.0; Gleam@denominator@1 -> 1.0 / Gleam@denominator@1 end end, Ty1 = (erlang:element(3, Min) - erlang:element(3, Start)) * Inv_dir_y, Ty2 = (erlang:element(3, Max) - erlang:element(3, Start)) * Inv_dir_y, T_min@2 = gleam@float:max(T_min@1, gleam@float:min(Ty1, Ty2)), T_max@2 = gleam@float:min(T_max@1, gleam@float:max(Ty1, Ty2)), case T_max@2 < T_min@2 of true -> false; false -> Inv_dir_z = case gleam@float:absolute_value( erlang:element(4, Dir) ) < 0.0001 of true -> 1.0e10; false -> case erlang:element(4, Dir) of +0.0 -> +0.0; -0.0 -> -0.0; Gleam@denominator@2 -> 1.0 / Gleam@denominator@2 end end, Tz1 = (erlang:element(4, Min) - erlang:element(4, Start)) * Inv_dir_z, Tz2 = (erlang:element(4, Max) - erlang:element(4, Start)) * Inv_dir_z, T_min@3 = gleam@float:max( T_min@2, gleam@float:min(Tz1, Tz2) ), T_max@3 = gleam@float:min( T_max@2, gleam@float:max(Tz1, Tz2) ), T_max@3 >= T_min@3 end end. -file("src/spatial/collider.gleam", 149). ?DOC( " Check if two colliders intersect.\n" "\n" " Handles all collider type combinations.\n" "\n" " **Time Complexity**: O(1) - constant time geometric calculation.\n" ). -spec intersects(internal_collider(), internal_collider()) -> boolean(). intersects(A, B) -> case {A, B} of {{box, Min_a, Max_a}, {box, Min_b, Max_b}} -> spatial_ffi:box_intersects(Min_a, Max_a, Min_b, Max_b); {{sphere, Center_a, Radius_a}, {sphere, Center_b, Radius_b}} -> spatial_ffi:sphere_intersects( Center_a, Radius_a, Center_b, Radius_b ); {{box, Min, Max}, {sphere, Center, Radius}} -> Closest_x = gleam@float:clamp( erlang:element(2, Center), erlang:element(2, Min), erlang:element(2, Max) ), Closest_y = gleam@float:clamp( erlang:element(3, Center), erlang:element(3, Min), erlang:element(3, Max) ), Closest_z = gleam@float:clamp( erlang:element(4, Center), erlang:element(4, Min), erlang:element(4, Max) ), Closest = {vec3, Closest_x, Closest_y, Closest_z}, spatial_ffi:distance_squared(Center, Closest) =< (Radius * Radius); {{sphere, Center, Radius}, {box, Min, Max}} -> Closest_x = gleam@float:clamp( erlang:element(2, Center), erlang:element(2, Min), erlang:element(2, Max) ), Closest_y = gleam@float:clamp( erlang:element(3, Center), erlang:element(3, Min), erlang:element(3, Max) ), Closest_z = gleam@float:clamp( erlang:element(4, Center), erlang:element(4, Min), erlang:element(4, Max) ), Closest = {vec3, Closest_x, Closest_y, Closest_z}, spatial_ffi:distance_squared(Center, Closest) =< (Radius * Radius); {{capsule, Start_a, End_a, Radius_a@1}, {capsule, Start_b, End_b, Radius_b@1}} -> Distance = line_segment_to_line_segment_distance( Start_a, End_a, Start_b, End_b ), Distance =< (Radius_a@1 + Radius_b@1); {{sphere, Center@1, Sphere_radius}, {capsule, Start, End, Cap_radius}} -> Distance@1 = point_to_line_segment_distance(Center@1, Start, End), Distance@1 =< (Sphere_radius + Cap_radius); {{capsule, Start, End, Cap_radius}, {sphere, Center@1, Sphere_radius}} -> Distance@1 = point_to_line_segment_distance(Center@1, Start, End), Distance@1 =< (Sphere_radius + Cap_radius); {{box, Min@1, Max@1}, {capsule, Start@1, End@1, Radius@1}} -> Expanded_min = {vec3, erlang:element(2, Min@1) - Radius@1, erlang:element(3, Min@1) - Radius@1, erlang:element(4, Min@1) - Radius@1}, Expanded_max = {vec3, erlang:element(2, Max@1) + Radius@1, erlang:element(3, Max@1) + Radius@1, erlang:element(4, Max@1) + Radius@1}, line_segment_intersects_box( Start@1, End@1, Expanded_min, Expanded_max ); {{capsule, Start@1, End@1, Radius@1}, {box, Min@1, Max@1}} -> Expanded_min = {vec3, erlang:element(2, Min@1) - Radius@1, erlang:element(3, Min@1) - Radius@1, erlang:element(4, Min@1) - Radius@1}, Expanded_max = {vec3, erlang:element(2, Max@1) + Radius@1, erlang:element(3, Max@1) + Radius@1, erlang:element(4, Max@1) + Radius@1}, line_segment_intersects_box( Start@1, End@1, Expanded_min, Expanded_max ); {{cylinder, Cyl_center, Cyl_radius, Height}, {sphere, Sphere_center, Sphere_radius@1}} -> Half_height = Height / 2.0, Dx = erlang:element(2, Sphere_center) - erlang:element( 2, Cyl_center ), Dz = erlang:element(4, Sphere_center) - erlang:element( 4, Cyl_center ), Radial_dist_sq = (Dx * Dx) + (Dz * Dz), Radial_dist = case gleam@float:square_root(Radial_dist_sq) of {ok, D} -> D; {error, _} -> +0.0 end, Y_clamped = gleam@float:clamp( erlang:element(3, Sphere_center), erlang:element(3, Cyl_center) - Half_height, erlang:element(3, Cyl_center) + Half_height ), Closest_y_dist = gleam@float:absolute_value( erlang:element(3, Sphere_center) - Y_clamped ), (Radial_dist =< (Cyl_radius + Sphere_radius@1)) andalso (Closest_y_dist =< Sphere_radius@1); {{sphere, Sphere_center, Sphere_radius@1}, {cylinder, Cyl_center, Cyl_radius, Height}} -> Half_height = Height / 2.0, Dx = erlang:element(2, Sphere_center) - erlang:element( 2, Cyl_center ), Dz = erlang:element(4, Sphere_center) - erlang:element( 4, Cyl_center ), Radial_dist_sq = (Dx * Dx) + (Dz * Dz), Radial_dist = case gleam@float:square_root(Radial_dist_sq) of {ok, D} -> D; {error, _} -> +0.0 end, Y_clamped = gleam@float:clamp( erlang:element(3, Sphere_center), erlang:element(3, Cyl_center) - Half_height, erlang:element(3, Cyl_center) + Half_height ), Closest_y_dist = gleam@float:absolute_value( erlang:element(3, Sphere_center) - Y_clamped ), (Radial_dist =< (Cyl_radius + Sphere_radius@1)) andalso (Closest_y_dist =< Sphere_radius@1); {{box, Min@2, Max@2}, {cylinder, Center@2, Radius@2, Height@1}} -> Half_height@1 = Height@1 / 2.0, Cyl_min = {vec3, erlang:element(2, Center@2) - Radius@2, erlang:element(3, Center@2) - Half_height@1, erlang:element(4, Center@2) - Radius@2}, Cyl_max = {vec3, erlang:element(2, Center@2) + Radius@2, erlang:element(3, Center@2) + Half_height@1, erlang:element(4, Center@2) + Radius@2}, (((((erlang:element(2, Cyl_min) =< erlang:element(2, Max@2)) andalso (erlang:element( 2, Cyl_max ) >= erlang:element(2, Min@2))) andalso (erlang:element(3, Cyl_min) =< erlang:element(3, Max@2))) andalso (erlang:element(3, Cyl_max) >= erlang:element(3, Min@2))) andalso (erlang:element(4, Cyl_min) =< erlang:element(4, Max@2))) andalso (erlang:element(4, Cyl_max) >= erlang:element(4, Min@2)); {{cylinder, Center@2, Radius@2, Height@1}, {box, Min@2, Max@2}} -> Half_height@1 = Height@1 / 2.0, Cyl_min = {vec3, erlang:element(2, Center@2) - Radius@2, erlang:element(3, Center@2) - Half_height@1, erlang:element(4, Center@2) - Radius@2}, Cyl_max = {vec3, erlang:element(2, Center@2) + Radius@2, erlang:element(3, Center@2) + Half_height@1, erlang:element(4, Center@2) + Radius@2}, (((((erlang:element(2, Cyl_min) =< erlang:element(2, Max@2)) andalso (erlang:element( 2, Cyl_max ) >= erlang:element(2, Min@2))) andalso (erlang:element(3, Cyl_min) =< erlang:element(3, Max@2))) andalso (erlang:element(3, Cyl_max) >= erlang:element(3, Min@2))) andalso (erlang:element(4, Cyl_min) =< erlang:element(4, Max@2))) andalso (erlang:element(4, Cyl_max) >= erlang:element(4, Min@2)); {{capsule, Start@2, End@2, Cap_radius@1}, {cylinder, Cyl_center@1, Cyl_radius@1, Height@2}} -> Half_height@2 = Height@2 / 2.0, Min_dist = point_to_line_segment_distance( Cyl_center@1, Start@2, End@2 ), Y_in_range = ((erlang:element(3, Start@2) =< (erlang:element( 3, Cyl_center@1 ) + Half_height@2)) andalso (erlang:element(3, Start@2) >= (erlang:element( 3, Cyl_center@1 ) - Half_height@2))) orelse ((erlang:element(3, End@2) =< (erlang:element( 3, Cyl_center@1 ) + Half_height@2)) andalso (erlang:element(3, End@2) >= (erlang:element( 3, Cyl_center@1 ) - Half_height@2))), (Min_dist =< (Cyl_radius@1 + Cap_radius@1)) andalso Y_in_range; {{cylinder, Cyl_center@1, Cyl_radius@1, Height@2}, {capsule, Start@2, End@2, Cap_radius@1}} -> Half_height@2 = Height@2 / 2.0, Min_dist = point_to_line_segment_distance( Cyl_center@1, Start@2, End@2 ), Y_in_range = ((erlang:element(3, Start@2) =< (erlang:element( 3, Cyl_center@1 ) + Half_height@2)) andalso (erlang:element(3, Start@2) >= (erlang:element( 3, Cyl_center@1 ) - Half_height@2))) orelse ((erlang:element(3, End@2) =< (erlang:element( 3, Cyl_center@1 ) + Half_height@2)) andalso (erlang:element(3, End@2) >= (erlang:element( 3, Cyl_center@1 ) - Half_height@2))), (Min_dist =< (Cyl_radius@1 + Cap_radius@1)) andalso Y_in_range; {{cylinder, Center_a@1, Radius_a@2, Height_a}, {cylinder, Center_b@1, Radius_b@2, Height_b}} -> Half_height_a = Height_a / 2.0, Half_height_b = Height_b / 2.0, Dx@1 = erlang:element(2, Center_a@1) - erlang:element(2, Center_b@1), Dz@1 = erlang:element(4, Center_a@1) - erlang:element(4, Center_b@1), Radial_dist_sq@1 = (Dx@1 * Dx@1) + (Dz@1 * Dz@1), Radial_dist@1 = case gleam@float:square_root(Radial_dist_sq@1) of {ok, D@1} -> D@1; {error, _} -> +0.0 end, Y_overlap = gleam@float:min( erlang:element(3, Center_a@1) + Half_height_a, erlang:element(3, Center_b@1) + Half_height_b ) > gleam@float:max( erlang:element(3, Center_a@1) - Half_height_a, erlang:element(3, Center_b@1) - Half_height_b ), (Radial_dist@1 =< (Radius_a@2 + Radius_b@2)) andalso Y_overlap end.