mirror of
https://github.com/1j01/textual-paint.git
synced 2024-12-23 14:51:50 +03:00
WIP
This commit is contained in:
parent
4e1a1fd1ea
commit
f1ea73d811
@ -11,7 +11,7 @@ import argparse
|
|||||||
import asyncio
|
import asyncio
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from random import randint, random
|
from random import randint, random
|
||||||
from typing import Any, Generator, List, Optional, Callable, Iterator, Tuple
|
from typing import Any, Generator, List, NamedTuple, Optional, Callable, Iterator, Tuple
|
||||||
|
|
||||||
from watchdog.events import PatternMatchingEventHandler, FileSystemEvent, EVENT_TYPE_CLOSED, EVENT_TYPE_OPENED
|
from watchdog.events import PatternMatchingEventHandler, FileSystemEvent, EVENT_TYPE_CLOSED, EVENT_TYPE_OPENED
|
||||||
from watchdog.observers import Observer
|
from watchdog.observers import Observer
|
||||||
@ -1240,13 +1240,61 @@ class AnsiArtDocument:
|
|||||||
# This could technically happen if there are two rects of the same size and position.
|
# This could technically happen if there are two rects of the same size and position.
|
||||||
assert len(rects) > 0, "No rect elements after removing rects containing other rects."
|
assert len(rects) > 0, "No rect elements after removing rects containing other rects."
|
||||||
|
|
||||||
# Find the cell size. Rects can span multiple cells, so find the smallest rect,
|
# Find the cell size.
|
||||||
# rather than averaging. That way it's at least a decent chance of it being a single cell.
|
# (This starts with a guess, and then is refined after guessing the document bounds.)
|
||||||
# This starts with a guess, and then is refined after guessing the document bounds.
|
# Strategy 1: average the rect sizes.
|
||||||
|
# cell_width = sum(float(rect.attrib["width"]) for rect in rects) / len(rects)
|
||||||
|
# cell_height = sum(float(rect.attrib["height"]) for rect in rects) / len(rects)
|
||||||
|
# Rects can span multiple cells, so this doesn't turn out well.
|
||||||
|
# Strategy 2: find the smallest rect.
|
||||||
|
# cell_width = min(float(rect.attrib["width"]) for rect in rects)
|
||||||
|
# cell_height = min(float(rect.attrib["height"]) for rect in rects)
|
||||||
|
# That way there's at least a decent chance of it being a single cell.
|
||||||
|
# But this doesn't contend with varying cell sizes of the pathological test case.
|
||||||
|
# Strategy 3: find the smallest rect, then sort rects into rows and columns,
|
||||||
|
# using the smallest rect as a baseline to decide what constitutes a row/column gap,
|
||||||
|
# then use the average spacing between rects in adjacent rows/columns,
|
||||||
|
# also using the smallest rect to avoid averaging in gaps that are more than one cell.
|
||||||
# TODO: handle case that there's no 1x1 cell rect.
|
# TODO: handle case that there's no 1x1 cell rect.
|
||||||
# TODO: use spacing rather than size. Partially done below.
|
min_width = min(float(rect.attrib["width"]) for rect in rects)
|
||||||
cell_width = min(float(rect.attrib["width"]) for rect in rects)
|
min_height = min(float(rect.attrib["height"]) for rect in rects)
|
||||||
cell_height = min(float(rect.attrib["height"]) for rect in rects)
|
# Start with each rect in its own track, then join tracks that are close enough.
|
||||||
|
measures = {}
|
||||||
|
Track = NamedTuple("Track", [("rects", list[ET.Element]), ("min_center", float), ("max_center", float)])
|
||||||
|
def join_tracks(track1: Track, track2: Track) -> Track:
|
||||||
|
return Track(track1.rects + track2.rects, min(track1.min_center, track2.min_center), max(track1.max_center, track2.max_center))
|
||||||
|
def rect_center(rect: ET.Element, coord_attrib: str) -> float:
|
||||||
|
return float(rect.attrib[coord_attrib]) + float(rect.attrib["width" if coord_attrib == "x" else "height"]) / 2
|
||||||
|
for (coord_attrib, min_rect_size) in [("x", min_width), ("y", min_height)]:
|
||||||
|
tracks = [Track([rect], rect_center(rect, coord_attrib), rect_center(rect, coord_attrib)) for rect in rects]
|
||||||
|
joined = True
|
||||||
|
while joined:
|
||||||
|
joined = False
|
||||||
|
for i in range(len(tracks)):
|
||||||
|
for j in range(i + 1, len(tracks)):
|
||||||
|
if tracks[i].max_center + min_rect_size >= tracks[j].min_center and tracks[i].min_center - min_rect_size <= tracks[j].max_center:
|
||||||
|
tracks[i] = join_tracks(tracks[i], tracks[j])
|
||||||
|
del tracks[j]
|
||||||
|
joined = True
|
||||||
|
break
|
||||||
|
if joined:
|
||||||
|
break
|
||||||
|
# Sort tracks
|
||||||
|
tracks.sort(key=lambda track: track.min_center)
|
||||||
|
# Find the average spacing between tracks, for spacings smaller than twice the smallest rect.
|
||||||
|
# I'm calling this gap because I'm lazy.
|
||||||
|
max_gap = min_rect_size * 2
|
||||||
|
gaps = [tracks[i + 1].min_center - tracks[i].max_center for i in range(len(tracks) - 1)]
|
||||||
|
gaps = [gap for gap in gaps if gap <= max_gap]
|
||||||
|
if len(gaps) == 0:
|
||||||
|
# No gaps smaller than twice the smallest rect.
|
||||||
|
# Use the smallest gap.
|
||||||
|
gaps = [min(gaps)]
|
||||||
|
measures[coord_attrib] = sum(gaps) / len(gaps)
|
||||||
|
|
||||||
|
cell_width = measures["x"]
|
||||||
|
cell_height = measures["y"]
|
||||||
|
|
||||||
print("Initial cell size guess: " + str(cell_width) + " x " + str(cell_height))
|
print("Initial cell size guess: " + str(cell_width) + " x " + str(cell_height))
|
||||||
# Find the document bounds.
|
# Find the document bounds.
|
||||||
min_x = min(float(rect.attrib["x"]) for rect in rects)
|
min_x = min(float(rect.attrib["x"]) for rect in rects)
|
||||||
|
Loading…
Reference in New Issue
Block a user