265 lines
8.1 KiB
GDScript
265 lines
8.1 KiB
GDScript
@tool
|
|
extends RefCounted
|
|
class_name SS2D_IndexMap
|
|
|
|
## Maps a set of indicies to an object.
|
|
|
|
var object: Variant = null
|
|
var indicies: PackedInt32Array
|
|
|
|
|
|
## Parameter [param subresources] has no effect, no subresources to duplicate.
|
|
func duplicate(_subresources: bool = false) -> SS2D_IndexMap:
|
|
return SS2D_IndexMap.new(indicies.duplicate(), object)
|
|
|
|
|
|
func _init(i: PackedInt32Array, o: Variant) -> void:
|
|
indicies = i
|
|
object = o
|
|
|
|
|
|
func _to_string() -> String:
|
|
return "[M_2_IDX] (%s) | %s" % [str(object), indicies]
|
|
|
|
|
|
static func is_index_array_valid(idx_array: PackedInt32Array) -> bool:
|
|
return idx_array.size() >= 2
|
|
|
|
|
|
func is_valid() -> bool:
|
|
return SS2D_IndexMap.is_index_array_valid(indicies)
|
|
|
|
|
|
# FIXME: Unused. Remove eventually
|
|
# func get_contiguous_segments() -> Array:
|
|
# if is_contiguous():
|
|
# return [indicies.duplicate()]
|
|
# var segments: Array = []
|
|
# var break_idx: int = find_break()
|
|
# var remainder: Array[int] = indicies.duplicate()
|
|
# while break_idx != -1:
|
|
# var new_slice: Array[int] = []
|
|
# for i in range(0, break_idx):
|
|
# new_slice.push_back(remainder[i])
|
|
# segments.push_back(new_slice)
|
|
# remainder = remainder.slice(break_idx, remainder.size())
|
|
# break_idx = SS2D_IndexMap.find_break_in_array(remainder)
|
|
# if not remainder.is_empty():
|
|
# segments.push_back(remainder)
|
|
# return segments
|
|
|
|
|
|
## Will join together segments that share the same idx,
|
|
## ex. [1,2], [4,5], and [2,3,4] become [1,2,3,4,5]
|
|
static func join_segments(segments: Array[PackedInt32Array]) -> Array[PackedInt32Array]:
|
|
var final_segments: Array[PackedInt32Array] = []
|
|
final_segments.assign(segments.duplicate())
|
|
|
|
var to_join_tuple: Vector2i
|
|
var join_performed := true
|
|
while join_performed:
|
|
join_performed = false
|
|
for i in range(0, final_segments.size()):
|
|
if join_performed:
|
|
break
|
|
for ii in range(i + 1, final_segments.size()):
|
|
var a := final_segments[i]
|
|
var b := final_segments[ii]
|
|
if a[-1] == b[0]:
|
|
to_join_tuple = Vector2i(i, ii)
|
|
join_performed = true
|
|
if b[-1] == a[0]:
|
|
to_join_tuple = Vector2i(ii, i)
|
|
join_performed = true
|
|
if join_performed:
|
|
break
|
|
if join_performed:
|
|
var idx_lowest: int = to_join_tuple[0]
|
|
var idx_highest: int = to_join_tuple[1]
|
|
var lowest: PackedInt32Array = final_segments[idx_lowest]
|
|
var highest: PackedInt32Array = final_segments[idx_highest]
|
|
final_segments.erase(lowest)
|
|
final_segments.erase(highest)
|
|
# pop the shared idx from lowest
|
|
lowest.remove_at(lowest.size() - 1)
|
|
var new_segment := lowest + highest
|
|
final_segments.push_back(new_segment)
|
|
|
|
return final_segments
|
|
|
|
|
|
## Does each index increment by 1 without any breaks.
|
|
func is_contiguous() -> bool:
|
|
return SS2D_IndexMap.is_array_contiguous(indicies)
|
|
|
|
|
|
static func is_array_contiguous(a: PackedInt32Array) -> bool:
|
|
return find_break_in_array(a) == -1
|
|
|
|
|
|
## Find a break in the indexes where they aren't contiguous.[br]
|
|
## Will return -1 if there's no break.[br]
|
|
func find_break() -> int:
|
|
return SS2D_IndexMap.find_break_in_array(indicies)
|
|
|
|
|
|
static func find_break_in_array(a: PackedInt32Array, offset: int = 0) -> int:
|
|
for i in range(offset, a.size() - 1, 1):
|
|
if is_break_at_index_in_array(a, i):
|
|
return i + 1
|
|
return -1
|
|
|
|
|
|
## Whether there is a break at the given index.[br]
|
|
## Will return -1 if there's no break.[br]
|
|
func is_break_at_index(i: int) -> bool:
|
|
return SS2D_IndexMap.is_break_at_index_in_array(indicies, i)
|
|
|
|
|
|
static func is_break_at_index_in_array(a: PackedInt32Array, i: int) -> bool:
|
|
var difference: int = absi((a[i]) - (a[i + 1]))
|
|
return difference != 1
|
|
|
|
|
|
func has_index(idx: int) -> bool:
|
|
return indicies.has(idx)
|
|
|
|
|
|
# FIXME: Unused, remove eventually.
|
|
# func lowest_index() -> int:
|
|
# return indicies.min()
|
|
#
|
|
#
|
|
# func highest_index() -> int:
|
|
# return indicies.max()
|
|
|
|
|
|
# FIXME: Unused, remove eventually
|
|
func _split_indicies_into_multiple_mappings(new_indicies: PackedInt32Array) -> Array[SS2D_IndexMap]:
|
|
var maps: Array[SS2D_IndexMap] = []
|
|
var break_idx := SS2D_IndexMap.find_break_in_array(new_indicies)
|
|
var offset := 0
|
|
var sub_indicies: PackedInt32Array
|
|
|
|
while break_idx != -1:
|
|
sub_indicies = new_indicies.slice(offset, break_idx)
|
|
|
|
if SS2D_IndexMap.is_index_array_valid(sub_indicies):
|
|
maps.push_back(SS2D_IndexMap.new(sub_indicies, object))
|
|
|
|
offset = break_idx
|
|
break_idx = SS2D_IndexMap.find_break_in_array(new_indicies, offset)
|
|
|
|
sub_indicies = new_indicies.slice(offset)
|
|
|
|
if SS2D_IndexMap.is_index_array_valid(sub_indicies):
|
|
maps.push_back(SS2D_IndexMap.new(sub_indicies, object))
|
|
|
|
return maps
|
|
|
|
|
|
## FIXME: Unused, remove eventually
|
|
## Will create a new set of SS2D_IndexMaps. [br][br]
|
|
##
|
|
## The new set will contain all of the indicies of the current set,
|
|
## minus the ones specified in the indicies parameter. [br][br]
|
|
##
|
|
## Example: [br]
|
|
## indicies = [0,1,2,3,4,5,6] [br]
|
|
## to_remove = [3,4] [br]
|
|
## new_sets = [0,1,2] [5,6] [br][br]
|
|
##
|
|
## This may split the IndexMap or make it invalid entirely.
|
|
## As a result, the returned array could have 0 or several IndexMaps.
|
|
func remove_indicies(to_remove: PackedInt32Array) -> Array[SS2D_IndexMap]:
|
|
var out: Array[SS2D_IndexMap] = []
|
|
var new_indicies := indicies.duplicate()
|
|
|
|
for r in to_remove:
|
|
var idx := new_indicies.find(r)
|
|
if idx >= 0:
|
|
new_indicies.remove_at(idx)
|
|
|
|
if not SS2D_IndexMap.is_index_array_valid(new_indicies):
|
|
return out
|
|
|
|
if SS2D_IndexMap.is_array_contiguous(new_indicies):
|
|
out.push_back(SS2D_IndexMap.new(new_indicies, object))
|
|
return out
|
|
|
|
return _split_indicies_into_multiple_mappings(new_indicies)
|
|
|
|
|
|
## Will create a new set of SS2D_IndexMaps. [br][br]
|
|
##
|
|
## The new set will contain all of the edges of the current set,
|
|
## minus the ones specified in the indicies parameter. [br][br]
|
|
##
|
|
## Example: [br]
|
|
## indicies = [0,1,2,3,4,5,6] [br]
|
|
## to_remove = [4,5] [br]
|
|
## new_sets = [0,1,2,3,4] [4,5,6] [br][br]
|
|
##
|
|
## This may split the IndexMap or make it invalid entirely.
|
|
## As a result, the returned array could have 0 or several IndexMaps.
|
|
func remove_edges(to_remove: PackedInt32Array) -> Array[SS2D_IndexMap]:
|
|
# Corner case
|
|
if to_remove.size() == 2:
|
|
var idx: int = indicies.find(to_remove[0])
|
|
if idx != indicies.size()-1:
|
|
if indicies[idx+1] == to_remove[1]:
|
|
# Need one split
|
|
var set_1 := indicies.slice(0, idx+1)
|
|
var set_2 := indicies.slice(idx+1, indicies.size())
|
|
var new_maps: Array[SS2D_IndexMap] = []
|
|
if SS2D_IndexMap.is_index_array_valid(set_1):
|
|
new_maps.push_back(SS2D_IndexMap.new(set_1, object))
|
|
if SS2D_IndexMap.is_index_array_valid(set_2):
|
|
new_maps.push_back(SS2D_IndexMap.new(set_2, object))
|
|
return new_maps
|
|
return [SS2D_IndexMap.new(indicies, object)]
|
|
|
|
# General case
|
|
var new_edges := SS2D_IndexMap.indicies_to_edges(indicies)
|
|
for i in range(0, to_remove.size() - 1, 1):
|
|
var idx1: int = to_remove[i]
|
|
var idx2: int = to_remove[i + 1]
|
|
var edges_to_remove := PackedInt32Array()
|
|
for ii in new_edges.size():
|
|
var edge := new_edges[ii]
|
|
if (edge[0] == idx1 or edge[0] == idx2) and (edge[1] == idx1 or edge[1] == idx2):
|
|
edges_to_remove.push_back(ii)
|
|
# Reverse iterate
|
|
for ii in range(edges_to_remove.size()-1, -1, -1):
|
|
new_edges.remove_at(edges_to_remove[ii])
|
|
|
|
new_edges = SS2D_IndexMap.join_segments(new_edges)
|
|
var new_index_mappings: Array[SS2D_IndexMap] = []
|
|
for e in new_edges:
|
|
new_index_mappings.push_back(SS2D_IndexMap.new(e, object))
|
|
return new_index_mappings
|
|
|
|
|
|
# NOTE: Even though it makes more sense to return an Array[Vector2i], we return PackedInt32Arrays
|
|
# instead because it makes things easier in the context where this function output is needed.
|
|
static func indicies_to_edges(p_indicies: PackedInt32Array) -> Array[PackedInt32Array]:
|
|
var edges: Array[PackedInt32Array] = []
|
|
for i in p_indicies.size() - 1:
|
|
var edge := PackedInt32Array([ i, i+1 ])
|
|
if absi(edge[0] - edge[1]) == 1:
|
|
edges.push_back(edge)
|
|
return edges
|
|
|
|
|
|
## Returns a Dict[Variant, Array[SS2D_IndexMap]]
|
|
static func index_map_array_sort_by_object(imaps: Array) -> Dictionary:
|
|
var dict := {}
|
|
for imap: SS2D_IndexMap in imaps:
|
|
if not dict.has(imap.object):
|
|
var arr: Array[SS2D_IndexMap] = [ imap ]
|
|
dict[imap.object] = arr
|
|
else:
|
|
var arr: Array[SS2D_IndexMap] = dict[imap.object]
|
|
arr.push_back(imap)
|
|
return dict
|