Spaces:
Sleeping
Sleeping
| title: B&W Flowchart Sketcher | |
| emoji: ๐ | |
| colorFrom: gray | |
| colorTo: gray | |
| sdk: streamlit | |
| sdk_version: 1.35.0 | |
| app_file: app.py | |
| pinned: false | |
| license: mit | |
| # B&W Flowchart Sketcher (Web & Desktop) | |
| A premium, interactive utility to sketch clean, high-contrast, black-and-white flowcharts. This repository bundles two interfaces: | |
| 1. **Interactive Tkinter Desktop App** (`flowchart_app.py`): Supports canvas drag-and-drop node adjustments, visual drafting gridlines, and project serialization. | |
| 2. **Streamlit Web Application** (`app.py`): Designed for cloud deployment (Hugging Face Spaces) with interactive slider coordinates for node tuning. | |
| --- | |
| ## ๐ Table of Contents | |
| - [Architecture & Schema Design](#-architecture--schema-design) | |
| - [Mathematical Engine (Shapes & Ray Intersection)](#-mathematical-engine-shapes--ray-intersection) | |
| - [Hierarchical Auto-Layout Solver](#-hierarchical-auto-layout-solver) | |
| - [Consistent Text-Wrapping Engine](#-consistent-text-wrapping-engine) | |
| - [Pillow Cropping & Vector-Like Rendering](#-pillow-cropping--vector-like-rendering) | |
| - [Getting Started](#-getting-started) | |
| - [Deployment on Hugging Face](#-deployment-on-hugging-face) | |
| --- | |
| ## ๐๏ธ Architecture & Schema Design | |
| Both the desktop and web clients rely on a shared data schema managed by a backend model: | |
| ``` | |
| +------------------+ +---------------------+ | |
| | Desktop GUI | | Web GUI | | |
| | (flowchart_app) | | (app.py) | | |
| +--------+---------+ +----------+----------+ | |
| | | | |
| +-------------+--------------+ | |
| | | |
| v | |
| +-----------+-----------+ | |
| | FlowchartModel | | |
| | - nodes {} | | |
| | - edges [] | | |
| +-----------+-----------+ | |
| | | |
| +-------------+-------------+ | |
| | | | |
| v v | |
| +--------+--------+ +--------+--------+ | |
| | Pillow Export | | JSON Project | | |
| | (Vector-like) | | Save/Load | | |
| +-----------------+ +-----------------+ | |
| ``` | |
| - **`Node`**: Houses coordinates `(x, y)`, dimension bounding boxes `(w, h)` matched to the shape, designation (short ID), shape name, and description. | |
| - **`Edge`**: Houses connectivity mappings `(u -> v)` and styling properties (`style` = straight/curved/dotted). | |
| - **`FlowchartModel`**: Provides path parsing (e.g. `A -> B -> C`), topological sorting layout, and JSON imports/exports. | |
| --- | |
| ## ๐ Mathematical Engine (Shapes & Ray Intersection) | |
| To produce professional-grade diagrams, connector lines and arrowheads must terminate **precisely** at the boundary of a node shape, rather than drawing to its coordinate center (which causes overlaps). | |
| The mathematical engine calculates boundary intersections for every shape dynamically: | |
| ### 1. Ellipses (Circles & Ovals) | |
| An oval centered at $(x_c, y_c)$ with semi-axes $a$ (horizontal radius) and $b$ (vertical radius) satisfies: | |
| $$\frac{(x - x_c)^2}{a^2} + \frac{(y - y_c)^2}{b^2} = 1$$ | |
| To find where a ray in direction $(dx, dy)$ intersects the boundary, we solve for $t$: | |
| $$t = \frac{1}{\sqrt{\frac{dx^2}{a^2} + \frac{dy^2}{b^2}}}$$ | |
| The boundary intersection point is $(x_c + t \cdot dx, y_c + t \cdot dy)$. | |
| ### 2. Polygons (Squares, Diamonds, Inverted Triangles) | |
| For shapes represented by vertices, we cast a ray from the center $(x_c, y_c)$ to $(x_c + 10000 \cdot dx, y_c + 10000 \cdot dy)$ to ensure it crosses the boundary. We perform 2D segment-segment intersection against all edges of the polygon. | |
| Given ray segment $AB$ and polygon edge $CD$: | |
| $$P_{int} = A + t(B - A) = C + u(D - C)$$ | |
| Solving this linear system yields parameters $t$ and $u$. An intersection is valid if $0 \le t \le 1$ and $0 \le u \le 1$. | |
| ### 3. Curved Connector Splines | |
| Curved connectors are drawn using **quadratic Bezier curves** parameterized by $t \in [0, 1]$: | |
| $$B(t) = (1-t)^2 P_{start} + 2(1-t)t P_{ctrl} + t^2 P_{end}$$ | |
| - The control point $P_{ctrl}$ is computed by taking the midpoint of the node centers and offsetting it perpendicularly by 45 pixels. | |
| - The boundary points $P_{start}$ and $P_{end}$ are computed using the ray-casting intersection formulas directed at $P_{ctrl}$. | |
| - The arrowhead at $P_{end}$ is drawn aligned to the tangent vector $P_{end} - P_{ctrl}$, which represents the instantaneous direction of the curve at $t=1$. | |
| --- | |
| ## ๐งฎ Hierarchical Auto-Layout Solver | |
| The layout solver uses a **Layered Hierarchical Graph Layout** algorithm: | |
| 1. **In-Degree Calculation**: Build an adjacency list and count incoming edges for every node. | |
| 2. **Topological Leveling (BFS)**: Node levels are assigned. Nodes with `in-degree = 0` start at Level 0. Outgoing neighbors are pushed to Level $L + 1$. Loops or cycles are broken by selecting a starter node when in-degrees are cyclic. | |
| 3. **Coordinate Allocation**: | |
| - Levels are mapped to vertical coordinates ($Y$-coordinate) spaced 140px apart. | |
| - Nodes within the same level are sorted alphabetically (to ensure layout stability) and distributed symmetrically along the horizontal center ($X$-coordinate) with 150px spacing. | |
| --- | |
| ## โ๏ธ Consistent Text-Wrapping Engine | |
| Because Tkinter Canvas and Pillow use different text-rendering engines, we implemented a custom, unified word-wrapper: | |
| - Font metrics are queried dynamically using `font.measure` in Tkinter and `draw.textlength` in Pillow. | |
| - The wrapper walks through the description, building text lines word-by-word. It splits to a new line as soon as the measured width exceeds the shape's specific maximum text boundaries. | |
| - **Maximum margins** are customized for each shape to prevent text from overflowing corners (e.g. diamonds restrict width to $58\%$ of node width, whereas squares allow up to $82\%$). | |
| --- | |
| ## ๐จ Pillow Cropping & Vector-Like Rendering | |
| When exporting a PNG: | |
| 1. The app calculates the bounding box of the entire flowchart: | |
| $$x_{min} = \min(x_i - w_i/2),\quad x_{max} = \max(x_i + w_i/2)$$ | |
| $$y_{min} = \min(y_i - h_i/2),\quad y_{max} = \max(y_i + h_i/2)$$ | |
| 2. It expands this box by a 50px margin on all sides. | |
| 3. The Pillow engine creates an image matching these exact cropped dimensions and applies a coordinate offset of $-(x_{min} - \text{margin}), -(y_{min} - \text{margin})$ to all drawing routines. | |
| 4. Shapes are rendered with crisp black borders, clean fills, and drop shadows offset by $+4\text{px}$ in light-gray, generating a beautiful modern aesthetic. | |
| --- | |
| ## ๐ Getting Started | |
| ### Prerequisites | |
| Ensure Python is installed and fetch dependencies: | |
| ```bash | |
| pip install Pillow streamlit | |
| ``` | |
| ### Running the Desktop Application | |
| ```bash | |
| python flowchart_app.py | |
| ``` | |
| ### Running the Web Application Locally | |
| ```bash | |
| streamlit run app.py | |
| ``` | |
| --- | |
| ## ๐ Deployment on Hugging Face | |
| This project is pre-configured for Hugging Face Spaces using the Streamlit SDK. | |
| 1. Create a new Space on [Hugging Face](https://huggingface.co/spaces) and choose the **Streamlit** SDK. | |
| 2. Link your local directory to the Git remote of the Space: | |
| ```bash | |
| git remote add origin https://huggingface.co/spaces/your-username/your-space-name | |
| ``` | |
| 3. Push to Hugging Face: | |
| ```bash | |
| git push -u origin main --force | |
| ``` | |