Refactored isometric grid (#430)

* Move get_tile_mode_rect() method to the Project class

* Refactored isometric grid

Now it should be drawn properly in the target rect.
Settings (in pixels):
- cell's AABB size,
- offset from the canvas origin.
This commit is contained in:
kleonc 2021-01-18 21:59:26 +01:00 committed by GitHub
parent 9f8c9807e7
commit 9c520a65e9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 192 additions and 85 deletions

View file

@ -54,7 +54,10 @@ var default_fill_color := Color(0, 0, 0, 0)
var grid_type = Grid_Types.CARTESIAN
var grid_width := 2
var grid_height := 2
var grid_isometric_cell_size := 2
var grid_isometric_cell_bounds_width := 16
var grid_isometric_cell_bounds_height := 8
var grid_isometric_offset_x := 0
var grid_isometric_offset_y := 0
var grid_color := Color.black
var pixel_grid_show_at_zoom := 1500.0 # percentage
var pixel_grid_color := Color("91212121")
@ -343,7 +346,6 @@ func undo(_frame_index := -1, _layer_index := -1, project : Project = current_pr
if action_name == "Scale":
canvas.camera_zoom()
Global.canvas.grid.isometric_polylines.clear()
Global.canvas.grid.update()
Global.canvas.pixel_grid.update()
Global.cursor_position_label.text = "[%s×%s]" % [project.size.x, project.size.y]
@ -375,7 +377,6 @@ func redo(_frame_index := -1, _layer_index := -1, project : Project = current_pr
if action_name == "Scale":
canvas.camera_zoom()
Global.canvas.grid.isometric_polylines.clear()
Global.canvas.grid.update()
Global.canvas.pixel_grid.update()
Global.cursor_position_label.text = "[%s×%s]" % [project.size.x, project.size.y]

View file

@ -174,7 +174,6 @@ func change_project() -> void:
i += 1
Global.zoom_level_label.text = str(round(100 / Global.camera.zoom.x)) + " %"
Global.canvas.update()
Global.canvas.grid.isometric_polylines.clear()
Global.canvas.grid.update()
Global.canvas.pixel_grid.update()
Global.transparent_checker._ready()
@ -566,3 +565,16 @@ func has_changed_changed(value : bool) -> void:
Global.tabs.set_tab_title(Global.tabs.current_tab, name + "(*)")
else:
Global.tabs.set_tab_title(Global.tabs.current_tab, name)
func get_tile_mode_rect() -> Rect2:
match Global.current_project.tile_mode:
Global.Tile_Mode.NONE:
return Rect2(Vector2.ZERO, size)
Global.Tile_Mode.XAXIS:
return Rect2(Vector2(-1, 0) * size, Vector2(3, 1) * size)
Global.Tile_Mode.YAXIS:
return Rect2(Vector2(0, -1) * size, Vector2(1, 3) * size)
Global.Tile_Mode.BOTH:
return Rect2(Vector2(-1, -1) * size, Vector2(3, 3) * size)
return Rect2()

View file

@ -20,7 +20,10 @@ var preferences = [
["grid_type", "Canvas/GridOptions/GridType", "selected", Global.grid_type],
["grid_width", "Canvas/GridOptions/GridWidthValue", "value", Global.grid_width],
["grid_height", "Canvas/GridOptions/GridHeightValue", "value", Global.grid_height],
["grid_isometric_cell_size", "Canvas/GridOptions/IsometricCellSizeValue", "value", Global.grid_isometric_cell_size],
["grid_isometric_cell_bounds_width", "Canvas/GridOptions/IsometricCellBoundsWidthValue", "value", Global.grid_isometric_cell_bounds_width],
["grid_isometric_cell_bounds_height", "Canvas/GridOptions/IsometricCellBoundsHeightValue", "value", Global.grid_isometric_cell_bounds_height],
["grid_isometric_offset_x", "Canvas/GridOptions/IsometricGridOffsetXValue", "value", Global.grid_isometric_offset_x],
["grid_isometric_offset_y", "Canvas/GridOptions/IsometricGridOffsetYValue", "value", Global.grid_isometric_offset_y],
["grid_color", "Canvas/GridOptions/GridColor", "color", Global.grid_color],
["pixel_grid_show_at_zoom", "Canvas/PixelGridOptions/ShowAtZoom", "value", Global.pixel_grid_show_at_zoom],
["pixel_grid_color", "Canvas/PixelGridOptions/GridColor", "color", Global.pixel_grid_color],
@ -128,8 +131,7 @@ func preference_update(prop : String) -> void:
else:
autosave_interval.mouse_default_cursor_shape = Control.CURSOR_FORBIDDEN
if prop in ["grid_type", "grid_width", "grid_height", "grid_isometric_cell_size", "grid_color"]:
Global.canvas.grid.isometric_polylines.clear()
if prop in ["grid_type", "grid_width", "grid_height", "grid_isometric_cell_bounds_width", "grid_isometric_cell_bounds_height", "grid_isometric_offset_x", "grid_isometric_offset_y", "grid_color"]:
Global.canvas.grid.update()
if prop in ["pixel_grid_show_at_zoom", "pixel_grid_color"]:

View file

@ -342,24 +342,88 @@ rounded = true
align = 2
suffix = "px"
[node name="IsometricCellSizeLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Canvas/GridOptions"]
[node name="IsometricCellBoundsWidthLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Canvas/GridOptions"]
margin_top = 57.0
margin_right = 123.0
margin_bottom = 71.0
hint_tooltip = "Sets the size of the cells in an isometric grid"
hint_tooltip = "Sets the width of the isometric cell's axis aligned bounding box"
mouse_filter = 0
text = "Isometric cell size:"
text = "Isometric cell bounds width:"
[node name="IsometricCellSizeValue" type="SpinBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/Canvas/GridOptions"]
[node name="IsometricCellBoundsWidthValue" type="SpinBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/Canvas/GridOptions"]
margin_left = 127.0
margin_top = 52.0
margin_right = 230.0
margin_bottom = 76.0
hint_tooltip = "Sets the size of the cells in an isometric grid"
hint_tooltip = "Sets the width of the isometric cell's axis aligned bounding box"
mouse_default_cursor_shape = 2
min_value = 2.0
max_value = 16384.0
value = 2.0
value = 16.0
rounded = true
align = 2
suffix = "px"
[node name="IsometricCellBoundsHeightLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Canvas/GridOptions"]
margin_top = 57.0
margin_right = 123.0
margin_bottom = 71.0
hint_tooltip = "Sets the height of the isometric cell's axis aligned bounding box"
mouse_filter = 0
text = "Isometric cell bounds height:"
[node name="IsometricCellBoundsHeightValue" type="SpinBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/Canvas/GridOptions"]
margin_left = 127.0
margin_top = 52.0
margin_right = 230.0
margin_bottom = 76.0
hint_tooltip = "Sets the height of the isometric cell's axis aligned bounding box"
mouse_default_cursor_shape = 2
min_value = 2.0
max_value = 16384.0
value = 8.0
rounded = true
align = 2
suffix = "px"
[node name="IsometricGridOffsetXLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Canvas/GridOptions"]
margin_top = 57.0
margin_right = 123.0
margin_bottom = 71.0
hint_tooltip = "Sets isometric grid's x offset from the canvas origin (top left corner of the image)"
mouse_filter = 0
text = "Isometric grid offset x:"
[node name="IsometricGridOffsetXValue" type="SpinBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/Canvas/GridOptions"]
margin_left = 127.0
margin_top = 52.0
margin_right = 230.0
margin_bottom = 76.0
hint_tooltip = "Sets isometric grid's x offset from the canvas origin (top left corner of the image)"
mouse_default_cursor_shape = 2
min_value = -16384.0
max_value = 16384.0
rounded = true
align = 2
suffix = "px"
[node name="IsometricGridOffsetYLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Canvas/GridOptions"]
margin_top = 57.0
margin_right = 123.0
margin_bottom = 71.0
hint_tooltip = "Sets isometric grid's y offset from the canvas origin (top left corner of the image)"
mouse_filter = 0
text = "Isometric grid offset y:"
[node name="IsometricGridOffsetYValue" type="SpinBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/Canvas/GridOptions"]
margin_left = 127.0
margin_top = 52.0
margin_right = 230.0
margin_bottom = 76.0
hint_tooltip = "Sets isometric grid's y offset from the canvas origin (top left corner of the image)"
mouse_default_cursor_shape = 2
min_value = -16384.0
max_value = 16384.0
rounded = true
align = 2
suffix = "px"

View file

@ -81,18 +81,6 @@ func _get_draw_rect() -> Rect2:
return Rect2(selected_pixels[0].x, selected_pixels[0].y, selected_pixels[-1].x - selected_pixels[0].x + 1, selected_pixels[-1].y - selected_pixels[0].y + 1)
func _get_tile_mode_rect() -> Rect2:
match Global.current_project.tile_mode:
Global.Tile_Mode.XAXIS:
return Rect2(Vector2(-Global.current_project.size.x,0), Vector2(Global.current_project.size.x * 3,Global.current_project.size.y))
Global.Tile_Mode.YAXIS:
return Rect2(Vector2(0,-Global.current_project.size.y), Vector2(Global.current_project.size.x,Global.current_project.size.y * 3))
Global.Tile_Mode.BOTH:
return Rect2(-Global.current_project.size, Global.current_project.size * 3)
return Rect2(Vector2(0,0),Global.current_project.size)
func _get_draw_image() -> Image:
var project : Project = Global.current_project
return project.frames[project.current_frame].cels[project.current_layer].image

View file

@ -257,11 +257,12 @@ func draw_tool_circle(position : Vector2, fill := false) -> void:
func draw_tool_brush(position : Vector2) -> void:
var project : Project = Global.current_project
if Global.mirror_view:
position.x = Global.current_project.size.x - position.x
position.x = project.size.x - position.x
if Global.current_project.tile_mode and _get_tile_mode_rect().has_point(position):
position = position.posmodv(Global.current_project.size)
if project.tile_mode and project.get_tile_mode_rect().has_point(position):
position = position.posmodv(project.size)
var size := _brush_image.get_size()
var dst := position - (size / 2).floor()
@ -273,7 +274,6 @@ func draw_tool_brush(position : Vector2) -> void:
var src_rect := Rect2(dst_rect.position - dst, dst_rect.size)
dst = dst_rect.position
var project : Project = Global.current_project
_draw_brush_image(_brush_image, src_rect, dst)
# Handle Mirroring
@ -305,7 +305,7 @@ func draw_tool_brush(position : Vector2) -> void:
func draw_indicator() -> void:
draw_indicator_at(_cursor, Vector2.ZERO, Color.blue)
if Global.current_project.tile_mode and _get_tile_mode_rect().has_point(_cursor):
if Global.current_project.tile_mode and Global.current_project.get_tile_mode_rect().has_point(_cursor):
var tile := _line_start if _draw_line else _cursor
if not tile in Global.current_project.selected_pixels:
var offset := tile - tile.posmodv(Global.current_project.size)
@ -336,7 +336,7 @@ func _set_pixel(position : Vector2) -> void:
var project : Project = Global.current_project
if Global.mirror_view:
position.x = project.size.x - position.x - 1
if Global.current_project.tile_mode and _get_tile_mode_rect().has_point(position):
if project.tile_mode and project.get_tile_mode_rect().has_point(position):
position = position.posmodv(project.size)
var entire_image_selected : bool = project.selected_pixels.size() == project.size.x * project.size.y

View file

@ -1,50 +1,86 @@
extends Node2D
var isometric_polylines := [] # An array of PoolVector2Arrays
func _draw() -> void:
if Global.draw_grid:
draw_grid(Global.grid_type)
if not Global.draw_grid:
return
var rect := Rect2(Vector2.ZERO, Global.current_project.size)
if rect.has_no_area():
return
func draw_grid(grid_type : int) -> void:
var size : Vector2 = Global.current_project.size
var grid_type : int = Global.grid_type
if grid_type == Global.Grid_Types.CARTESIAN || grid_type == Global.Grid_Types.ALL:
for x in range(0, size.x + 1, Global.grid_width):
draw_line(Vector2(x, 0), Vector2(x, size.y), Global.grid_color)
var start_x : float = wrapf(0, rect.position.x, rect.position.x + Global.grid_width)
for x in range(start_x, rect.end.x + 1, Global.grid_width):
draw_line(Vector2(x, rect.position.y), Vector2(x, rect.end.y), Global.grid_color)
for y in range(0, size.y + 1, Global.grid_height):
draw_line(Vector2(0, y), Vector2(size.x, y), Global.grid_color)
var start_y : float = wrapf(0, rect.position.y, rect.position.y + Global.grid_height)
for y in range(start_y, rect.end.y + 1, Global.grid_height):
draw_line(Vector2(rect.position.x, y), Vector2(rect.end.x, y), Global.grid_color)
if grid_type == Global.Grid_Types.ISOMETRIC || grid_type == Global.Grid_Types.ALL:
var i := 0
for x in range(Global.grid_isometric_cell_size, size.x + 2, Global.grid_isometric_cell_size * 2):
for y in range(0, size.y + 1, Global.grid_isometric_cell_size):
draw_isometric_tile(i, Vector2(x, y))
i += 1
_draw_isometric_grid(rect)
func draw_isometric_tile(i : int, origin := Vector2.RIGHT, cell_size : int = Global.grid_isometric_cell_size) -> void:
# A random value I found by trial and error, I have no idea why it "works"
var diff = 1.11754
var approx_30_degrees = deg2rad(26.565)
func _draw_isometric_grid(target_rect : Rect2) -> void:
# Using Array instead of PoolVector2Array to avoid kinda
# random "resize: Can't resize PoolVector if locked" errors.
# See: https://github.com/Orama-Interactive/Pixelorama/issues/331
# It will be converted to PoolVector2Array before being sent to be rendered.
var grid_multiline_points := []
var pool := PoolVector2Array()
if i < isometric_polylines.size():
pool = isometric_polylines[i]
else:
var a = origin - Vector2(0, 0.5)
var b = a + Vector2(cos(approx_30_degrees), sin(approx_30_degrees)) * cell_size * diff
var c = a + Vector2.DOWN * cell_size
var d = c - Vector2(cos(approx_30_degrees), sin(approx_30_degrees)) * cell_size * diff
pool.append(a)
pool.append(b)
pool.append(c)
pool.append(d)
pool.append(a)
isometric_polylines.append(pool)
var cell_size := Vector2(Global.grid_isometric_cell_bounds_width, Global.grid_isometric_cell_bounds_height)
var max_cell_count : Vector2 = target_rect.size / cell_size
if pool.size() > 2:
draw_polyline(pool, Global.grid_color)
var origin := Vector2(Global.grid_isometric_offset_x, Global.grid_isometric_offset_y)
var origin_offset : Vector2 = (origin - target_rect.position).posmodv(cell_size)
# lines ↗↗↗ (from bottom-left to top-right)
var per_cell_offset := cell_size * Vector2(1, -1)
# lines ↗↗↗ starting from the rect's left side (top to bottom):
var y : float = fposmod(origin_offset.y + cell_size.y * (0.5 + origin_offset.x / cell_size.x), cell_size.y)
while y < target_rect.size.y:
var start : Vector2 = target_rect.position + Vector2(0, y)
var cells_to_rect_bounds : float = min(max_cell_count.x, y / cell_size.y)
var end : Vector2 = start + cells_to_rect_bounds * per_cell_offset
grid_multiline_points.push_back(start)
grid_multiline_points.push_back(end)
y += cell_size.y
# lines ↗↗↗ starting from the rect's bottom side (left to right):
var x : float = (y - target_rect.size.y) / cell_size.y * cell_size.x
while x < target_rect.size.x:
var start : Vector2 = target_rect.position + Vector2(x, target_rect.size.y)
var cells_to_rect_bounds : float = min(max_cell_count.y, max_cell_count.x - x / cell_size.x)
var end : Vector2 = start + cells_to_rect_bounds * per_cell_offset
grid_multiline_points.push_back(start)
grid_multiline_points.push_back(end)
x += cell_size.x
# lines ↘↘↘ (from top-left to bottom-right)
per_cell_offset = cell_size
# lines ↘↘↘ starting from the rect's left side (top to bottom):
y = fposmod(origin_offset.y - cell_size.y * (0.5 + origin_offset.x / cell_size.x), cell_size.y)
while y < target_rect.size.y:
var start : Vector2 = target_rect.position + Vector2(0, y)
var cells_to_rect_bounds : float = min(max_cell_count.x, max_cell_count.y - y / cell_size.y)
var end : Vector2 = start + cells_to_rect_bounds * per_cell_offset
grid_multiline_points.push_back(start)
grid_multiline_points.push_back(end)
y += cell_size.y
# lines ↘↘↘ starting from the rect's top side (left to right):
x = fposmod(origin_offset.x - cell_size.x * (0.5 + origin_offset.y / cell_size.y), cell_size.x)
while x < target_rect.size.x:
var start : Vector2 = target_rect.position + Vector2(x, 0)
var cells_to_rect_bounds : float = min(max_cell_count.y, max_cell_count.x - x / cell_size.x)
var end : Vector2 = start + cells_to_rect_bounds * per_cell_offset
grid_multiline_points.push_back(start)
grid_multiline_points.push_back(end)
x += cell_size.x
if not grid_multiline_points.empty():
draw_multiline(grid_multiline_points, Global.grid_color)

View file

@ -9,7 +9,7 @@ func _draw() -> void:
if zoom_percentage < Global.pixel_grid_show_at_zoom:
return
var rect : Rect2 = get_rect_to_draw()
var rect : Rect2 = Global.current_project.get_tile_mode_rect()
if rect.has_no_area():
return
@ -17,18 +17,3 @@ func _draw() -> void:
draw_line(Vector2(x, rect.position.y), Vector2(x, rect.end.y), Global.pixel_grid_color)
for y in range(ceil(rect.position.y), floor(rect.end.y) + 1):
draw_line(Vector2(rect.position.x, y), Vector2(rect.end.x, y), Global.pixel_grid_color)
func get_rect_to_draw() -> Rect2:
var size := Global.current_project.size
var tiling_rect : Rect2
match Global.current_project.tile_mode:
Global.Tile_Mode.NONE:
tiling_rect = Rect2(Vector2.ZERO, size)
Global.Tile_Mode.XAXIS:
tiling_rect = Rect2(Vector2(-1, 0) * size, Vector2(3, 1) * size)
Global.Tile_Mode.YAXIS:
tiling_rect = Rect2(Vector2(0, -1) * size, Vector2(1, 3) * size)
Global.Tile_Mode.BOTH:
tiling_rect = Rect2(Vector2(-1, -1) * size, Vector2(3, 3) * size)
return tiling_rect

View file

@ -288,6 +288,7 @@ func tile_mode_submenu_id_pressed(id : int) -> void:
Global.tile_mode_submenu.set_item_checked(i, i == id)
Global.canvas.tile_mode.update()
Global.canvas.pixel_grid.update()
Global.canvas.grid.update()
func toggle_mirror_view() -> void: