diff --git a/client/tests/test_step_canvas_annotation_layer.gd b/client/tests/test_step_canvas_annotation_layer.gd index e5cc635fb..b19f9aede 100644 --- a/client/tests/test_step_canvas_annotation_layer.gd +++ b/client/tests/test_step_canvas_annotation_layer.gd @@ -448,23 +448,53 @@ func _canvas_with_courses(courses: Array) -> Dictionary: return {"width": 4, "height": 4, "courses": courses} -## A server "course" is an EDGE of the river network — the stretch between two -## confluences — not a river. Culling per course therefore culls per SEGMENT, -## and a long river assembled from many short edges vanishes entirely. Measured -## on Ferrath's Global canvas before chaining: 375 courses, 180 surviving the -## water clip, ZERO surviving the length cull. After chaining: 5 rivers. -func test_edges_chain_into_one_river_before_the_length_cull() -> void: +## The cull measures a WHOLE river, not its segments. +## +## A server course used to be one D8 hop, so culling per course culled per hop +## and a long river assembled from short pieces vanished entirely — measured on +## Ferrath's Global canvas: 375 courses, 180 surviving the water clip, ZERO +## surviving the length cull. The client briefly compensated by chaining runs +## end-to-end; that was the wrong layer and only half-worked (375 hops rejoined +## into 260 pieces, because each hop was warped independently so shared +## confluence points no longer coincided). The join moved to the server, which +## now emits one course per river (`river_course::build_paths`). +## +## So the property to hold here is no longer "chain the pieces" but "measure the +## arriving polyline as a whole": a course whose individual segments are each +## under the cull must still be kept when its total length clears it. +func test_a_rivers_length_is_measured_whole_not_per_segment() -> void: var layer: StepCanvasAnnotationLayer = auto_free(StepCanvasAnnotationLayer.new()) add_child(layer) - # Three collinear edges, each meeting the next end-to-end. Sized so each - # is individually UNDER the cull and the chain is comfortably over it. - # At District with a 4x4 canvas the pitch is 512 m/gridunit and 2 px per - # gridunit, i.e. 256 m per screen px — so the 15 px floor is 3,840 m. - # 1,500 m per edge: 5.9 px alone (culled), 17.6 px chained (kept). + # ONE course of four collinear points. At District with a 4x4 canvas the + # pitch is 512 m/gridunit at 2 px per gridunit, i.e. 256 m per screen px, so + # the 15 px floor is 3,840 m. Each 1,500 m segment is 5.9 px on its own — + # under the floor — while the whole 4,500 m course is 17.6 px and must be + # kept. A per-segment cull would drop it entirely. var step: float = 1500.0 - var edges: Array = [] + var points: Array = [] + for i in range(4): + points.append([float(i) * step, 0.0]) + var river: Array = [{"class": 2, "points": points}] + layer.set_frame(_canvas_with_courses(river), Vector2.ZERO, "District", Vector2i(4, 4), 0.0) + assert_int(layer.get_drawn_course_count()).override_failure_message( + "a river whose segments are each under the cull must survive on its TOTAL length" + ).is_equal(1) + + +## Separate courses must NOT be rejoined, even when they meet end-to-end. +## +## This is the deleted chaining's headstone. Water splits a course, and D-261 is +## explicit that a river crossing a lake is genuinely two visible strokes — so a +## client that helpfully reconnected them would erase the lake. Three short +## collinear courses that touch are three sub-threshold rivers, and all three +## are culled; a resurrected chaining pass would report 1. +func test_separate_courses_meeting_end_to_end_are_not_rejoined() -> void: + var layer: StepCanvasAnnotationLayer = auto_free(StepCanvasAnnotationLayer.new()) + add_child(layer) + var step: float = 1500.0 + var courses: Array = [] for i in range(3): - edges.append( + courses.append( { "class": 2, "points": [ @@ -473,10 +503,11 @@ func test_edges_chain_into_one_river_before_the_length_cull() -> void: ], } ) - layer.set_frame(_canvas_with_courses(edges), Vector2.ZERO, "District", Vector2i(4, 4), 0.0) + layer.set_frame(_canvas_with_courses(courses), Vector2.ZERO, "District", Vector2i(4, 4), 0.0) assert_int(layer.get_drawn_course_count()).override_failure_message( - "three end-to-end edges must chain into ONE river, not be culled as three segments" - ).is_equal(1) + "three touching courses are three rivers, each under the cull — rejoining them " + + "would draw one stroke straight across the water that separates them" + ).is_equal(0) ## The cull itself: a river too short to read as a line is not drawn at all,