72 lines
3.6 KiB
GDScript
72 lines
3.6 KiB
GDScript
@tool
|
|
extends RefCounted
|
|
|
|
# Find the roof outline from two diagonal corners at the top
|
|
# of parallel walls, even when the structure is rotated around Y.
|
|
static func find_corners(root: ModularBuilderPiece, first_world: Vector3, second_world: Vector3, allow_center: bool = false) -> PackedVector3Array:
|
|
var first_edges := _matching_top_edges(root, first_world, allow_center)
|
|
var second_edges := _matching_top_edges(root, second_world, allow_center)
|
|
var best_corners := PackedVector3Array()
|
|
var best_score := INF
|
|
var best_length := 0.0
|
|
for first_edge in first_edges:
|
|
for second_edge in second_edges:
|
|
if first_edge["wall"] == second_edge["wall"]:
|
|
continue
|
|
var a: Vector3 = first_edge["selected"]
|
|
var adjacent_a: Vector3 = first_edge["other"]
|
|
var c: Vector3 = second_edge["selected"]
|
|
var adjacent_c: Vector3 = second_edge["other"]
|
|
var edge_a := Vector2(adjacent_a.x - a.x, adjacent_a.z - a.z)
|
|
var edge_c := Vector2(adjacent_c.x - c.x, adjacent_c.z - c.z)
|
|
var length_a := edge_a.length()
|
|
var length_c := edge_c.length()
|
|
if length_a < 0.2 or length_c < 0.2:
|
|
continue
|
|
if edge_a.normalized().dot(edge_c.normalized()) > -0.9:
|
|
continue
|
|
var center_a := (a + adjacent_a) * 0.5
|
|
var center_c := (c + adjacent_c) * 0.5
|
|
if Vector2(center_c.x - center_a.x, center_c.z - center_a.z).length() < 0.2:
|
|
continue
|
|
var diagonal_error := (a + c - adjacent_a - adjacent_c).length()
|
|
var length_error := absf(length_a - length_c)
|
|
var tolerance := maxf(0.12, maxf(first_edge["thickness"], second_edge["thickness"]) * 0.5)
|
|
if diagonal_error > tolerance or length_error > tolerance:
|
|
continue
|
|
var score := diagonal_error + length_error
|
|
if score < best_score - 0.01 or (absf(score - best_score) <= 0.01 and length_a > best_length):
|
|
best_score = score
|
|
best_length = length_a
|
|
best_corners = PackedVector3Array([a, adjacent_a, c, adjacent_c])
|
|
return best_corners
|
|
|
|
static func _matching_top_edges(root: ModularBuilderPiece, world_point: Vector3, allow_center: bool) -> Array[Dictionary]:
|
|
var result: Array[Dictionary] = []
|
|
var walls: Array[ModularBuilderPiece] = []
|
|
_collect_walls(root, walls)
|
|
for wall in walls:
|
|
var endpoints: PackedVector3Array = wall.call("_get_centered_wall_points") if wall.center_align else wall.custom_points
|
|
if endpoints.size() < 2:
|
|
continue
|
|
var direction := endpoints[1] - endpoints[0]
|
|
var horizontal := Vector2(direction.x, direction.z)
|
|
if horizontal.length() < 0.2:
|
|
continue
|
|
var side := Vector3(-horizontal.y, 0.0, horizontal.x).normalized() * wall.thickness * 0.5
|
|
for offset in [side, -side]:
|
|
var top_a := wall.to_global(endpoints[0] + Vector3.UP * wall.unit_size + offset)
|
|
var top_b := wall.to_global(endpoints[1] + Vector3.UP * wall.unit_size + offset)
|
|
var center_a := wall.to_global(endpoints[0] + Vector3.UP * wall.unit_size)
|
|
var center_b := wall.to_global(endpoints[1] + Vector3.UP * wall.unit_size)
|
|
if top_a.distance_to(world_point) <= 0.08 or (allow_center and center_a.distance_to(world_point) <= 0.08):
|
|
result.append({"wall": wall, "selected": root.to_local(top_a), "other": root.to_local(top_b), "thickness": wall.thickness})
|
|
if top_b.distance_to(world_point) <= 0.08 or (allow_center and center_b.distance_to(world_point) <= 0.08):
|
|
result.append({"wall": wall, "selected": root.to_local(top_b), "other": root.to_local(top_a), "thickness": wall.thickness})
|
|
return result
|
|
|
|
static func _collect_walls(node: Node, result: Array[ModularBuilderPiece]) -> void:
|
|
if node is ModularBuilderPiece and node.piece_type == ModularBuilderPiece.PieceType.WALL:
|
|
result.append(node)
|
|
for child in node.get_children():
|
|
_collect_walls(child, result)
|