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

@ -807,10 +807,28 @@ msgstr ""
msgid "Sets how far apart are horizontal lines of the grid"
msgstr ""
msgid "Isometric cell size:"
msgid "Isometric cell bounds width:"
msgstr ""
msgid "Sets the size of the cells in an isometric grid"
msgid "Sets the width of the isometric cell's axis aligned bounding box"
msgstr ""
msgid "Isometric cell bounds height:"
msgstr ""
msgid "Sets the height of the isometric cell's axis aligned bounding box"
msgstr ""
msgid "Isometric grid offset x:"
msgstr ""
msgid "Sets isometric grid's x offset from the canvas origin (top left corner of the image)"
msgstr ""
msgid "Isometric grid offset y:"
msgstr ""
msgid "Sets isometric grid's y offset from the canvas origin (top left corner of the image)"
msgstr ""
msgid "Grid color:"

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: