1
0
Fork 0
mirror of https://github.com/Orama-Interactive/Pixelorama.git synced 2025-01-19 09:39:48 +00:00
Pixelorama/src/Palette/Palette.gd

202 lines
5.9 KiB
GDScript

class_name Palette
extends Resource
const DEFAULT_WIDTH = 8
const DEFAULT_HEIGHT = 8
# Metadata
export var name: String = "Custom Palette"
export var comment: String = ""
# Grid size
export var width := DEFAULT_WIDTH
export var height := DEFAULT_HEIGHT
# Sparse colors dictionary - actual color position in the palette is determined by it's index
export var colors: Dictionary = {}
# How many colors fit in palette grid
export var colors_max := 0
func _init(init_name: String = "Custom Palette", init_width: int = DEFAULT_WIDTH, init_height: int = DEFAULT_HEIGHT, init_comment: String = "") -> void:
name = init_name
comment = init_comment
width = init_width
height = init_height
colors_max = init_width * init_height
colors = {}
func edit(new_name: String, new_width: int, new_height: int, new_comment: String) -> void:
var old_width = width
width = new_width
height = new_height
name = new_name
comment = new_comment
var old_colors_max = colors_max
colors_max = width * height
if colors_max < old_colors_max:
# If size was reduced colors must be reindexed to fit into new smaller size
reindex_colors_on_size_reduce(true)
if old_width < new_width and colors_max > old_colors_max:
# If width increases colors have to be reindexed so they keep same grid positions
# unless the height has become smaller and we have to re-position the colors so that they won't get erased
reindex_colors_on_width_increase(old_width)
# Iterates all colors from lowest index and reindexes them so they start at zero index
# Remove trailing removes all colors that are over colors_max limit and thus don't fit into grid
func reindex_colors_on_size_reduce(remove_trailing: bool) -> void:
var sorted_colors_indexes = colors.keys()
sorted_colors_indexes.sort()
var new_index = 0
for old_index in sorted_colors_indexes:
# Color cannot fit into grid anymore - erase it
if remove_trailing and new_index >= colors_max:
colors.erase(old_index)
# Move color to new lower index - erase it from it's original index
elif new_index < old_index:
colors[new_index] = colors[old_index]
colors[new_index].index = new_index
colors.erase(old_index)
new_index += 1
# Adds difference of old and new width to color indexes
# so they remain on the same position as before resize
func reindex_colors_on_width_increase(old_width: int) -> void:
var sorted_colors_indexes = colors.keys()
sorted_colors_indexes.sort()
var new_colors = {}
for old_index in sorted_colors_indexes:
var new_index: int = old_index + (width - old_width) * (old_index / old_width)
new_colors[new_index ] = colors[old_index]
colors = new_colors
# Adds new color to the first empty swatch
func add_color(new_color: Color, start_index: int = 0) -> void:
if start_index >= colors_max:
return
# If palette is full automatically increase the palette height
if is_full():
height += 1
# Find the first empty index since start index and insert a new color
for i in range(start_index, colors_max):
if not colors.has(i):
colors[i] = PaletteColor.new(new_color, i)
break
# Returns color at index or null if no color exists
func get_color(index: int):
var palette_color = colors.get(index)
if palette_color != null:
return palette_color.color
return null
# Changes color data
func set_color(index: int, new_color: Color) -> void:
if self.colors.has(index):
self.colors[index].color = new_color
# Removes a color at the specified index
func remove_color(index: int) -> void:
colors.erase(index)
# Inserts a color to the specified index
# If index is already occupied move the original color to right
func insert_color(index: int, new_color: Color) -> void:
var c := PaletteColor.new(new_color, index)
# If insert happens on non empty swatch recursively move the original color
# and every other color to it's right one swatch to right
if colors.get(index) != null:
move_right(index)
colors[index] = c
# Recursive function that moves every color to right until one of them is moved to empty swatch
func move_right(index: int) -> void:
# Moving colors to right would overflow the size of the palette so increase it's height automatically
if index + 1 == colors_max:
height += 1
colors_max = width * height
# If swatch to right to this color is not empty move that color right too
if colors.get(index + 1) != null:
move_right(index + 1)
colors[index + 1] = colors.get(index)
# Swaps two colors
func swap_colors(from_index: int, to_index: int) -> void:
var from_color = colors.get(from_index)
var to_color = colors.get(to_index)
if not from_color and to_color:
colors[from_index] = to_color
colors[from_index].index = from_index
colors.erase(to_index)
elif from_color and not to_color:
colors[to_index] = from_color
colors[to_index].index = to_index
colors.erase(from_index)
elif from_color and to_color:
colors[to_index] = from_color
colors[to_index].index = to_index
colors[from_index] = to_color
colors[from_index].index = from_index
# Copies color
func copy_colors(from_index: int, to_index: int) -> void:
# Only allow copy of existing colors
if colors.has(from_index):
colors[to_index] = colors[from_index].duplicate()
colors[to_index].index = to_index
# True if all swatches are occupied
func is_full() -> bool:
return self.colors.size() >= self.colors_max
# True if palette has no colors
func is_empty() -> bool:
return self.colors.size() == 0
func has_color(color: Color) -> bool:
for palette_color in colors.values():
if palette_color.color == color:
return true
return false
# Sets name that is used to save the palette to disk
func set_resource_name(new_resource_name: String) -> void:
# Store palette path name only with valid path characters
resource_name = strip_unvalid_characters(new_resource_name)
static func strip_unvalid_characters(string_to_strip: String) -> String:
var regex := RegEx.new()
regex.compile("[^a-zA-Z0-9_]+")
return regex.sub(string_to_strip, "", true)