141 lines
3.7 KiB
GDScript
141 lines
3.7 KiB
GDScript
extends ImageScript
|
|
|
|
const ROOT2DIV2 = 0.7071
|
|
|
|
|
|
func _get_param_list():
|
|
var default_curve = Curve.new()
|
|
default_curve.add_point(Vector2(0.0, 0.5), 1, 1, Curve.TANGENT_LINEAR, Curve.TANGENT_LINEAR)
|
|
default_curve.add_point(Vector2(1.0, 0.5), 1, 1, Curve.TANGENT_LINEAR, Curve.TANGENT_LINEAR)
|
|
return [
|
|
[
|
|
"Bevel",
|
|
SCRIPT_PARAM_INT,
|
|
1,
|
|
[1, 150]
|
|
],
|
|
[
|
|
"Bevel Distance",
|
|
SCRIPT_PARAM_ENUM,
|
|
0,
|
|
["Circle", "Square", "Diamond"]
|
|
],
|
|
]
|
|
|
|
|
|
func _get_image(new_image, selection):
|
|
var image_size : Vector2i = new_image.get_size()
|
|
var closest_edge : Array[Vector2] = []
|
|
var entropy := {}
|
|
closest_edge.resize(image_size.x * image_size.y)
|
|
|
|
var pix : Color
|
|
var vec : Vector2
|
|
for i in image_size.x:
|
|
for j in image_size.y:
|
|
pix = new_image.get_pixel(i, j)
|
|
if pix.a == 0.0:
|
|
closest_edge[i + j * image_size.x] = Vector2.ZERO
|
|
|
|
else:
|
|
vec = Vector2(0, 0)
|
|
if i == 0: vec.x -= 1
|
|
if j == 0: vec.y -= 1
|
|
if i == image_size.x - 1: vec.x += 1
|
|
if j == image_size.y - 1: vec.y += 1
|
|
|
|
if vec == Vector2.ZERO:
|
|
vec = Vector2(-INF, -INF)
|
|
entropy[Vector2(i, j)] = 5
|
|
|
|
closest_edge[i + j * image_size.x] = vec
|
|
|
|
var neighbors = []
|
|
var neighbor_pos = []
|
|
var iters = 0
|
|
var closest_edge_new : Array[Vector2]
|
|
while entropy.size() > 0:
|
|
iters += 1
|
|
closest_edge_new = closest_edge.duplicate()
|
|
for pos in entropy.keys():
|
|
neighbor_pos = [
|
|
pos + Vector2.RIGHT,
|
|
pos + Vector2.DOWN,
|
|
pos + Vector2.LEFT,
|
|
pos + Vector2.UP,
|
|
]
|
|
var any_neighbor_defined = false
|
|
for x in neighbor_pos:
|
|
if closest_edge[x.x + x.y * image_size.x] != Vector2(-INF, -INF):
|
|
any_neighbor_defined = true
|
|
break
|
|
|
|
if any_neighbor_defined:
|
|
entropy.erase(pos)
|
|
|
|
neighbors = [
|
|
closest_edge[pos.x + 1 + pos.y * image_size.x]
|
|
if !entropy.has(pos + Vector2.RIGHT) else Vector2(-INF, -INF),
|
|
closest_edge[pos.x + (pos.y + 1) * image_size.x]
|
|
if !entropy.has(pos + Vector2.DOWN) else Vector2(-INF, -INF),
|
|
closest_edge[pos.x - 1 + pos.y * image_size.x]
|
|
if !entropy.has(pos + Vector2.LEFT) else Vector2(-INF, -INF),
|
|
closest_edge[pos.x + (pos.y - 1) * image_size.x]
|
|
if !entropy.has(pos + Vector2.UP) else Vector2(-INF, -INF),
|
|
]
|
|
closest_edge_new[pos.x + pos.y * image_size.x] = get_closest_edge(neighbors)
|
|
|
|
closest_edge = closest_edge_new
|
|
|
|
var bevel = get_param("Bevel")
|
|
var dist_func = [
|
|
# Three different distance calculations have their own artifacts..
|
|
func (x): return x.length_squared() > bevel * 2.0, # Circle
|
|
func (x): return max(abs(x.x), abs(x.y)) > bevel, # Square
|
|
func (x): return abs(x.x) + abs(x.y) > bevel * 2.0, # Diamond
|
|
][get_param("Bevel Distance")]
|
|
|
|
var vec3 : Vector3
|
|
var vec_len : float
|
|
var vec_dir : Vector2
|
|
for i in image_size.x:
|
|
for j in image_size.y:
|
|
vec = closest_edge[i + j * image_size.x]
|
|
if vec == Vector2.ZERO || dist_func.call(vec):
|
|
new_image.set_pixel(i, j, Color(0.5, 0.5, 1.0))
|
|
continue
|
|
|
|
vec_len = vec.length()
|
|
vec_dir = vec / vec_len
|
|
vec3 = Vector3.BACK.rotated(
|
|
Vector3(-vec_dir.y, vec_dir.x, 0.0),
|
|
PI * 0.25
|
|
)
|
|
vec3 = vec3 * Vector3(0.5, -0.5, 1.0) + Vector3(0.5, 0.5, 0.0)
|
|
|
|
new_image.set_pixel(i, j, Color(vec3.x, vec3.y, vec3.z))
|
|
|
|
return new_image
|
|
|
|
|
|
func get_closest_edge(neighbor_vecs):
|
|
var lengths = [
|
|
neighbor_vecs[0].length_squared(),
|
|
neighbor_vecs[1].length_squared(),
|
|
neighbor_vecs[2].length_squared(),
|
|
neighbor_vecs[3].length_squared(),
|
|
]
|
|
var closest_length = INF
|
|
for i in 4:
|
|
if lengths[i] < closest_length:
|
|
closest_length = lengths[i]
|
|
|
|
var result = Vector2.ZERO
|
|
var neighbor_dirs = [Vector2.RIGHT, Vector2.DOWN, Vector2.LEFT, Vector2.UP]
|
|
var coeff = 1.0
|
|
for i in 4:
|
|
if lengths[i] == closest_length:
|
|
result += neighbor_vecs[i] + neighbor_dirs[i]
|
|
|
|
return result
|