From da95181896f678816d1ad1e9d957698418d2aa2e Mon Sep 17 00:00:00 2001 From: Maxim Averyanov Date: Thu, 27 Aug 2026 18:27:44 +0200 Subject: [PATCH] Version 1.3.0, and a front page that fits on one screen The README was still a wall: 278 lines of paragraphs. Now 178, and the prose that explained itself at length is either one sentence or a link to docs/INTERNALS.md. The Russian version is a full-size button in the top row rather than a small badge among the status ones - the point of splitting the languages was that each is a front page, and a reader should not have to hunt for theirs. 1.3.0 is the release where both windows became native and the installer started saying something. The changelog has the list. --- CHANGELOG.md | 28 ++++ README.md | 271 ++++++++++++--------------------------- README.ru.md | 257 ++++++++++++------------------------- mthread/__init__.py | 2 +- mthread_draw/__init__.py | 2 +- 5 files changed, 195 insertions(+), 365 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e89d4c3..20e24aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,33 @@ # Changelog +## 1.3.0 + +The release where both windows became native and the installer started +saying something. + +- **The Windows window is WinUI 3, and it is the one that gets installed.** + Until now the installer carried the Tk window, so installing MThread Draw on + Windows got the old interface, slowly, and the native one was only in a zip + nobody had a reason to open. The Tk window is gone: `mthread_draw/app.py`, + the `gui` extra and the customtkinter dependency with it. Linux keeps the + command line and the library. +- **A macOS window, in SwiftUI.** Frosted glass over `NSVisualEffectView` with + behind-window blending, the same phone view with drag placement and the same + eraser, driving the same engine over a pipe. `tools/build_macos.py` + assembles the bundle around `swift build` and makes the disk image. +- **The installer has a user interface.** Without one an MSI shows "gathering + required information", installs, and closes without a word, so a successful + install cannot be told apart from a failed one - and it was read as failure. + Now: welcome, where to put it, progress, finished, and a ticked offer to + start the program. Apps & features shows the install folder, and a new + version replaces the old one instead of sitting beside it. +- **Builds happen outside the checkout.** This tree lives in OneDrive on one + machine, and a sync client holds handles on files while it uploads them. + Writing several thousand of them into a synced folder failed at a different + step every time, always with an access denied that named a file rather than + the cause. Everything is staged in temp; only the finished installer and zip + are copied into `dist/`. + ## 1.2.0 The release that made the window an editor rather than a loader. diff --git a/README.md b/README.md index 917fda3..858a37a 100644 --- a/README.md +++ b/README.md @@ -4,73 +4,60 @@ MThread Draw — a motorcycle assembling itself out of touch strokes, one stroke at a time -The drawing above draws itself, and it is not an illustration: those are the 232 strokes
-tools/make_hero.py gets out of examples/motorcycle.jpg, in the order the program sends them to a phone.
+Not an illustration — 232 real strokes from examples/motorcycle.jpg, in the order they are sent to the phone.

-Download for Windows -Download for macOS -Try it in your browser +Download for Windows +Download for macOS +Try it in your browser +Русская версия
CI Latest release -Downloads Licence: AGPL-3.0 Native front ends -По-русски

