class_name TerrainChunkEdgeProfile extends RefCounted var edge := TerrainChunkTopology.Edge.NORTH var points := PackedVector2Array() func _init( profile_edge := TerrainChunkTopology.Edge.NORTH, profile_points := PackedVector2Array(), ) -> void: edge = profile_edge points = profile_points func matches(other: TerrainChunkEdgeProfile, tolerance: float) -> bool: if other == null: return false var first_surface := _upper_surface_points() var second_surface := other._upper_surface_points() return _surfaces_match(first_surface, second_surface, tolerance) ## Compares only the visible raised portion of two edge profiles. Authored ## deep-water cliff pieces continue below ordinary ground level; when their ## lateral end meets an inland cliff, that lower extension is buried by the ## neighboring base terrain and must not create a false seam mismatch. func matches_above_height( other: TerrainChunkEdgeProfile, minimum_height: float, tolerance: float, ) -> bool: if other == null: return false var first_surface := _points_above_height( _upper_surface_points(), minimum_height, tolerance, ) var second_surface := _points_above_height( other._upper_surface_points(), minimum_height, tolerance, ) return _surfaces_match(first_surface, second_surface, tolerance) ## Compares the visible terrain seam at and above a shared waterline while ## deliberately ignoring authored underwater wall depth. Terrain chunks may ## extend different distances below the water without creating a visible gap, ## but their banks still need to meet exactly at the surface. func matches_at_or_above_height( other: TerrainChunkEdgeProfile, minimum_height: float, tolerance: float, ) -> bool: if other == null: return false var first_surface := _points_at_or_above_height( _upper_surface_points(), minimum_height, tolerance, ) var second_surface := _points_at_or_above_height( other._upper_surface_points(), minimum_height, tolerance, ) if first_surface.is_empty() or second_surface.is_empty(): return first_surface.is_empty() and second_surface.is_empty() return _surfaces_match(first_surface, second_surface, tolerance) func _surfaces_match( first_surface: PackedVector2Array, second_surface: PackedVector2Array, tolerance: float, ) -> bool: if first_surface.size() < 2 or second_surface.size() < 2: return false # Matching authoring control points can shift slightly along a steep rounded # bank while describing the same seam. Accept the pair directly when every # corresponding point remains inside the requested two-axis tolerance. if first_surface.size() == second_surface.size(): var control_points_match := true for index: int in first_surface.size(): if ( absf(first_surface[index].x - second_surface[index].x) > tolerance or absf(first_surface[index].y - second_surface[index].y) > tolerance ): control_points_match = false break if control_points_match: return true if ( absf(first_surface[0].x - second_surface[0].x) > tolerance or absf( first_surface[first_surface.size() - 1].x - second_surface[second_surface.size() - 1].x ) > tolerance ): return false var sample_tangents := PackedFloat32Array() for point: Vector2 in first_surface: _append_unique_tangent(sample_tangents, point.x, tolerance) for point: Vector2 in second_surface: _append_unique_tangent(sample_tangents, point.x, tolerance) sample_tangents.sort() for tangent: float in sample_tangents: var first_height := _sample_height(first_surface, tangent) var second_height := _sample_height(second_surface, tangent) if absf(first_height - second_height) > tolerance: return false return true func _points_above_height( surface: PackedVector2Array, minimum_height: float, tolerance: float, ) -> PackedVector2Array: var result := PackedVector2Array() for point: Vector2 in surface: if point.y > minimum_height + tolerance: result.append(point) return result func _points_at_or_above_height( surface: PackedVector2Array, minimum_height: float, tolerance: float, ) -> PackedVector2Array: var result := PackedVector2Array() for point: Vector2 in surface: if point.y >= minimum_height - tolerance: result.append(point) return result func _upper_surface_points() -> PackedVector2Array: var result := PackedVector2Array() for point: Vector2 in points: if ( not result.is_empty() and is_equal_approx(result[result.size() - 1].x, point.x) ): var previous := result[result.size() - 1] previous.y = maxf(previous.y, point.y) result[result.size() - 1] = previous continue result.append(point) return result func _append_unique_tangent( tangents: PackedFloat32Array, tangent: float, tolerance: float, ) -> void: for existing: float in tangents: if absf(existing - tangent) <= tolerance: return tangents.append(tangent) func _sample_height(surface: PackedVector2Array, tangent: float) -> float: if tangent <= surface[0].x: return surface[0].y for index: int in range(1, surface.size()): var right := surface[index] if tangent > right.x: continue var left := surface[index - 1] var span := right.x - left.x if is_zero_approx(span): return maxf(left.y, right.y) return lerpf(left.y, right.y, (tangent - left.x) / span) return surface[surface.size() - 1].y func signature(quantization: float) -> String: var tokens := PackedStringArray() for point: Vector2 in points: tokens.append( "%d:%d" % [ roundi(point.x / quantization), roundi(point.y / quantization), ] ) return ",".join(tokens)