Skip to content

next version - new syntax, AST, etc #9

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 100 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
100 commits
Select commit Hold shift + click to select a range
c378e00
formatter
dragoncoder047 Aug 11, 2024
eae3b2d
implement shrink method
dragoncoder047 Aug 11, 2024
7cb9b79
refactor metric code
dragoncoder047 Aug 11, 2024
b615ce8
tweaks
dragoncoder047 Aug 11, 2024
8cace81
add Wire ast node
dragoncoder047 Aug 11, 2024
b17d2f1
tweaks
dragoncoder047 Aug 12, 2024
d5e7621
add top-level drawing node
dragoncoder047 Aug 12, 2024
612cb34
broke Grid __repr__
dragoncoder047 Aug 12, 2024
79fe28a
add TOML-powered data parser prototype
dragoncoder047 Aug 12, 2024
dac599b
remove outdated imports
dragoncoder047 Aug 12, 2024
217352d
toml is weird so *gasp* I rolled my own parser
dragoncoder047 Aug 12, 2024
0fb00a9
formatting etc
dragoncoder047 Aug 12, 2024
3dd8f0e
add refdes finder
dragoncoder047 Aug 12, 2024
cc4de5a
add component flood finder
dragoncoder047 Aug 13, 2024
40f25c6
formatter
dragoncoder047 Aug 13, 2024
228249e
add component registry
dragoncoder047 Aug 13, 2024
aeb6cdb
make the linter happier
dragoncoder047 Aug 14, 2024
3e2e65b
more making the linter happy
dragoncoder047 Aug 14, 2024
be578e8
finished making the linter happy
dragoncoder047 Aug 14, 2024
ad1cd6a
missed typed
dragoncoder047 Aug 14, 2024
2c16928
modify to include wire configurations on Wire class
dragoncoder047 Aug 14, 2024
ac9e6dd
whoops that's going to be the box...
dragoncoder047 Aug 14, 2024
91db204
add components terminals finder
dragoncoder047 Aug 14, 2024
c7a1544
need to mask terminal that is not already a wire
dragoncoder047 Aug 16, 2024
40bb0d9
linter
dragoncoder047 Aug 16, 2024
d908063
refactor
dragoncoder047 Aug 16, 2024
70e9237
add partial net and general drawing parse code
dragoncoder047 Aug 16, 2024
f90d6d2
change import method
dragoncoder047 Aug 17, 2024
c1089f6
add wire tag parsing
dragoncoder047 Aug 17, 2024
25947c3
connect wire_tag to wire
dragoncoder047 Aug 17, 2024
d69c908
add points2path to make path output smaller
dragoncoder047 Aug 17, 2024
c37d58c
add net grouping by wire tag
dragoncoder047 Aug 17, 2024
731549a
allow refdes without number
dragoncoder047 Aug 17, 2024
d4d5bf0
Update grid.py
dragoncoder047 Aug 17, 2024
0549d42
use cls instead of hardcoded class
dragoncoder047 Aug 17, 2024
549d078
wrong directions
dragoncoder047 Aug 18, 2024
e4e71df
fix terminal-finding bugs & add comment annotations
dragoncoder047 Aug 18, 2024
7d420bc
bunch_o_lines output is now even shorter
dragoncoder047 Aug 19, 2024
140392b
refactor duplicated flood fill functions
dragoncoder047 Aug 19, 2024
e6c3fbf
clean up
dragoncoder047 Aug 19, 2024
e306475
add annotation boxes
dragoncoder047 Aug 19, 2024
bcd21af
change global options name
dragoncoder047 Aug 19, 2024
4b09b29
change global options name
dragoncoder047 Aug 19, 2024
38e2e09
Merge branch 'origin/next' into next
dragoncoder047 Aug 19, 2024
923e804
make keywords obvious
dragoncoder047 Aug 20, 2024
852ae5f
docstrings (PEP 257)
dragoncoder047 Aug 21, 2024
5614335
add some hook methods and renderers to component and drawing class
dragoncoder047 Aug 21, 2024
a704452
pep 257 on utils.py
dragoncoder047 Aug 22, 2024
f9c3c06
add DataConsumer bases to simplify data-getting from Data instances
dragoncoder047 Aug 26, 2024
ba5cd30
implement annotation renderer
dragoncoder047 Aug 26, 2024
2990350
need to also call superclass method
dragoncoder047 Aug 26, 2024
554f88a
still need to add it to the namespaces
dragoncoder047 Aug 26, 2024
3b6cfa0
prevent unexpected keyword argument error
dragoncoder047 Aug 26, 2024
cb87828
don't complain if it's the same exact option
dragoncoder047 Aug 26, 2024
210f50b
"inherit" didn't do anything
dragoncoder047 Aug 27, 2024
b56db42
delete old files
dragoncoder047 Aug 27, 2024
24e13c5
implement first component renderer classes - Resistors
dragoncoder047 Aug 27, 2024
363c723
dots
dragoncoder047 Aug 27, 2024
c9ba88e
import_all_components()
dragoncoder047 Aug 28, 2024
9c858e0
formatting
dragoncoder047 Aug 29, 2024
6d70fb9
add capacitor and refactor units definitions
dragoncoder047 Aug 29, 2024
0dfcbde
special case 0 can take any multiplier because it's 0
dragoncoder047 Aug 30, 2024
41a990f
port over inductor
dragoncoder047 Aug 30, 2024
c86233d
prot battery
dragoncoder047 Aug 30, 2024
67defbc
refactor to make shorter literals
dragoncoder047 Aug 30, 2024
ab28a90
typing for deep_transform
dragoncoder047 Sep 13, 2024
eb44d7d
allow the unit to be included in the value
dragoncoder047 Sep 13, 2024
b4b4af3
add documentation for new format
dragoncoder047 Sep 13, 2024
f18dba0
switched arguments oops
dragoncoder047 Sep 13, 2024
19b547e
out of date stuff
dragoncoder047 Sep 13, 2024
39566e1
add a makefile yay
dragoncoder047 Sep 13, 2024
2b0ebb6
add note about data separator
dragoncoder047 Sep 13, 2024
a13bd71
rename to available_options
dragoncoder047 Sep 13, 2024
d8726cc
make docs
dragoncoder047 Sep 13, 2024
7a07e84
Merge remote-tracking branch 'origin/main' into next
dragoncoder047 Sep 17, 2024
398c9c5
docstring
dragoncoder047 Nov 6, 2024
afe34bc
squelch "security hole" warning, it isn't
dragoncoder047 Nov 6, 2024
4c0274b
test test
dragoncoder047 Nov 6, 2024
36badb8
add diode skeleton (need to finish)
dragoncoder047 Nov 6, 2024
44ddaad
add blob test file for SO question
dragoncoder047 Nov 6, 2024
02b9c35
simplify
dragoncoder047 Nov 6, 2024
d492ce5
some more testing of blob algorithm
dragoncoder047 Nov 6, 2024
777a903
fix some typing and stuff
dragoncoder047 Apr 6, 2025
7ac546d
new algorithm idea for U
dragoncoder047 Apr 6, 2025
0df2c5b
some work on the blob algorithm
dragoncoder047 Apr 8, 2025
339f7b8
one character fix
dragoncoder047 Apr 8, 2025
906accb
fix perimeter walking algorithm
dragoncoder047 Apr 8, 2025
206e8c9
use topleft for all
dragoncoder047 Apr 8, 2025
abbfd58
enable type checking
dragoncoder047 Apr 8, 2025
04ebdce
it is working nice!!
dragoncoder047 Apr 8, 2025
e001396
more test cases and edge cases
dragoncoder047 Apr 8, 2025
c7c4f5b
not used
dragoncoder047 Apr 8, 2025
df69da4
add many more test cases + tweaks
dragoncoder047 Apr 8, 2025
82baa42
fix cli args options
dragoncoder047 Apr 9, 2025
7b1fc28
minor refactoring: make things that don't need to be lists into sets,…
dragoncoder047 Apr 9, 2025
793a07d
i could have actually been putting the test case name there the whole…
dragoncoder047 Apr 10, 2025
dff1320
i am ashamed for such a large commit
dragoncoder047 Apr 12, 2025
975b49c
metaclass madness breaks stuff, multiple inheritance causes problems,…
dragoncoder047 Apr 14, 2025
56cab42
fixed decorator registering
dragoncoder047 Apr 14, 2025
228db38
docs script is all messed up because of inheritance
dragoncoder047 Apr 15, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
{
"cSpell.words": [
"Cbox",
"clrall",
"clrmask",
"MOSFET",
"nanofarads",
"NFET",
"PFET",
"polylinegon",
"rendec",
"schemascii",
"tspan"
],
"python.linting.pylintEnabled": false,
"python.linting.flake8Enabled": false,
"python.linting.enabled": true
]
}
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.PHONY: docs must_specify