-### It draws pictures on an Android screen by touching it,
and it records and replays gestures. Nothing is installed on the phone. +### Draws any picture on an Android screen by touching it.
Records and replays gestures. Nothing installed on the phone. -USB or wireless ADB · no root on most devices · no Android SDK · one download, everything inside +USB or wireless ADB · no root on most devices · no Android SDK · Python, OpenCV and adb travel inside the app
-A colour photograph of a guitar being traced into touch strokes and drawn on a phone screen +A photograph of a guitar traced into touch strokes and drawn on a phone screen -A photograph in, 57 strokes and 478 touch points out — the exact path list sent to the device.
-On a Pixel 8 Pro that draws in under two seconds.
+57 strokes, 478 points — the exact path list sent to the device. Under two seconds on a Pixel 8 Pro. ---- +## Start -## In about a minute - -| | | +| | | |---|---| -| **1 · Install** | [**Windows**](https://github.com/MAXAWER/MThread-Draw/releases/latest) — `MThreadDraw-x.y.z-x64.msi`, Start Menu entry and uninstaller included. [**macOS**](https://github.com/MAXAWER/MThread-Draw/releases/latest) — `.dmg` for Apple Silicon or Intel, drag it to Applications. **Linux** — the command line, [from source](#from-source). | -| **2 · Wake the phone** | Settings → About phone → tap **Build number** seven times → Developer options → **USB debugging**. Plug it in, or `adb connect 192.168.1.42:5555`. | -| **3 · Draw** | It finds the phone, shows the screen live, and lays the drawing over exactly where it will land. Drag to move, wheel to resize, Shift and wheel to turn — then **START DRAWING**. | - -**Nothing else to install.** Python, OpenCV and **adb** all travel inside the -application. Both windows are native — **WinUI 3** on Windows, **SwiftUI** on -macOS — and each drives the same engine over a pipe, so neither has its own idea -of how anything works. The builds are not signed: SmartScreen says "unknown -publisher" once, and macOS wants a right-click → **Open** the first time. +| **1** | Install: [Windows `.msi`](https://github.com/MAXAWER/MThread-Draw/releases/latest) · [macOS `.dmg`](https://github.com/MAXAWER/MThread-Draw/releases/latest) · Linux — [from source](#from-source). Unsigned, so Windows says "unknown publisher" once and macOS wants right-click → **Open**. | +| **2** | On the phone: Settings → About phone → **Build number** ×7 → Developer options → **USB debugging**. Plug in, or `adb connect 192.168.1.42:5555`. | +| **3** | Open it. Drag the drawing over the live screen, wheel to resize, Shift+wheel to turn — **START DRAWING**. | ---- +## What it does -## What the window lets you do - -| | | +| | | |---|---| -| **Place it exactly** | Drag the drawing across the live view; wheel resizes, Shift and wheel turns, `Flip` mirrors, `Fit` starts over. Held in fractions of the screen, so it survives the phone being turned. | -| **Layers** | Several pictures arranged against each other, each with its own placement and its own tracer settings. Hide one, reorder them, remove one. | -| **Erase single strokes** | Drag across the lines you do not want with the eraser on; `Undo erase` brings them back. | -| **Re-trace in place** | The detail slider re-traces what is already loaded. No need to open the file again. | -| **Record and replay** | Press record, do something on the phone, press stop. The file holds fractions of the screen, so it **replays on a different phone**, at any speed, any number of times. | -| **No live view?** | If capture fails, a screenshot you copied off the phone by hand will do: it does not update, but its proportions are what placement needs. | - -Nothing to prepare, from the command line or from code: +| **Place it by hand** | Drag, resize, turn, flip, `Fit`. Held in fractions of the screen, so it survives the phone being turned. | +| **Layers** | Several pictures at once, each with its own placement and tracer settings. | +| **Erase strokes** | Drag across the lines you do not want; `Undo erase` brings them back. | +| **Re-trace in place** | The detail slider re-traces what is loaded — no reopening the file. | +| **Record and replay** | The file holds fractions of the screen, so it **replays on a different phone**, at any speed. | +| **Two tracers** | Canny for machines and buildings, flow-based coherent lines for faces and animals. The app asks what is in the picture, not which algorithm. | +| **Native windows** | WinUI 3 on Windows, SwiftUI on macOS, both driving one shared engine. | +| **No live view?** | A screenshot copied off the phone by hand will do — its proportions are what placement needs. | ```bash mthread shape heart # a heart, fitted to the screen -mthread text "hello" --y 0.35 # words, in any font this machine has +mthread text "hello" --y 0.35 # any font this machine has mthread record -o login.json # then: mthread play login.json --speed 2 ``` @@ -79,108 +66,24 @@ from mthread import Device Device().draw_paths([[(100, 200), (400, 200), (400, 600)]]) ``` ---- - -## How a photograph becomes touches -
-Source photograph, the lines the tracer finds, and the resulting stroke paths -
- -A tracer decides where the lines are, the result is thinned to one pixel wide, -and each line is walked into a single stroke — not an outline *around* the line, -which is what draws everything twice. Above is `examples/guitar.jpg`, untouched: -57 strokes, 478 points. +Four photographs and the line drawings traced from them -The app asks what is in the picture rather than which algorithm you would like: - -| What you say is in it | What runs | Why that one | -|---|---|---| -| **Buildings, machines, objects** | Canny edges, thinned, walked into strokes | Keeps every bit of structure an edge detector sees — which is what a machine or a building is made of. | -| **Portraits, animals, nature** | Flow-based coherent lines, after Kang, Lee and Chui | Works out the direction each line runs in and filters along it: calmer, longer strokes, and a face stays a face. | - -Neither wins everywhere, which is why both are here. Colour is what gets lost — -a finger draws one black line, so the output is always a line drawing. - -
-Four photographs and the line drawings traced from them: a guitar, a motorcycle, a cat and a lighthouse - -Nothing prepared, retouched or masked — the files in examples/, resized and otherwise untouched.
-The only thing that differs between the columns is the two sliders every user has.
+The files in examples/, resized and otherwise untouched — nothing prepared, retouched or masked.
---- - -## Why it exists - -`adb shell input tap` spawns a process on the device for every call. At -100–300 ms each, anything continuous — a gesture, a line, a test script — is -unusably slow. `mthread` gets the whole drawing onto the device in one go -instead: kernel events through a single pushed script where the phone allows it, -and a 3 KB injector run through `app_process` where it does not. A stroke that -takes 40 seconds through `input swipe` finishes in well under a second. - -Two pieces, and either works without the other: **`mthread`**, a Python library -for synthetic touch input whose core has no dependencies at all, and **MThread -Draw**, the desktop application on top of it. - -**[How it works, in detail →](docs/INTERNALS.md)** — the digitizer's own -coordinate space, the three ways into a device and why a recent Pixel refuses -the fast one, what "instant" costs, and how it draws like a hand. - ---- - -## What it works with - -| | | -|---|---| -| **Devices** | Anything `adb devices` lists, over USB or wireless ADB. Root is not needed on most devices. | -| **Emulators** | Android Studio AVD, BlueStacks (`:5555`), LDPlayer (`:5555`), Nox (`:62001`), MEmu (`:21503`). Raw `/dev/input` support differs between builds — `mthread info` says which path yours gets, and [device reports](https://github.com/MAXAWER/MThread-Draw/issues/new?template=device_report.md) are welcome. | -| **Images** | PNG, JPEG, BMP, WebP. Raster only for now. | -| **Host** | Windows, macOS, Linux. Python 3.9+. | - -Used for drawing games and canvases, signatures and stamps, QA passes that -replay a recorded flow against every build, and repetitive tapping in apps with -no other automation hook. Whether automating a particular game is allowed is -between you and that game's rules; this is a general-purpose input tool. - - -
-From source, and building the applications yourself - -
- -[`run.bat`](run.bat) on Windows and [`run.sh`](run.sh) elsewhere do the whole -thing: virtual environment, dependencies, and `adb` if the machine has none. -Otherwise: - -```bash -git clone https://github.com/MAXAWER/MThread-Draw.git -cd MThread-Draw - -pip install -e . # library only - no dependencies at all -pip install -e ".[draw]" # + image tracing (OpenCV, NumPy, Pillow) -pip install -e ".[bg]" # + rembg background removal -``` +## Why it is fast -`adb` is found in this order: `ADB_PATH`, the copy inside a packaged build, your -`PATH`, a `platform-tools` directory beside the working directory, then the usual -Android SDK locations. If you have none of those, -`python tools/fetch_platform_tools.py` fetches it — 7 MB, straight from Google. +`adb shell input tap` starts a process on the device per call, 100–300 ms each. +This sends the whole drawing at once instead, and a stroke that takes 40 seconds +through `input swipe` finishes in well under a second. -```bash -pip install pyinstaller -python tools/build_app.py --msi # Windows: engine, WinUI front end, installer -python tools/build_macos.py --dmg # macOS: the bundle and its disk image -``` - -The installer needs WiX: `dotnet tool install --global wix --version 5.0.2`. -[Releasing](docs/RELEASING.md) covers the rest. - -
+**[How it works →](docs/INTERNALS.md)** — the digitizer's own coordinate space, +the three ways into a device and why a recent Pixel refuses the fast one, what +"instant" actually costs, drawing like a hand, and the open ends.
-Command line — every command, and the options they share +Command line
@@ -188,92 +91,88 @@ The installer needs WiX: `dotnet tool install --global wix --version 5.0.2`. mthread devices # what is attached mthread info # screen size and digitizer ranges -mthread shape heart # heart, star, circle, square, polygon, spiral, wave -mthread shape star --points 7 --rotate 20 -mthread text "hello world" # any font the machine has +mthread shape star --points 7 --rotate 20 # heart, star, circle, square, polygon, spiral, wave mthread text "signed" --font arial.ttf --scale 0.5 --y 0.8 - -mthread record -o session.json # record until Enter mthread play session.json --speed 0.5 --repeat 3 ``` -Every drawing command takes the same placement options — `--scale`, `--rotate`, -`--flip-x`, `--flip-y`, `--x`, `--y`, `--margin` — and `--speed`/`--human` for -how it draws. +Every drawing command shares the placement options `--scale`, `--rotate`, +`--flip-x`, `--flip-y`, `--x`, `--y`, `--margin`, and `--speed`/`--human` for how +it draws. Text is rendered with a real font and then traced, which is why any font works -and why letters come out as outlines: a filled glyph is a shape with an inside -and an outside, and this draws with one finger. +and why letters come out as outlines: a filled glyph has an inside and an +outside, and this draws with one finger.
+
-Library — the whole API in ten lines +From source, and building the apps yourself
-```python -from mthread import Device, Recorder, Session, replay +[`run.bat`](run.bat) on Windows and [`run.sh`](run.sh) elsewhere do everything: +environment, dependencies, and `adb` if the machine has none. By hand: -device = Device() -print(device.screen_size, device.touch_device.path) +```bash +git clone https://github.com/MAXAWER/MThread-Draw.git && cd MThread-Draw +pip install -e . # library only - no dependencies at all +pip install -e ".[draw]" # + image tracing (OpenCV, NumPy, Pillow) +``` -recorder = Recorder(device) -recorder.start() -input("Do something on the phone, then press Enter...") -recorder.stop().save("flow.json") +`adb` is looked for in `ADB_PATH`, inside a packaged build, your `PATH`, a +`platform-tools` folder beside the working directory, then the usual SDK +locations — or `python tools/fetch_platform_tools.py` fetches it, 7 MB from +Google. -replay(device, Session.load("flow.json"), speed=2.0, repeat=10) +```bash +pip install pyinstaller +python tools/build_app.py --msi # Windows: engine, WinUI front end, installer +python tools/build_macos.py --dmg # macOS: bundle and disk image ``` +The installer needs WiX: `dotnet tool install --global wix --version 5.0.2`. +Details in [RELEASING.md](docs/RELEASING.md). +
-Known limits, said plainly +Limits, and what to do when touches land in the wrong place
-- **A recording does not know which way up the phone was.** Drawing follows the - orientation; a recording holds fractions of the screen it was made on, so a - portrait recording replayed in landscape lands sideways. -- **Replay carries a fixed overhead.** Starting and stopping the on-device - injector costs a second or two: the strokes and the gaps are faithful, the - total is not. -- **Stop is not instantaneous.** It cancels what has not been queued yet and the - device finishes what it already has — about two seconds' worth. +- **A recording does not know which way up the phone was** — replay in the + orientation you recorded in. Drawing itself does follow the orientation. +- **Replay has a fixed overhead** of a second or two for the on-device injector: + the strokes and gaps are faithful, the total is not. +- **Stop is not instantaneous.** It cancels what is not yet queued; the device + finishes the two seconds it already has. - **Recordings from before 1.2 are not portable** and say so rather than misfiring. -- **`mthread info` is the first thing to check** when touches land in the wrong - place. - -
- ---- +- **Start with `mthread info`.** Then open an issue — templates for + [bugs](https://github.com/MAXAWER/MThread-Draw/issues/new?template=bug_report.md) + and [devices](https://github.com/MAXAWER/MThread-Draw/issues/new?template=device_report.md). + Digitizer ranges differ wildly between panels, and only what people report can + be fixed. -## Help, and helping +Works with any device `adb devices` lists, and with emulators that expose an ADB +port — AVD, BlueStacks, LDPlayer (`:5555`), Nox (`:62001`), MEmu (`:21503`). +PNG, JPEG, BMP, WebP. Python 3.9+. -Something not working? Open an issue — there are templates for -[bugs](https://github.com/MAXAWER/MThread-Draw/issues/new?template=bug_report.md) -and [device reports](https://github.com/MAXAWER/MThread-Draw/issues/new?template=device_report.md). -Paste the output of `mthread info`; digitizer ranges differ wildly between -panels, and only what people report can be handled. - -Contributions welcome — [CONTRIBUTING.md](CONTRIBUTING.md), and -[`good first issue`](https://github.com/MAXAWER/MThread-Draw/labels/good%20first%20issue) -is the easiest way in. [What is worth doing next →](docs/INTERNALS.md#open-ends) + ## Licence -**AGPL-3.0, with a commercial licence available from the author.** Use it, change -it, share it, free of charge — but a version you **distribute**, or **run as a -service** other people use, has to publish its complete source under the AGPL -too, a rebranded copy included. To put it inside a product whose source stays -closed, [ask for a commercial licence](https://github.com/MAXAWER/MThread-Draw/issues/new?title=Licence%20request). - -Binding text: [LICENSE](LICENSE). Plain language, English and Russian: -**[TERMS.md](TERMS.md)**. +**AGPL-3.0, with a commercial licence available from the author.** Free to use, +change and share — but anything you **distribute**, or **run as a service** +others use, must publish its complete source under the AGPL, a rebranded copy +included. For a product whose source stays closed, +[ask for a commercial licence](https://github.com/MAXAWER/MThread-Draw/issues/new?title=Licence%20request). +Binding text: [LICENSE](LICENSE) · plain language: [TERMS.md](TERMS.md) · +contributing: [CONTRIBUTING.md](CONTRIBUTING.md).

-If this saved you an afternoon, a ⭐ costs nothing and is how anyone else finds it. +If this saved you an afternoon, a ⭐ is how anyone else finds it.
diff --git a/README.ru.md b/README.ru.md index 764a7b4..9e0363d 100644 --- a/README.ru.md +++ b/README.ru.md @@ -4,14 +4,14 @@ MThread Draw — мотоцикл, собирающийся из штрихов касаний -Рисунок выше рисует себя сам, и это не иллюстрация: это те самые 232 штриха, которые
-tools/make_hero.py получает из examples/motorcycle.jpg — в том порядке, в котором программа отправляет их на телефон.
+Не иллюстрация — 232 настоящих штриха из examples/motorcycle.jpg, в том порядке, в котором они уходят на телефон.

-Скачать для Windows -Скачать для macOS -Попробовать в браузере +Скачать для Windows +Скачать для macOS +Попробовать в браузере +English version
@@ -19,58 +19,45 @@ Последний релиз Лицензия AGPL-3.0 Нативные интерфейсы -In English

-### Рисует картинки на экране Android, касаясь его,
записывает и повторяет жесты. На телефон ничего не ставится. +### Рисует любую картинку на экране Android, касаясь его.
Записывает и повторяет жесты. На телефон ничего не ставится. -USB или ADB по Wi-Fi · на большинстве устройств без root · без Android SDK · одна загрузка, всё внутри +USB или ADB по Wi-Fi · на большинстве устройств без root · без Android SDK · Python, OpenCV и adb лежат внутри приложения
-Фотография гитары, превращаемая в штрихи и рисуемая на экране телефона +Фотография гитары, превращённая в штрихи и нарисованная на экране телефона -На входе фотография, на выходе 57 штрихов и 478 точек касания — ровно тот список путей, что уходит на устройство.
-На Pixel 8 Pro это рисуется меньше чем за две секунды.
+57 штрихов, 478 точек — ровно тот список путей, что уходит на устройство. На Pixel 8 Pro меньше двух секунд. ---- +## Начать -## Примерно за минуту - -| | | +| | | |---|---| -| **1 · Установить** | [**Windows**](https://github.com/MAXAWER/MThread-Draw/releases/latest) — `MThreadDraw-x.y.z-x64.msi`, с ярлыком в «Пуске» и деинсталлятором. [**macOS**](https://github.com/MAXAWER/MThread-Draw/releases/latest) — `.dmg` для Apple Silicon или Intel, перетащить в Applications. **Linux** — командная строка, [из исходников](#из-исходников). | -| **2 · Разбудить телефон** | Настройки → О телефоне → семь раз по «Номер сборки» → Для разработчиков → **Отладка по USB**. Подключить кабель или `adb connect 192.168.1.42:5555`. | -| **3 · Рисовать** | Телефон находится сам, экран показывается живьём, рисунок ложится поверх того места, куда попадёт. Тащить мышью, колесо — размер, Shift с колесом — поворот, затем **START DRAWING**. | - -**Больше ничего ставить не нужно.** Python, OpenCV и **adb** лежат внутри самого -приложения. Оба окна нативные — **WinUI 3** на Windows и **SwiftUI** на macOS — и -оба разговаривают с одним и тем же движком по каналу, так что у них не может -быть разных представлений о происходящем. Сборки не подписаны: Windows один раз -скажет «неизвестный издатель», а macOS попросит первый запуск через правую -кнопку → **Открыть**. +| **1** | Установить: [Windows `.msi`](https://github.com/MAXAWER/MThread-Draw/releases/latest) · [macOS `.dmg`](https://github.com/MAXAWER/MThread-Draw/releases/latest) · Linux — [из исходников](#из-исходников). Подписи нет, поэтому Windows один раз скажет «неизвестный издатель», а macOS попросит правую кнопку → **Открыть**. | +| **2** | На телефоне: Настройки → О телефоне → **Номер сборки** ×7 → Для разработчиков → **Отладка по USB**. Подключить кабель или `adb connect 192.168.1.42:5555`. | +| **3** | Открыть. Тащить рисунок по живому экрану, колесо — размер, Shift+колесо — поворот, затем **START DRAWING**. | ---- +## Что умеет -## Что можно сделать в окне - -| | | +| | | |---|---| -| **Разместить точно** | Перетаскивание по живому экрану; колесо — размер, Shift с колесом — поворот, `Flip` — отражение, `Fit` — вписать заново. Положение хранится в долях экрана и переживает поворот телефона. | -| **Слои** | Несколько картинок друг относительно друга, у каждой своё положение и свои настройки трассировки. Слой можно спрятать, переставить, удалить. | -| **Ластик по штрихам** | Включить ластик и провести по ненужным линиям; `Undo erase` возвращает их. | -| **Пересчёт на месте** | Ползунок детализации перетрассирует уже загруженное — открывать файл заново не нужно. | -| **Запись и повтор** | Нажать запись, поделать что-нибудь на телефоне, остановить. Файл хранит доли экрана, поэтому **воспроизводится на другом телефоне**, с любой скоростью и сколько угодно раз. | -| **Нет живого экрана?** | Если захват не работает, подойдёт снимок, снятый на телефоне вручную: он не обновляется, но его пропорции — это то, что нужно для размещения. | - -Ничего не готовя — из командной строки или из кода: +| **Размещение мышью** | Тащить, менять размер, поворачивать, отражать, `Fit`. Хранится в долях экрана и переживает поворот телефона. | +| **Слои** | Несколько картинок сразу, у каждой своё положение и свои настройки трассировки. | +| **Ластик по штрихам** | Провести по ненужным линиям; `Undo erase` возвращает их. | +| **Пересчёт на месте** | Ползунок детализации перетрассирует загруженное — заново открывать файл не нужно. | +| **Запись и повтор** | Файл хранит доли экрана, поэтому **воспроизводится на другом телефоне**, с любой скоростью. | +| **Два трассировщика** | Кэнни для техники и зданий, когерентные линии по потоку для лиц и животных. Приложение спрашивает, что на картинке, а не какой алгоритм. | +| **Нативные окна** | WinUI 3 на Windows, SwiftUI на macOS, оба поверх одного движка. | +| **Нет живого экрана?** | Подойдёт снимок, снятый на телефоне вручную — важны его пропорции. | ```bash mthread shape heart # сердце, вписанное в экран -mthread text "привет" --y 0.35 # текст любым шрифтом, что есть в системе +mthread text "привет" --y 0.35 # любым шрифтом, что есть в системе mthread record -o login.json # затем: mthread play login.json --speed 2 ``` @@ -79,69 +66,46 @@ from mthread import Device Device().draw_paths([[(100, 200), (400, 200), (400, 600)]]) ``` ---- - -## Как фотография превращается в касания -
-Исходная фотография, найденные линии и итоговые штрихи -
- -Трассировщик находит линии, результат утончается до одного пикселя, и каждая -линия проходится одним штрихом — не обводится по контуру, иначе всё рисовалось бы -дважды. Выше — `examples/guitar.jpg` без единой правки: 57 штрихов, 478 точек. +Четыре фотографии и штриховые рисунки, полученные из них -Приложение спрашивает не про алгоритм, а про то, что на фотографии: - -| Что на фотографии | Что работает | Почему именно это | -|---|---|---| -| **Здания, техника, объекты** | Границы по Кэнни, утончение, обход в штрихи | Сохраняет всю структуру, которую видит детектор границ, — а машина или башня из неё и состоит. | -| **Портреты, животные, природа** | Когерентные линии по направлению потока | Считает, куда идёт каждая линия, и фильтрует вдоль неё: штрихи длиннее и спокойнее, лицо остаётся лицом, а не зерном плёнки. | - -Ни один не выигрывает везде — поэтому остались оба. Теряется цвет: палец рисует -одну чёрную линию, поэтому результат всегда штриховой. - -
-Четыре фотографии и штриховые рисунки, полученные из них - -Ничего не готовилось и не ретушировалось — это файлы из examples/, только уменьшенные.
-Между колонками отличаются лишь те два ползунка, что есть у любого пользователя.
+Это файлы из examples/, только уменьшенные — ничего не готовилось, не ретушировалось и не вырезалось.
---- +## Почему это быстро -## Зачем это нужно +`adb shell input tap` запускает на устройстве отдельный процесс на каждый вызов — +100–300 мс за штуку. Здесь весь рисунок уходит целиком, и штрих, который через +`input swipe` рисуется 40 секунд, занимает меньше секунды. -`adb shell input tap` запускает отдельный процесс на устройстве при каждом -вызове — 100–300 мс на команду, и для чего-либо непрерывного это неприемлемо -медленно. `mthread` отправляет весь рисунок на устройство целиком: события ядра -одним загруженным сценарием там, где телефон это позволяет, и инжектор на 3 КБ -через `app_process` там, где нет. Штрих, который через `input swipe` рисуется -40 секунд, здесь занимает меньше секунды. +**[Как это устроено →](docs/INTERNALS.md)** (по-английски) — своя система +координат у тачскрина, три пути внутрь устройства и почему свежий Pixel +отказывает в самом быстром, чего стоит «мгновенно», рисование по-человечески и +список открытых задач. -Две части, и любая работает без другой: **`mthread`** — библиотека -синтетического ввода касаний, ядру которой не нужно вообще никаких зависимостей, -и **MThread Draw** — приложение поверх неё. +
+Командная строка -**[Как это устроено, подробно →](docs/INTERNALS.md)** (по-английски) — своя -система координат у тачскрина, три пути внутрь устройства и почему свежий Pixel -отказывает в самом быстром, чего стоит «мгновенно» и как это рисует по-человечески. +
---- +```bash +mthread devices # какие устройства подключены +mthread info # разрешение экрана и диапазоны тачскрина -## С чем работает +mthread shape star --points 7 --rotate 20 # heart, star, circle, square, polygon, spiral, wave +mthread text "подпись" --font arial.ttf --scale 0.5 --y 0.8 +mthread play session.json --speed 0.5 --repeat 3 +``` -| | | -|---|---| -| **Устройства** | Всё, что видно в `adb devices` — по USB или по Wi-Fi. На большинстве устройств root не нужен. | -| **Эмуляторы** | Android Studio AVD, BlueStacks (`:5555`), LDPlayer (`:5555`), Nox (`:62001`), MEmu (`:21503`). Поддержка сырого `/dev/input` отличается от сборки к сборке — `mthread info` покажет за одну строку. | -| **Картинки** | PNG, JPEG, BMP, WebP. Пока только растр. | -| **Хост** | Windows, macOS, Linux. Python 3.9+. | +У всех команд рисования общие параметры размещения `--scale`, `--rotate`, +`--flip-x`, `--flip-y`, `--x`, `--y`, `--margin` и `--speed`/`--human` — как +рисовать. + +Текст рисуется настоящим шрифтом и затем трассируется: поэтому доступен любой +шрифт системы и поэтому буквы выходят контурами — у залитой глифы есть внутренняя +и внешняя граница, а здесь рисует один палец. -Используют для рисовалок и досок на телефоне, подписей и штампов, тестовых -прогонов, где записанный сценарий повторяют на каждой сборке, и однообразных -нажатий там, где других способов автоматизации нет. Допустимо ли автоматизировать -конкретную игру — вопрос её правил; это инструмент ввода общего назначения. +
@@ -150,21 +114,17 @@ Device().draw_paths([[(100, 200), (400, 200), (400, 600)]])
[`run.bat`](run.bat) на Windows и [`run.sh`](run.sh) на macOS и Linux делают всё -сами: окружение, зависимости и `adb`, если своего нет. Иначе вручную: +сами: окружение, зависимости и `adb`, если своего нет. Вручную: ```bash -git clone https://github.com/MAXAWER/MThread-Draw.git -cd MThread-Draw - +git clone https://github.com/MAXAWER/MThread-Draw.git && cd MThread-Draw pip install -e . # только библиотека - зависимостей нет вообще pip install -e ".[draw]" # + трассировка картинок (OpenCV, NumPy, Pillow) -pip install -e ".[bg]" # + удаление фона (rembg) ``` -`adb` ищется по порядку: `ADB_PATH`, копия внутри собранного приложения, ваш -`PATH`, папка `platform-tools` рядом с рабочим каталогом, затем обычные пути -Android SDK. Если ничего из этого нет, `python tools/fetch_platform_tools.py` -скачает его — 7 МБ, прямо от Google. +`adb` ищется в `ADB_PATH`, внутри собранного приложения, в вашем `PATH`, в папке +`platform-tools` рядом с рабочим каталогом, затем в обычных путях SDK — или +`python tools/fetch_platform_tools.py` скачает его, 7 МБ от Google. ```bash pip install pyinstaller @@ -177,98 +137,41 @@ python tools/build_macos.py --dmg # macOS: бандл и образ диска
-Командная строка — все команды и общие параметры - -
- -```bash -mthread devices # какие устройства подключены -mthread info # разрешение экрана и диапазоны тачскрина - -mthread shape heart # heart, star, circle, square, polygon, spiral, wave -mthread shape star --points 7 --rotate 20 -mthread text "привет" # текст любым шрифтом, что есть в системе -mthread text "подпись" --font arial.ttf --scale 0.5 --y 0.8 - -mthread record -o session.json # запись до нажатия Enter -mthread play session.json --speed 2 --repeat 5 -``` - -У всех команд рисования одни и те же параметры размещения — `--scale`, -`--rotate`, `--flip-x`, `--flip-y`, `--x`, `--y`, `--margin` — и -`--speed`/`--human`, отвечающие за то, как рисовать. - -Текст рисуется настоящим шрифтом и затем трассируется — поэтому доступен любой -шрифт системы, и поэтому буквы выходят контурами: залитая глифа это фигура с -внутренней и внешней границей, а здесь рисует один палец. - -
- -
-Библиотека — весь интерфейс в десяти строках +Ограничения и что делать, если касания попадают не туда
-```python -from mthread import Device, Recorder, Session, replay - -device = Device() -print(device.screen_size, device.touch_device.path) - -recorder = Recorder(device) -recorder.start() -input("Сделайте что-нибудь на телефоне и нажмите Enter...") -recorder.stop().save("flow.json") - -replay(device, Session.load("flow.json"), speed=2.0, repeat=10) -``` - -
- -
-Ограничения, честно - -
- -- **Запись не знает, как был повёрнут телефон.** Рисование ориентацию учитывает, - а запись хранит доли того экрана, на котором сделана, поэтому портретная - запись в горизонтальной ориентации ляжет набок. +- **Запись не знает, как был повёрнут телефон** — воспроизводите в той же + ориентации, в которой записывали. Само рисование ориентацию учитывает. - **У воспроизведения есть постоянная накладная стоимость** — секунда-две на - запуск и остановку инжектора. Штрихи и паузы точны, общая длительность нет. -- **Стоп не мгновенный.** Он отменяет то, что ещё не отправлено, а устройство - дорисовывает уже полученное — около двух секунд. + инжектор: штрихи и паузы точны, общая длительность нет. +- **Стоп не мгновенный.** Он отменяет то, что ещё не отправлено; уже полученные + две секунды устройство дорисует. - **Записи старше 1.2 не переносятся** между телефонами и честно об этом сообщают, а не рисуют мимо. -- **Начинайте с `mthread info`**, если касания попадают не туда. - -
- ---- +- **Начните с `mthread info`.** Дальше — issue, есть шаблоны для + [багов](https://github.com/MAXAWER/MThread-Draw/issues/new?template=bug_report.md) + и [устройств](https://github.com/MAXAWER/MThread-Draw/issues/new?template=device_report.md). + Диапазоны координат тачскрина у разных панелей разные, и починить можно только + то, что видно. -## Помощь и участие +Работает с любым устройством из `adb devices` и с эмуляторами, открывающими порт +ADB — AVD, BlueStacks, LDPlayer (`:5555`), Nox (`:62001`), MEmu (`:21503`). +PNG, JPEG, BMP, WebP. Python 3.9+. -Что-то не работает — заведите issue, есть шаблоны для -[багов](https://github.com/MAXAWER/MThread-Draw/issues/new?template=bug_report.md) -и [отчётов об устройстве](https://github.com/MAXAWER/MThread-Draw/issues/new?template=device_report.md). -Приложите вывод `mthread info`: диапазоны координат тачскрина у разных панелей -разные, и починить можно только то, что видно. - -Участие приветствуется — [CONTRIBUTING.md](CONTRIBUTING.md), а метка -[`good first issue`](https://github.com/MAXAWER/MThread-Draw/labels/good%20first%20issue) -самый простой вход. + ## Лицензия -**AGPL-3.0 плюс коммерческая лицензия от автора.** Пользуйтесь, меняйте, -делитесь, бесплатно — но распространяемая версия и сервис на её основе обязаны -опубликовать полный исходный код под AGPL, включая перекрашенную копию. Чтобы -встроить в продукт с закрытым кодом, нужна +**AGPL-3.0 плюс коммерческая лицензия от автора.** Пользоваться, менять и +делиться — бесплатно, но всё, что вы **распространяете** или **держите сервисом** +для других, обязано публиковать полный исходный код под AGPL, включая +перекрашенную копию. Для продукта с закрытым кодом нужна [коммерческая лицензия](https://github.com/MAXAWER/MThread-Draw/issues/new?title=Licence%20request). - -Юридический текст: [LICENSE](LICENSE). Человеческим языком, по-русски и -по-английски: **[TERMS.md](TERMS.md)**. +Юридический текст: [LICENSE](LICENSE) · человеческим языком: +[TERMS.md](TERMS.md) · участие: [CONTRIBUTING.md](CONTRIBUTING.md).

-Если инструмент сэкономил вам вечер — звезда ⭐ ничего не стоит, а найти проект другим людям помогает. +Если инструмент сэкономил вам вечер — звезда ⭐ помогает найти его другим.
diff --git a/mthread/__init__.py b/mthread/__init__.py index 0abe3ad..434819f 100644 --- a/mthread/__init__.py +++ b/mthread/__init__.py @@ -36,7 +36,7 @@ from .session import InputEvent, Session from .touch import TouchDevice, build_stroke_events, parse_getevent_pl, pick_touchscreen -__version__ = "1.2.0" +__version__ = "1.3.0" #: AGPL-3.0, with a commercial licence available from the author for use in #: products that will not publish their source. See TERMS.md. diff --git a/mthread_draw/__init__.py b/mthread_draw/__init__.py index 54b99c7..e31bc88 100644 --- a/mthread_draw/__init__.py +++ b/mthread_draw/__init__.py @@ -3,4 +3,4 @@ Draws images on an Android device and records / replays touch gestures. """ -__version__ = "1.2.0" +__version__ = "1.3.0"