Files
przygody-pana-cegly/addons/rmsmartshape/shapes/index_map.gd

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