diff --git a/Tools/iconsmooth.py b/Tools/iconsmooth.py index 6ac74abe33..879b7c183c 100644 --- a/Tools/iconsmooth.py +++ b/Tools/iconsmooth.py @@ -22,53 +22,42 @@ import PIL import PIL.Image +import sys +import iconsmooth_lib + +if len(sys.argv) != 5: + raise Exception("iconsmooth.py <" + iconsmooth_lib.all_conv + "> ") # Input detail configuration -input_name = "rplasma_window.dmi" -input_row = 7 -tile_w = 32 -tile_h = 32 -shodan = False # Citadel Station +input_name = sys.argv[1] +metric_mode = sys.argv[2] +conversion_mode = sys.argv[3] +out_prefix = sys.argv[4] -# Output state configuration -if not shodan: - # TG - out_states = [ - # Each output state gives a source quadrant for BR, TL, TR, BL. - # The idea is that each of the 4 directions is a different rotation of the same state. - # These states are associated by a bitfield indicating occupance relative to the indicated corner: - # 1: Tile anti-clockwise of indicated diagonal occupied. - # 2: Tile in indicated diagonal occupied. - # 4: Tile clockwise of indicated diagonal occupied. - [ 0, 0, 0, 0], # 0 : Standing / Outer corners - [ 12, 12, 3, 3], # 1 : Straight line ; top half horizontal bottom half vertical - [ 0, 0, 0, 0], # 2 : Standing / Outer corners diagonal - [ 12, 12, 3, 3], # 3 : Seems to match 1 - [ 3, 3, 12, 12], # 4 : Straight line ; top half vertical bottom half horizontal - [ 15, 15, 15, 15], # 5 : Inner corners - [ 3, 3, 12, 12], # 6 : Seems to match 4 - [ 46, 46, 46, 46], # 7 : Full - ] -else: - # Citadel Station - out_states = [ - [ 3, 0, 1, 2], - [ 11, 8, 5, 6], - [ 3, 0, 1, 2], - [ 11, 8, 5, 6], - [ 7, 4, 9, 10], - [ 15, 12, 13, 14], - [ 7, 4, 9, 10], - [ 19, 16, 17, 18], - ] - -# Infer +# Metric configuration +tile_w = int(metric_mode) +tile_h = int(metric_mode) subtile_w = tile_w // 2 subtile_h = tile_h // 2 +# Infer remainder from subtile +# This is for uneven geometries +# +# SUB | +# ----+---- +# | REM +# +remtile_w = tile_w - subtile_w +remtile_h = tile_h - subtile_h + +# Output state configuration +out_states = iconsmooth_lib.conversion_modes[conversion_mode].states + # Source loading src_img = PIL.Image.open(input_name) +input_row = src_img.size[0] // tile_w + tiles = [] # 48 is the amount of tiles that usually exist for i in range(48): @@ -78,34 +67,30 @@ for i in range(48): tile.paste(src_img, (tx * -tile_w, ty * -tile_h)) # now split that up # note that THIS is where the weird ordering gets put into place - tile_a = PIL.Image.new("RGBA", (subtile_w, subtile_h)) + tile_a = PIL.Image.new("RGBA", (remtile_w, remtile_w)) tile_a.paste(tile, (-subtile_w, -subtile_h)) tile_b = PIL.Image.new("RGBA", (subtile_w, subtile_h)) tile_b.paste(tile, (0, 0)) - tile_c = PIL.Image.new("RGBA", (subtile_w, subtile_h)) + tile_c = PIL.Image.new("RGBA", (remtile_w, subtile_h)) tile_c.paste(tile, (-subtile_w, 0)) - tile_d = PIL.Image.new("RGBA", (subtile_w, subtile_h)) + tile_d = PIL.Image.new("RGBA", (subtile_w, remtile_w)) tile_d.paste(tile, (0, -subtile_h)) tiles.append([tile_a, tile_b, tile_c, tile_d]) state_size = (tile_w * 2, tile_h * 2) -def subtile_copy(dst, dst_x, dst_y, src, src_x, src_y): - dst_x += src_x - dst_y += src_y - for state in range(len(out_states)): full = PIL.Image.new("RGBA", state_size) full.paste(tiles[out_states[state][0]][0], (subtile_w, subtile_h)) full.paste(tiles[out_states[state][1]][1], (tile_w, 0)) full.paste(tiles[out_states[state][2]][2], (subtile_w, tile_h)) full.paste(tiles[out_states[state][3]][3], (tile_w, tile_h + subtile_h)) - full.save("state_" + str(state) + ".png") + full.save(out_prefix + str(state) + ".png") full_finale = PIL.Image.new("RGBA", (tile_w, tile_h)) full_finale.paste(tiles[out_states[0][0]][0], (subtile_w, subtile_h)) full_finale.paste(tiles[out_states[0][1]][1], (0, 0)) full_finale.paste(tiles[out_states[0][2]][2], (subtile_w, 0)) full_finale.paste(tiles[out_states[0][3]][3], (0, subtile_h)) -full_finale.save("full.png") +full_finale.save(out_prefix + "full.png") diff --git a/Tools/iconsmooth_inv.py b/Tools/iconsmooth_inv.py new file mode 100644 index 0000000000..5a40d01b66 --- /dev/null +++ b/Tools/iconsmooth_inv.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2022 Space Wizards Federation +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import PIL +import PIL.Image +import sys +import iconsmooth_lib + +if len(sys.argv) != 4: + raise Exception("iconsmooth_rt2vxap.py INPREFIX (i.e. Resources/Textures/Structures/catwalk.rsi/catwalk_) <" + iconsmooth_lib.all_conv + "> out.png") + +# Input detail configuration +input_prefix = sys.argv[1] +conversion_mode = iconsmooth_lib.conversion_modes[sys.argv[2]] +output_name = sys.argv[3] + +# Source loading +tiles = [] + +for j in range(8): + src_img = PIL.Image.open(input_prefix + str(j) + ".png") + # Infer + tile_w = src_img.size[0] // 2 + tile_h = src_img.size[1] // 2 + subtile_w = tile_w // 2 + subtile_h = tile_h // 2 + remtile_w = tile_w - subtile_w + remtile_h = tile_h - subtile_h + + tile_a = PIL.Image.new("RGBA", (remtile_w, remtile_h)) + tile_a.paste(src_img, (-subtile_w, -subtile_h)) + tile_b = PIL.Image.new("RGBA", (subtile_w, subtile_h)) + tile_b.paste(src_img, (-tile_w, 0)) + tile_c = PIL.Image.new("RGBA", (remtile_w, subtile_h)) + tile_c.paste(src_img, (-subtile_w, -tile_h)) + tile_d = PIL.Image.new("RGBA", (subtile_w, remtile_h)) + tile_d.paste(src_img, (-tile_w, -(tile_h + subtile_h))) + tiles.append([tile_a, tile_b, tile_c, tile_d]) + +# Prepare finale +output_tw = conversion_mode.tw +output_th = conversion_mode.th + +full_finale = PIL.Image.new("RGBA", (tile_w * output_tw, tile_h * output_th)) + +# State table to be inverted +out_states = conversion_mode.states + +# Directions to subtile offsets +subtile_ofx = [1, 0, 1, 0] +subtile_ofy = [1, 0, 0, 1] + +for i in [7, 6, 5, 4, 3, 2, 1, 0]: + for j in range(4): + target_tile = out_states[i][j] + if target_tile != -1: + target_stx = (target_tile % output_tw) * tile_w + target_sty = (target_tile // output_tw) * tile_h + target_stx += subtile_ofx[j] * subtile_w + target_sty += subtile_ofy[j] * subtile_h + full_finale.paste(tiles[i][j], (target_stx, target_sty)) + +# Done! +full_finale.save(output_name) + diff --git a/Tools/iconsmooth_lib.py b/Tools/iconsmooth_lib.py new file mode 100644 index 0000000000..3e60154476 --- /dev/null +++ b/Tools/iconsmooth_lib.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2022 Space Wizards Federation +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +class ConversionMode: + def __init__(self, tw, th, states): + self.tw = tw + self.th = th + self.states = states + +conversion_modes = { + # TG + "tg": ConversionMode( + 7, 7, + [ + # Each output state gives a source quadrant for BR, TL, TR, BL. + # The idea is that each of the 4 directions is a different rotation of the same state. + # These states are associated by a bitfield indicating occupance relative to the indicated corner: + # 1: Tile anti-clockwise of indicated diagonal occupied. + # 2: Tile in indicated diagonal occupied. + # 4: Tile clockwise of indicated diagonal occupied. + + # BR, TL, TR, BL + [ 0, 0, 0, 0], # 0 : Standing / Outer corners + [ 12, 12, 3, 3], # 1 : Straight line ; top half horizontal bottom half vertical + [ 0, 0, 0, 0], # 2 : Standing / Outer corners diagonal + [ 12, 12, 3, 3], # 3 : Seems to match 1 + [ 3, 3, 12, 12], # 4 : Straight line ; top half vertical bottom half horizontal + [ 15, 15, 15, 15], # 5 : Inner corners + [ 3, 3, 12, 12], # 6 : Seems to match 4 + [ 46, 46, 46, 46], # 7 : Full + ] + ), + # Citadel Station + "citadel": ConversionMode( + 7, 3, + [ + # BR, TL, TR, BL + [ 3, 0, 1, 2], + [ 11, 8, 5, 6], + [ 3, 0, 1, 2], + [ 11, 8, 5, 6], + [ 7, 4, 9, 10], + [ 15, 12, 13, 14], + [ 7, 4, 9, 10], + [ 19, 16, 17, 18], + ] + ), + # VXA + "vxa": ConversionMode( + 2, 3, + [ + # 01 # 1: Tile anti-clockwise of indicated diagonal occupied. + # 23 # 2: Tile in indicated diagonal occupied. + # 45 # 4: Tile clockwise of indicated diagonal occupied. + # BR, TL, TR, BL + [ 5, 2, 3, 4], # 0 X (ST) + [ 4, 3, 5, 2], # 1 + [ 5, 2, 3, 4], # 2 X (ST) + [ 4, 3, 5, 2], # 3 + [ 3, 4, 2, 5], # 4 + [ 1, 1, 1, 1], # 5 X (IC) + [ 3, 4, 2, 5], # 6 + [ 2, 5, 4, 3], # 7 X (F) + ] + ), + # VXA+ - custom extensions to the VXA AT field format to make it map 1:1 with RT subtiles + "vxap": ConversionMode( + 2, 4, + [ + # BR, TL, TR, BL + [ 5, 2, 3, 4], # 0 X (ST) + [ 4, 3, 5, 2], # 1 + [ 0, 0, 0, 0], # 2 - diagdup of 0 + [ 6, 6, 7, 7], # 3 - diagdup of 1 + [ 3, 4, 2, 5], # 4 + [ 1, 1, 1, 1], # 5 X (IC) + [ 7, 7, 6, 6], # 6 - diagdup of 4 + [ 2, 5, 4, 3], # 7 X (F) + ] + ), + # rt_states - debugging! + "rt_states": ConversionMode( + 8, 1, + [ + [ 0, 0, 0, 0], + [ 1, 1, 1, 1], + [ 2, 2, 2, 2], + [ 3, 3, 3, 3], + [ 4, 4, 4, 4], + [ 5, 5, 5, 5], + [ 6, 6, 6, 6], + [ 7, 7, 7, 7], + ] + ), +} + +all_conv = "tg/citadel/vxa/vxap/rt_states" +