must_specify:
@echo "there is no default makefile rule"
@exit 1

docs:
python3 scripts/docs.py
125 changes: 1 addition & 124 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,128 +1,5 @@
# Schemascii

[![GitHub issues](https://img.shields.io/github/issues/dragoncoder047/schemascii)](https://github.com/dragoncoder047/schemascii/issues)
![GitHub commit activity](https://img.shields.io/github/commit-activity/w/dragoncoder047/schemascii)
![GitHub last commit](https://img.shields.io/github/last-commit/dragoncoder047/schemascii)
![GitHub repo file count](https://img.shields.io/github/directory-file-count/dragoncoder047/schemascii)
![Python](https://img.shields.io/badge/python-%3E%3D3.10-blue)

A command-line tool and library for converting ASCII-art diagrams into beautiful SVG circuit schematics.

Turn this:

```none
*--BAT1+--*-------*---*
| | | |
| R1 .~~~. |
| | : :-*
| *-----: :---+C2--*--D2+--*----------J1
| | :U1 : | |
| R2 :555: | |
| | *-: :-* | |
| C1 | : : | + C3
| | *-: : C4 D1 +
| *---* .~~~. | | |
| | | | | |
*---------*-------*---*------*-------*----------J2

BAT1:5
R1:10k
R2:100k
C1:10000p
C2:10u
C3:100u
C4:10p
D1:1N4001
D2:1N4001
U1:NE555,7,6,2,1,5,3,4,8
J1:-5V
J2:GND
```

Into this:

![image](test_data/test_charge_pump.png)

And with a little CSS, this:

![image](test_data/test_charge_pump_css.png)

Works with Python 3.10+. It uses the new `match` feature in a few places. If you need to run Schemascii on an older version of Python, feel free to fork it and send me a pull request.

## Installation

Not published to PyPI yet, so you have two options:

1. Install using pip's VCS support:
```bash
pip install git+https://github.com/dragoncoder047/schemascii
```
2. Install from source:
```bash
git clone https://github.com/dragoncoder047/schemascii
cd schemascii
pip install .
```

You can also add `git+https://github.com/dragoncoder047/schemascii` to your `requirements.txt` if you have one.

## Command line usage

```usage
usage: schemascii [-h] [-V] [-o OUT_FILE] [--padding PADDING] [--scale SCALE] [--stroke_width STROKE_WIDTH] [--stroke STROKE]
[--label {L,V,VL}] [--nolabels]
in_file

Render ASCII-art schematics into SVG.

positional arguments:
in_file File to process.

options:
-h, --help show this help message and exit
-V, --version show program's version number and exit
-o OUT_FILE, --out OUT_FILE
Output SVG file. (default input file plus .svg)
--padding PADDING Amount of padding to add on the edges.
--scale SCALE Scale at which to enlarge the entire diagram by.
--stroke_width STROKE_WIDTH
Width of the lines
--stroke STROKE Color of the lines.
--label {L,V,VL} Component label style (L=include label, V=include value, VL=both)
--nolabels Turns off labels on all components, except for part numbers on ICs.
```

## Python usage

```python
import schemascii

# Render a file
svg = schemascii.render("my_circuit.txt")

# Render a string
text = ... # this is the text of your file
svg = schemascii.render("<string>", text)

# Provide options
svg = schemascii.render("my_circuit.txt",
padding=10,
scale=15,
stroke_width=2,
stroke="black",
label="LV",
nolabels=False)
# these are the defaults
```

## Contributing Tips

Make sure you have an *editable* install, so you can edit and still be able to use the `schemascii` command to test it:

```bash
pip uninstall schemascii
cd path/to/your/schemascii/checkout
pip install -e .
```
## This is the NEXT branch which contains unstable breaking changes. Don't install this branch if you want Schemascii to work.

<!-- https://realpython.com/pypi-publish-python-package/ -->
2 changes: 2 additions & 0 deletions algorithm.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Schemascii algorithm

(THIS DOCUMENT IS HORRIBLY OUT-OF-DATE, I NEED TO RE-WRITE THE UPDATED ALGORITHM...)

Everything is based on a `Grid` that contains all the data for the document. This `Grid` can be masked to show something else instead of the original, and restored to the original.

The algorithm first starts by finding all the "large component" boxes in the grid. After that, it finds all the "small component" reference designators. Then it picks out the BOM notes so component values, etc. can be included.
Expand Down
5 changes: 4 additions & 1 deletion designators.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

(copied from <https://en.wikipedia.org/wiki/Reference_designator> and edited lightly)

This is a list of all components that Schemascii *might* support. For a complete list of all supported components, (generated from the implementation file), please see [supported-components.md](./supported-components.md). If a component you want is not supported, have a look at [#3](https://github.com/dragoncoder047/schemascii/issues/3) or fork and implement it yourself.
This is a list of all components that Schemascii *might* support. For a complete list of all supported components, (generated from the implementation file), please see [options.md][options]. If a component you want is not supported, post a request on [issue #3][todo_components] or fork and implement it yourself.

| Designator | Component type |
|:--:|:--|
Expand Down Expand Up @@ -73,3 +73,6 @@ This is a list of all components that Schemascii *might* support. For a complete
| VT | Voltage transformer |
| W | Wire |
| X, XTAL, Y | Crystal oscillator, ceramic resonator |

[options]: options.md
[todo_components]: https://github.com/dragoncoder047/schemascii/issues/3
128 changes: 97 additions & 31 deletions format.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,56 +15,122 @@ Crossed: Joined: Corner: Crossed: Crossed: Dangling en
| | | |
```

## Components

### Small components
### Wire Tags

Small components are notated with their reference designator: one or more uppercase letters followed by an ID, which can either be a number, or a period followed by some text.
Wire tags look like `<name=` or `=name>` and are attached to specific wires. All wires tagged with the same `name` belong to the same net. Currently this only affects rendering.

They are always written horizontally even if the terminals are on the top and bottom.
## Components

IDs are allowed to be duplicated and result in the same component values.
Components are notated with their reference designator: one or more uppercase letters followed by an ID (a nonnegative integer) optionally followed by a suffix (which can be composed of uppercase letters and underscores)

Components can be padded on either side with `#`'s to make them bigger.
* They are always written horizontally even if the terminals are on the top and bottom.
* IDs are allowed to be duplicated and result in the same component values.
* Components can be padded (horizontally and vertically) `#`'s to make them bigger and control the shape (for components that support shapes).

Examples:

* `C33`
* `Q1001`
* `L.Coil`
* `L5`
* `F3#`
* `D7#####`
* `U1#####`
* `####R.Heater####`
* `####D7#####`
* `R333A`
* ```txt
# ######
# ########
----# #########
# #U1G1#####----
----# #########
# ########
# ######
```

Components are able to accept "flags", which are other punctuation characters and lowercase letters touching them.
Components' terminals are annotated with "flags", which are other punctuation characters and lowercase letters touching them.

* Polarized components (caps, diodes, etc.) accept a `+` to indicate the polarity,
* Transistors use lowercase letters to indicate collector/emitter/base (for BJTs) or source/gate/drain (for FETs).
* Polarized components (caps, diodes, etc.) use a `+` for one terminal to indicate the polarity,
* Transistors use 3 lowercase letters to indicate collector/emitter/base (for bipolar transistors) or source/gate/drain (for field-effect transistors).

### Big components
## Annotations

* Big components are indicated with a box made of `~` (top and bottom), `:` (sides), and `.` (corners).
* The inside of the box can contain anything you want, but it must include exactly one reference designator as to what component it is.
### Text Annotations

## Reference designators
You can include arbitrary text in the diagram by `[enclosing it in brackets like this]`.

To include component values, pin numbers, etc, somewhere in the drawing but not touching any wires or other components, you can write component values - these are formatted as the reference designator (same as in circuit) followed by a `:` and the value parameter (which stops at the first whitespace). *I usually put these in a "BOM section" below the circuit itself but you could also put them right next to the component.*
### Line Annotations

For simple components, this is usually just a value rating, but *without* the units (only the Metric prefix). For more specific components (mostly semiconductor devices) this is usually the part number and/or some string to determine how to draw the component.
To outline certain areas with lines (that are not wires), you can draw lines with colons/tildes (for straight lines), and periods/single quotes (for corners) like so:

Examples:
```txt
.~~~~~~~~~~.
: .~~~ :
: : :
: : :
: : .~~'~~~~.
: '~~~~' :
'~~~~~~~~~~~~~~~'
```

Lines are allowed to cross wires. They are crossed the same way that wires cross each other.

## Data

Every drawing is required to have a "data section" appended after it. The data section contains mappings of values to tell Schemascii how to render each component (e.g. how thick to make the lines, what the components' values are, etc.)

The data section is separated from the actual circuit by a line containing `---` and nothing else. This is REQUIRED and Schemascii will complain if it doesn't find any line containing `---` and nothing else, even if there is no data after it.

Here is some example data:

```txt
* {
%% these are global config options
color = black
width = 2; padding = 20;
format = symbol
mystring = "hello\nworld"
}

R* %% matches any resistor
{tolerance = .05; wattage = 0.25}

VR1 {
resistance = 0 - 10k; %% ranges on variable components
%% trailing comments are allowed
}
```

In each RULE, the SELECTOR defines what components and/or drawing objects match; for the ones that match, the MAPPING (which is stored as a Python `dict`) is or'ed into the computed properties. Rules on the bottom take precedence (there is no such thing as specificity like there is in CSS).

Selectors can match multiple scopes by using glob matching. Schemascii currently uses [`fnmatch`][fnmatch] to determine matching selectors but I'll probably change that.

See [options.md][options] for what options are available in which scopes.

Here is a rough grammar (space ignored):

```ebnf
DATA = RULE (eol+ RULE)* ;
RULE = SELECTOR eol* MAPPING ;
SELECTOR = symbol ;
MAPPING = "{" ((NAME "=" VALUE)? eol)* "}" ;
NAME = string | symbol ;
VALUE = (number | string | symbol)* ;
number = /(?:\d*\.)?\d+(?:[Ee][+-]?\d+)?/ ;
string = /"(?:\\"|[^"])+"|\s+/ ;
symbol = <any sequence of printing characters that isn't another kind of token>
eol = ";" | comment? "\n"+ ;
comment = "%%" <everything until newline> ;
```

### Metric values

* `C33:2.2u` -- "2.2 &micro;F"
* `Q1001:pnp:TIP102` -- "pnp", "npn", "pfet", or "nfet" to determine what kind of transistor, plus the part number; printed verbatim
* `L51:0.33` -- this is rewritten to "330 mH" so that it has no decimal point.
* `F3:1500m` -- rewritten: "1.5 A"
* `D7:1N4001` -- again, part number
* `U1:SN74LS08N,14,1,2,7,3` -- some components let you label pins with whatever you want (in this case just numbers). They start at the top-leftmost and follow **counterclockwise** to follow with the pin-numbering of most IC's.
* `R2:10,5` -- this is formatted as "10 &ohm; 5 W". The second value is the rating; for resistors, this is a wattage, for most else, this is a maximum voltage.
For many small components, the value of the component is just a number and some unit -- e.g. for resistors it's ohms (&ohm;). Schemascii includes built-in Metric normalization and units, so if the "value" is a Metric number it will be re-written to be as short as possible while still observing the conventions of typical component values. If the unit symbol is not included, it will be added.

## Inline configuration values
* `value = 0.33m` on an inductor --> "330 &micro;H" (integer values are preferred).
* `value = 0.33` on a resistor --> "0.33 &ohm;" (no Metric multiplier is preferred).
* `value = 1500mA` on a fuse --> "1.5 A" (using decimal point is shorter)
* `value = 2.2 nF` on a capacitor --> "2200 pF" (capacitances aren't typically given in nanofarads)
* `value = 10; power = 5` --> "10 &ohm; 5 W". Many components have "secondary" optional values; the name will be given in the [options][].

**New in 0.2.0!**
Also, if the value is not even a number -- such as `L1 {value = "detector coil"}`, the Metric-normalization code will not activate, and the value written will be used verbatim -- "L1 detector coil".

You can specify configuration values for rendering the components inline in the document by writing `!name=value!` in your document. See the help output of the Schemascii CLI for the different options (in the README) or look at the config options at the top of [`configs.py`](https://github.com/dragoncoder047/schemascii/blob/main/schemascii/configs.py). The most common options I use are `scale` and `padding`.
[fnmatch]: https://docs.python.org/3/library/fnmatch.html#fnmatch.fnmatch
[options]: options.md
Loading