Class PolygonTileMapper
- Namespace
- GridPlacement.GDScript
- Assembly
- MoonBark.GridPlacement.GDScript.dll
Handles polygon-to-tile-offset conversion with testable, separated concerns.\n\nThis class extracts the complex polygon processing logic from CollisionMapper to enable better testing and separation of responsibilities.\n\nCoordinate semantics - Tile addressing is center-based. For 16×16 tiles, the center is at +8,+8 from the tile origin. - Tests and runtime share "bottom-inclusive, top-exclusive" behavior on axis-aligned boundaries to avoid fencepost rows when a polygon edge lies exactly on a tile boundary.\n\nPipeline stages: 1. Transform polygon to world space and compute initial tile coverage 2. Apply trapezoid expansion heuristics for convex polygons when beneficial 3. Prune concave polygon fringes to remove unwanted overhangs 4. Filter tiles by minimum area overlap to remove slivers (with concave center-point check)
class PolygonTileMapper
Fields
MIN_POLY_TILE_OVERLAP_RATIO
const MIN_POLY_TILE_OVERLAP_RATIO = 0.12
Properties
default_ratio
Configuration for area-based filtering thresholds
var default_ratio: float = MIN_POLY_TILE_OVERLAP_RATIO
convex_ratio
var convex_ratio: float = 0.01
expanded_trapezoid_ratio
var expanded_trapezoid_ratio: float = 0.01
expansion_candidate_ratio
var expansion_candidate_ratio: float = 0.01
offsets
var offsets = CollisionGeometryUtils.compute_polygon_tile_offsets(world_points, tile_size, center_tile, tile_shape_val, map)
did_expand_trapezoid
var did_expand_trapezoid: bool = false
was_convex
var was_convex: bool = false
initial_offset_count
var initial_offset_count: int = 0
final_offset_count
var final_offset_count: int = 0
center_tile
var center_tile = map.local_to_map(map.to_local(polygon_node.global_position))
center_tile
var center_tile = map.local_to_map(map.to_local(polygon_node.global_position))
world_points
var world_points = _transform_polygon_world(polygon_node)
tile_size
var tile_size = Vector2(map.tile_set.tile_size)
has_tile_shape
var has_tile_shape = false
tile_shape_val
var tile_shape_val = map.tile_set.tile_shape
offsets
var offsets = CollisionGeometryUtils.compute_polygon_tile_offsets(world_points, tile_size, center_tile, tile_shape_val, map)
tile_shape_val
var tile_shape_val = map.tile_set.tile_shape
offsets
var offsets = CollisionGeometryUtils.compute_polygon_tile_offsets(world_points, tile_size, center_tile, tile_shape_val, map)
is_convex
var is_convex: bool = CollisionGeometryUtils.is_polygon_convex(polygon_node.polygon)
did_expand_trapezoid
var did_expand_trapezoid: bool = false
expansion_result
var expansion_result = _apply_trapezoid_expansion(offsets, world_points, tile_size, center_tile)
thresholds
var thresholds = AreaThresholds.new()
min_area
var min_area = tile_size.x * tile_size.y * min_ratio
filtered
var filtered: Array[Vector2i] = []
abs_tile
var abs_tile = center_tile + candidate
tile_rect
var tile_rect = Rect2(tile_center_local - tile_size/2.0, tile_size)
area
var area = get_polygon_tile_overlap_area(world_points, tile_rect)
result
var result: Dictionary = {"expanded": false, "offsets": offsets}
center_tile
var center_tile = map.local_to_map(map.to_local(polygon_node.global_position))
world_points
var world_points = _transform_polygon_world(polygon_node)
tile_size
var tile_size = Vector2(map.tile_set.tile_size)
has_tile_shape
var has_tile_shape = false
tile_shape_val
var tile_shape_val = map.tile_set.tile_shape
offsets
var offsets = CollisionGeometryUtils.compute_polygon_tile_offsets(world_points, tile_size, center_tile, tile_shape_val, map)
expansion_result
var expansion_result = _apply_trapezoid_expansion(offsets, world_points, tile_size, center_tile)
thresholds
var thresholds = AreaThresholds.new()
result
var result: Dictionary = {"expanded": false, "offsets": offsets}
analysis
var analysis: Dictionary = _analyze_offset_pattern(offsets)
hollow_shape
var hollow_shape: bool = PolygonIndicatorHeuristics.is_hollow(offsets)
expanded
var expanded: Array[Vector2i] = PolygonIndicatorHeuristics.generate_trapezoid_offsets()
validated
var validated: Array[Vector2i] = []
min_ratio
var min_ratio = _determine_area_threshold(is_convex, did_expand, thresholds)
min_area
var min_area = tile_size.x * tile_size.y * min_ratio
filtered
var filtered: Array[Vector2i] = []
abs_tile
var abs_tile = center_tile + candidate
tile_rect
var tile_rect = Rect2(tile_center_local - tile_size/2.0, tile_size)
area
var area = get_polygon_tile_overlap_area(world_points, tile_rect)
should_include
var should_include = area >= min_area
tile_center_world
var tile_center_world = map.to_global(tile_center_local)
eps
var eps = 0.0001
xs
var xs: Array[float] = []
ys
var ys: Array[int] = []
found_x
var found_x = false
found_y
var found_y = false
bounds
var bounds: Rect2 = _compute_polygon_bounds(world_points)
height_tiles
var height_tiles: float = bounds.size.y / tile_size.y
max_y
var max_y = filtered[0].y
adjusted
var adjusted: Array[Vector2i] = []
ys
var ys: Array[int] = []
xs_by_y
var xs_by_y: Dictionary = {}
validated
var validated: Array[Vector2i] = []
tile_area
var tile_area = tile_size.x * tile_size.y
min_expansion_area
var min_expansion_area = tile_area * 0.02
abs_tile
var abs_tile = center_tile + candidate
tile_center_local
var tile_center_local = map.map_to_local(abs_tile)
tile_rect
var tile_rect = Rect2(tile_center_local - tile_size/2.0, tile_size)
area
var area = get_polygon_tile_overlap_area(world_points, tile_rect)
original_set
var original_set: Dictionary = {}
tile_center_local
var tile_center_local = map.map_to_local(abs_tile)
tile_center_world
var tile_center_world = map.to_global(tile_center_local)
poly_bounds
var poly_bounds: Rect2 = _compute_polygon_bounds(polygon)
is_rect
var is_rect: bool = true
corners
var corners: Array[Vector2] = [
found
var found: bool = false
min_pt
var min_pt: Vector2 = polygon[0]
max_pt
var max_pt: Vector2 = polygon[0]
Methods
print_diagnostics
func print_diagnostics() -> void:
Returns
compute_tile_offsets
static func compute_tile_offsets(polygon_node: CollisionPolygon2D, map: TileMapLayer) -> Array[Vector2i]:
Parameters
polygon_nodemap
Returns
compute_tile_offsets_with_positioner
static func compute_tile_offsets_with_positioner(polygon_node: CollisionPolygon2D, map: TileMapLayer, positioner: Node2D) -> Array[Vector2i]:
Parameters
polygon_nodemappositioner
Returns
process_polygon_with_diagnostics
static func process_polygon_with_diagnostics(polygon_node: CollisionPolygon2D, map: TileMapLayer) -> ProcessingResult:
Parameters
polygon_nodemap
Returns
get_polygon_tile_overlap_area
static func get_polygon_tile_overlap_area(polygon: PackedVector2Array, rect: Rect2) -> float:
Parameters
polygonrect