Я работаю над программой (на Python), которая предполагает разрезание квадрата на более мелкие части. Пользователь должен ввести «код», который программа автоматически преобразует в координаты каждого отдельного прямоугольника. Каждый прямоугольник также имеет связанное с ним значение.
На данный момент я придумал следующий код:
1 # the entire square
0.5;0.5 # square split in half from top to bottom
1:0.5,0.5 # square split in half from side to side
0.5;0.5:0.15,0.35,0.5 # square split in half from top to bottom, with the right side being further subdivided
;
обрабатывает подразделения слева направо, а :
+,
обрабатывает подразделения сверху вниз.
Проблема, с которой я столкнулся, заключается в том, что с помощью этого текущего метода невозможно представить что-то относительно простое:
Единственное, что я могу придумать для решения этой проблемы, — это какой-то рекурсивный алгоритм, который будет обрабатывать какой-то код и генерировать координаты при появлении запроса. Приоритетом является то, чтобы это оставалось максимально удобным для пользователя, поскольку эти коды можно вводить много раз, а запутанный/длинный код сводит на нет его цель (можно просто ввести вложенный dict
/list
). Я до сих пор не могу понять, как такое вообще может работать...
Другим решением было бы использовать какой-нибудь tkinter
графический интерфейс. Я использую его для своего проекта и в качестве входных данных для этого кода, поэтому какое-то интерактивное окно (например, те сайты, где в середине есть ползунок, показывающий, что было до и после?) тоже подойдет.
Эта часть программы занимается разделением японского кандзи (漢字) на составляющие радикалы/части. Большую часть кандзи уже можно представить с помощью моего решения, но есть множество других, которые не могут! Символы отображаются на объекте tkinter Canvas
, за каждым радикалом нарисованы прямоугольники. Пользователь выбирает мышкой некоторое количество этих радикалов (эту часть я уже сделал).
Как я уже упоминал, каждому прямоугольнику присвоено значение, которое имеет значение по умолчанию. Это означало, что в моей попытке перед преобразованием в координаты я использовал вложенный список для хранения значений:
[[0.5,[[1,1]]],[0.5,[[0.15,1],[0.35,1],[0.5,1]]]] # generated from the fourth example
^ assigmed value ^ ^ ^
Координаты были рассчитаны на основе длины стороны объекта Canvas
.
Хотя это и не является строго необходимым, но способ объединить эти прямоугольники в коде/с виджетом tkinter был бы чрезвычайно полезен, поскольку некоторые радикалы могут иметь странные формы (например, L) или даже обтекать все (например, 口).
Да, разрезаем прямоугольник на более мелкие. Если реализация с использованием текста слишком сложна, вместо нее можно использовать виджет tkinter
, но вопрос заключался в разделении прямоугольника.
Это довольно сложно объяснить, поэтому поиграйте с примерами или дайте мне больше фигур для кодирования. Также вы можете сохранить/загрузить этот метод кодирования, используя встроенную библиотеку json
Python.
import tkinter as tk
def cut(direction, cuts, left, right, top, bottom):
if direction == "v":
start, end = left, right
elif direction == "h":
start, end = top, bottom
else:
raise ValueError("Direction must be vertical/horizontal ('v' or 's')")
total = 0
last_loc = 0
for fraction, sub_cuts in cuts:
total += fraction
loc = start + (end-start)*total
# Calculate the (x1,y1), (x2,y2) of the line that will be displayed
if direction == "v":
x1 = x2 = loc
y1, y2 = top, bottom
cut("h", sub_cuts, last_loc, loc, top, bottom) # recursively cut
elif direction == "h":
y1 = y2 = loc
x1, x2 = left, right
cut("v", sub_cuts, left, right, last_loc, loc) # recursively cut
canvas.create_line(x1, y1, x2, y2) # create the line
last_loc = loc
WIDTH = 400
HEIGHT = 400
root = tk.Tk()
canvas = tk.Canvas(root, width=WIDTH, height=HEIGHT)
canvas.pack()
# No cuts
cuts = []
# Cut it vertically 50%
cuts = [(0.5,[])]
# Cut it 50% vertically and for the left small rectangle cut it horizontally 50%
cuts = [(0.5, [(0.5,[])])]
# Cut it 50% vertically and leave the left part.
# The right part gets cut twice more at 15% and at (15+35)%
cuts = [(0.5,[]), (0.5, [(0.15,[]), (0.35,[])])]
# Cut it vertically right at the end (does nothing - except now we can cut horizontally)
# Then cut it hotizontally at 50%. The top part gets cut vertically at 50%
cuts = [(1, [(0.5, [(0.5, [])])])]
# Cut 50% vertically and do nothing right the left part.
# The right part (other 50% of the total area) gets cut horizontally at 50%
# And the top part of that gets cuts 50% vertically again
cuts = [(0.5,[]), (0.5,[(0.5,[(0.5,[])])])]
cut("v", cuts, 0, WIDTH, 0, HEIGHT)
Сначала я заметил, что вы делаете (несколько) вертикальных разрезов, а затем (несколько) горизонтальных разрезов. Вы чередуете вертикальные и горизонтальные разрезы. Таким образом, для каждого разреза в моем списке хранятся дроби, а также список дальнейших разрезов в противоположном направлении.
Поэтому, если вы хотите разрезать его на 3 части (по вертикали), вы должны использовать [(1/3,[]), (1/3,[])]
(сделав 2 разреза по 1/3 каждый и используя пустой список, чтобы обозначить отсутствие дальнейших разрезов).
Чтобы разрезать на 3 части (по горизонтали), используйте то же самое, но заверните внутрь [(1, <list from previous example>)]
. Это делает разрез в крайнем правом углу (по сути, ничего не делая), а затем разрезает его по горизонтали.
Как я уже сказал, это сложно объяснить, но не стесняйтесь приводить мне больше примеров, которые я могу преобразовать, чтобы вы могли их получить.
Обновлено: возникает небольшая проблема, когда вы разрезаете в самом конце (вертикально или горизонтально), линия все равно отображается. Это потому, что в физической сфере вы не можете делать разрезы прямо на краю чего-либо (в отличие от математической сферы). Чтобы это исправить, вы можете закодировать эти дроби, используя None
(представляющий последний подпрямоугольник), а затем просто пропустить canvas.create_line
после замены переменной franction
на 1-total
.
Редактировать 2: Дополнительные примеры:
Разрезаем на сетку из 9:
# Cut vertically into 3rds and then in sub_cuts cut horizontally into 3rds again
sub_cuts = [(1/3,[]), (1/3,[])]
cuts = [(1/3,sub_cuts), (1/3,sub_cuts), (1/3,sub_cuts)]
Разрезаем на сетку 2х3:
# Cut vertically into 3rds and then in sub_cuts cut horizontally into 2 halfs
sub_cuts = [(1/2,[])]
cuts = [(1/3,sub_cuts), (1/3,sub_cuts), (1/3,sub_cuts)]
Разрезаем на сетку 3x2:
# Cut vertically into halfs and then in sub_cuts cut horizontally into 3rds
sub_cuts = [(1/3,[]), (1/3,[])]
cuts = [(1/2,sub_cuts), (1/2,sub_cuts)]
Более сложный пример:
third_sub_cuts = [(1/3,[])]*2
sixth_sub_cuts = [(1/6,[])]*5
cuts = [(0.25,sixth_sub_cuts), (0.5,third_sub_cuts), (0.25,sixth_sub_cuts)]
Привет @TheLizzard, спасибо за твою помощь. Алгоритм очень хорошо работает для создания разрезов, однако я до сих пор не уверен, как пользователь мог ввести этот код в обычное текстовое поле. Не могли бы вы предложить «код», который мог бы ввести пользователь?
@Leo Можете ли вы использовать tkinter.Entry
вызов встроенной функции eval
для его содержимого, чтобы получить сокращения? Если вам нужен графический редактор для сокращений, это потребует много кода и будет полноценным проектом. На этом этапе я предлагаю изменить ваши требования так, чтобы принимались все непересекающиеся прямоугольники вместо используемого вами метода разрезов.
Просто чтобы уточнить: ищете ли вы способ представить различные способы разрезания прямоугольника на более мелкие прямоугольники? Или вы спрашиваете, как представить метод с помощью
tkinter
? Кстати, о StackOverflow, избегайте задавать несколько вопросов в одном сообщении. Я бы посоветовал сделать 2 отдельных поста с двумя разными вопросами.