├── .gitignore
├── LICENSE
├── MANIFEST.in
├── Makefile
├── README.md
├── docs
├── API_Documentation
│ ├── arc.md
│ ├── circle.md
│ ├── ellipse.md
│ ├── line.md
│ ├── node.md
│ ├── plot_coordinates.md
│ ├── point.md
│ ├── rectangle.md
│ ├── scope.md
│ └── tikz_picture.md
├── examples.md
├── index.md
├── installation.md
├── png
│ ├── PlotCoords_rotate_Example.png
│ ├── arc_ex_1.png
│ ├── arc_ex_2.png
│ ├── barycentric.png
│ ├── basic.png
│ ├── blowup.png
│ ├── blowup_ex.png
│ ├── cantor.png
│ ├── cauchy_residue_thm_arc_ex.png
│ ├── cauchy_residue_thm_ex.png
│ ├── circle_center.png
│ ├── circle_east.png
│ ├── circle_ex_1.png
│ ├── circle_ex_2.png
│ ├── circle_intersections.png
│ ├── circle_intersections_2.png
│ ├── circle_north.png
│ ├── circle_point_at_arg.png
│ ├── circle_south.png
│ ├── circle_west.png
│ ├── circles.png
│ ├── connect_circles.png
│ ├── des.png
│ ├── dots.png
│ ├── draw_segments.png
│ ├── ellipse_ex_1.png
│ ├── favicon.png
│ ├── fully_connected_neural_network.png
│ ├── geometry_figure.png
│ ├── grid.png
│ ├── integration_ex.png
│ ├── intersection_blobs.png
│ ├── intersection_circles.png
│ ├── line_and_two_nodes.png
│ ├── line_end.png
│ ├── line_ex_1.png
│ ├── line_ex_2.png
│ ├── line_midpoint.png
│ ├── line_pos_a_t.png
│ ├── line_start.png
│ ├── linear_transformation_ex_1.png
│ ├── linear_transformation_ex_2.png
│ ├── lorenz_ex.png
│ ├── nn_nodes.png
│ ├── node_ex_1.png
│ ├── node_ex_2.png
│ ├── plotcoordinates_ex_1.png
│ ├── plotcoordinates_ex_2.png
│ ├── plotcoordinates_ex_3.png
│ ├── polar.png
│ ├── projective_cone.png
│ ├── rectangle_ex_1.png
│ ├── roots_of_unity.png
│ ├── rotate_circles.png
│ ├── rotate_plot.png
│ ├── shift_plot.png
│ ├── sphere_loop.png
│ ├── spiral.png
│ ├── test_region_and.png
│ ├── test_region_not.png
│ ├── test_region_or.png
│ ├── test_region_xor.png
│ ├── tikz_write_ex_1.png
│ ├── tikz_write_ex_2.png
│ ├── transformer.png
│ └── tutorial_imgs
│ │ ├── line.png
│ │ ├── log_cut_step_1.png
│ │ ├── log_cut_step_2.png
│ │ ├── log_cut_step_3.png
│ │ ├── neural_network_diagram.png
│ │ ├── neural_network_step_1.png
│ │ ├── neural_network_step_2.png
│ │ ├── neural_network_step_3.png
│ │ ├── neural_network_step_4.png
│ │ └── neural_network_step_5.png
├── stylesheets
│ └── extra.css
└── tutorials.md
├── examples
├── _run_all_examples.sh
├── barycentric
│ └── barycentric.py
├── basic
│ └── basic_ex.py
├── blowup
│ └── blowup.py
├── cantor
│ └── cantor.py
├── cauchy_residue_thm
│ ├── cauchy_residue_thm.py
│ └── cauchy_residue_thm_arc.py
├── circle_intersections
│ └── circle_intersections.py
├── circle_intersections_2
│ └── circle_intersections_2.py
├── circles
│ └── circles.py
├── code.tex
├── controls
│ └── controls.py
├── des
│ └── des.py
├── geometry_figure
│ └── geometry_figure.py
├── line_and_two_nodes
│ └── line_and_two_nodes.py
├── linear_model
│ └── linear_model.py
├── linear_transformations
│ └── linear_transformations.py
├── lorenz
│ └── lorenz.py
├── neural_network
│ └── neural_network.py
├── neural_network_connection
│ └── neural_network_connection.py
├── neural_network_simple
│ └── neural_network_simple.py
├── polar
│ └── polar.py
├── projective_cone
│ └── projective_cone.py
├── relu
│ └── relu.py
├── roots_of_unity
│ └── roots_of_unity.py
├── rotate_circles
│ └── rotate_circles.py
├── rotate_plot
│ └── rotate_plot.py
├── shift_plot
│ └── shift_plot.py
├── sigmoid
│ └── sigmoid.py
├── sphere_loop
│ └── sphere_loop.py
├── spiral
│ └── spiral.py
├── symbolic_integration
│ └── integrate_and_plot.py
├── tikz_code.tex
├── transformer
│ └── pre_layer_transformer.py
└── ven_diagrams
│ └── intersections_scope_clip.py
├── mkdocs.yml
├── pyproject.toml
├── requirements.txt
├── src
└── tikzpy
│ ├── __init__.py
│ ├── colors
│ ├── __init__.py
│ └── colors.py
│ ├── drawing_objects
│ ├── __init__.py
│ ├── arc.py
│ ├── circle.py
│ ├── drawing_object.py
│ ├── drawing_utils.py
│ ├── ellipse.py
│ ├── line.py
│ ├── node.py
│ ├── plotcoordinates.py
│ ├── point.py
│ ├── rectangle.py
│ └── xy_plane.py
│ ├── styles
│ ├── __init__.py
│ └── arrows_along_path.py
│ ├── templates
│ ├── tex_file.py
│ └── tikz_code.tex
│ ├── tikz_environments
│ ├── __init__.py
│ ├── clip.py
│ ├── scope.py
│ ├── tikz_command.py
│ ├── tikz_environment.py
│ ├── tikz_picture.py
│ └── tikz_style.py
│ └── utils
│ ├── __init__.py
│ ├── helpers.py
│ └── types.py
└── tests
├── drawing_objects
├── test_arc.py
├── test_circle.py
├── test_ellipse.py
├── test_line.py
├── test_node.py
├── test_plot_coordinates.py
├── test_point.py
└── test_rectangle.py
├── integration
├── test_basic.py
├── test_cauchy_residue_thm.py
├── test_circles.py
├── test_controls.py
├── test_line_and_two_nodes.py
├── test_relu.py
├── test_shift_plot.py
└── test_simple.py
├── test_action_arg.py
├── test_add_node.py
├── test_attribute_assignment.py
├── test_file_creation.py
├── test_helpler_funcs.py
└── test_tikz_picture.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.sw*
2 |
3 | # "Get a Mac!", they said...
4 | *.DS_Store
5 |
6 | # tikz_code
7 | *tex_file.pdf
8 |
9 | # Pycharm
10 | .idea
11 |
12 | ### Python ###
13 | # Byte-compiled / optimized / DLL files
14 | __pycache__/
15 | *.py[cod]
16 | *$py.class
17 |
18 | # C extensions
19 | *.so
20 |
21 | # Distribution / packaging
22 | .Python
23 | build/
24 | develop-eggs/
25 | dist/
26 | downloads/
27 | eggs/
28 | .eggs/
29 | lib/
30 | lib64/
31 | parts/
32 | sdist/
33 | var/
34 | wheels/
35 | pip-wheel-metadata/
36 | share/python-wheels/
37 | *.egg-info/
38 | .installed.cfg
39 | *.egg
40 | MANIFEST
41 |
42 | # PyInstaller
43 | # Usually these files are written by a python script from a template
44 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
45 | *.manifest
46 | *.spec
47 |
48 | # Installer logs
49 | pip-log.txt
50 | pip-delete-this-directory.txt
51 |
52 | # Unit test / coverage reports
53 | htmlcov/
54 | .tox/
55 | .nox/
56 | .coverage
57 | .coverage.*
58 | .cache
59 | nosetests.xml
60 | coverage.xml
61 | *.cover
62 | *.py,cover
63 | .hypothesis/
64 | .pytest_cache/
65 | pytestdebug.log
66 |
67 | # Translations
68 | *.mo
69 | *.pot
70 |
71 | # Django stuff:
72 | *.log
73 | local_settings.py
74 | db.sqlite3
75 | db.sqlite3-journal
76 |
77 | # Flask stuff:
78 | instance/
79 | .webassets-cache
80 |
81 | # Scrapy stuff:
82 | .scrapy
83 |
84 | # Sphinx documentation
85 | docs/_build/
86 | doc/_build/
87 |
88 | # PyBuilder
89 | target/
90 |
91 | # Jupyter Notebook
92 | .ipynb_checkpoints
93 |
94 | # IPython
95 | profile_default/
96 | ipython_config.py
97 |
98 | # pyenv
99 | .python-version
100 |
101 | # pipenv
102 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
103 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
104 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
105 | # install all needed dependencies.
106 | #Pipfile.lock
107 |
108 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
109 | __pypackages__/
110 |
111 | # Celery stuff
112 | celerybeat-schedule
113 | celerybeat.pid
114 |
115 | # SageMath parsed files
116 | *.sage.py
117 |
118 | # Environments
119 | .env
120 | .venv
121 | env/
122 | venv/
123 | ENV/
124 | env.bak/
125 | venv.bak/
126 | pythonenv*
127 |
128 | # Spyder project settings
129 | .spyderproject
130 | .spyproject
131 |
132 | # Rope project settings
133 | .ropeproject
134 |
135 | # mkdocs documentation
136 | /site
137 |
138 | # mypy
139 | .mypy_cache/
140 | .dmypy.json
141 | dmypy.json
142 |
143 | # Pyre type checker
144 | .pyre/
145 |
146 | # pytype static type analyzer
147 | .pytype/
148 |
149 | # profiling data
150 | .prof
151 |
152 | ### TeX ###
153 | ## Core latex/pdflatex auxiliary files:
154 | *.aux
155 | *.lof
156 | *.lot
157 | *.fls
158 | *.out
159 | *.toc
160 | *.fmt
161 | *.fot
162 | *.cb
163 | *.cb2
164 | .*.lb
165 |
166 | ## Intermediate documents:
167 | *.dvi
168 | *.xdv
169 | *-converted-to.*
170 | # these rules might exclude image files for figures etc.
171 | # *.ps
172 | # *.eps
173 | # *.pdf
174 |
175 | ## Generated if empty string is given at "Please type another file name for output:"
176 | .pdf
177 |
178 | ## Bibliography auxiliary files (bibtex/biblatex/biber):
179 | *.bbl
180 | *.bcf
181 | *.blg
182 | *-blx.aux
183 | *-blx.bib
184 | *.run.xml
185 |
186 | ## Build tool auxiliary files:
187 | *.fdb_latexmk
188 | *.synctex
189 | *.synctex(busy)
190 | *.synctex.gz
191 | *.synctex.gz(busy)
192 | *.pdfsync
193 |
194 | ## Build tool directories for auxiliary files
195 | # latexrun
196 | latex.out/
197 |
198 | ## Auxiliary and intermediate files from other packages:
199 | # algorithms
200 | *.alg
201 | *.loa
202 |
203 | # achemso
204 | acs-*.bib
205 |
206 | # amsthm
207 | *.thm
208 |
209 | # beamer
210 | *.nav
211 | *.pre
212 | *.snm
213 | *.vrb
214 |
215 | # changes
216 | *.soc
217 |
218 | # comment
219 | *.cut
220 |
221 | # cprotect
222 | *.cpt
223 |
224 | # elsarticle (documentclass of Elsevier journals)
225 | *.spl
226 |
227 | # endnotes
228 | *.ent
229 |
230 | # fixme
231 | *.lox
232 |
233 | # feynmf/feynmp
234 | *.mf
235 | *.mp
236 | *.t[1-9]
237 | *.t[1-9][0-9]
238 | *.tfm
239 |
240 | #(r)(e)ledmac/(r)(e)ledpar
241 | *.end
242 | *.?end
243 | *.[1-9]
244 | *.[1-9][0-9]
245 | *.[1-9][0-9][0-9]
246 | *.[1-9]R
247 | *.[1-9][0-9]R
248 | *.[1-9][0-9][0-9]R
249 | *.eledsec[1-9]
250 | *.eledsec[1-9]R
251 | *.eledsec[1-9][0-9]
252 | *.eledsec[1-9][0-9]R
253 | *.eledsec[1-9][0-9][0-9]
254 | *.eledsec[1-9][0-9][0-9]R
255 |
256 | # glossaries
257 | *.acn
258 | *.acr
259 | *.glg
260 | *.glo
261 | *.gls
262 | *.glsdefs
263 | *.lzo
264 | *.lzs
265 |
266 | # uncomment this for glossaries-extra (will ignore makeindex's style files!)
267 | # *.ist
268 |
269 | # gnuplottex
270 | *-gnuplottex-*
271 |
272 | # gregoriotex
273 | *.gaux
274 | *.gtex
275 |
276 | # htlatex
277 | *.4ct
278 | *.4tc
279 | *.idv
280 | *.lg
281 | *.trc
282 | *.xref
283 |
284 | # hyperref
285 | *.brf
286 |
287 | # knitr
288 | *-concordance.tex
289 | # TODO Comment the next line if you want to keep your tikz graphics files
290 | *.tikz
291 | *-tikzDictionary
292 |
293 | # listings
294 | *.lol
295 |
296 | # luatexja-ruby
297 | *.ltjruby
298 |
299 | # makeidx
300 | *.idx
301 | *.ilg
302 | *.ind
303 |
304 | # minitoc
305 | *.maf
306 | *.mlf
307 | *.mlt
308 | *.mtc
309 | *.mtc[0-9]*
310 | *.slf[0-9]*
311 | *.slt[0-9]*
312 | *.stc[0-9]*
313 |
314 | # minted
315 | _minted*
316 | *.pyg
317 |
318 | # morewrites
319 | *.mw
320 |
321 | # nomencl
322 | *.nlg
323 | *.nlo
324 | *.nls
325 |
326 | # pax
327 | *.pax
328 |
329 | # pdfpcnotes
330 | *.pdfpc
331 |
332 | # sagetex
333 | *.sagetex.sage
334 | *.sagetex.py
335 | *.sagetex.scmd
336 |
337 | # scrwfile
338 | *.wrt
339 |
340 | # sympy
341 | *.sout
342 | *.sympy
343 | sympy-plots-for-*.tex/
344 |
345 | # pdfcomment
346 | *.upa
347 | *.upb
348 |
349 | # pythontex
350 | *.pytxcode
351 | pythontex-files-*/
352 |
353 | # tcolorbox
354 | *.listing
355 |
356 | # thmtools
357 | *.loe
358 |
359 | # TikZ & PGF
360 | *.dpth
361 | *.md5
362 | *.auxlock
363 |
364 | # todonotes
365 | *.tdo
366 |
367 | # vhistory
368 | *.hst
369 | *.ver
370 |
371 | # easy-todo
372 | *.lod
373 |
374 | # xcolor
375 | *.xcp
376 |
377 | # xmpincl
378 | *.xmpi
379 |
380 | # xindy
381 | *.xdy
382 |
383 | # xypic precompiled matrices and outlines
384 | *.xyc
385 | *.xyd
386 |
387 | # endfloat
388 | *.ttt
389 | *.fff
390 |
391 | # Latexian
392 | TSWLatexianTemp*
393 |
394 | ## Editors:
395 | # WinEdt
396 | *.bak
397 | *.sav
398 |
399 | # Texpad
400 | .texpadtmp
401 |
402 | # LyX
403 | *.lyx~
404 |
405 | # Kile
406 | *.backup
407 |
408 | # gummi
409 | .*.swp
410 |
411 | # KBibTeX
412 | *~[0-9]*
413 |
414 | # TeXnicCenter
415 | *.tps
416 |
417 | # auto folder when using emacs and auctex
418 | ./auto/*
419 | *.el
420 |
421 | # expex forward references with \gathertags
422 | *-tags.tex
423 |
424 | # standalone packages
425 | *.sta
426 |
427 | # Makeindex log files
428 | *.lpz
429 |
430 | # REVTeX puts footnotes in the bibliography by default, unless the nofootinbib
431 | # option is specified. Footnotes are the stored in a file with suffix Notes.bib.
432 | # Uncomment the next line to have this generated file ignored.
433 | #*Notes.bib
434 |
435 | ### TeX Patch ###
436 | # LIPIcs / OASIcs
437 | *.vtc
438 |
439 | # glossaries
440 | *.glstex
441 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2021 Luke Trujillo
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
9 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include tikzpy/templates/tex_file.tex
2 | include tikzpy/templates/tikz_code.tex
3 |
4 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: default
2 | default: black lint test
3 |
4 | .PHONY: black
5 | black:
6 | black tests src
7 |
8 | .PHONY: lint
9 | lint:
10 | ruff check src/tikzpy/*.py
11 |
12 | .PHONY: test
13 | test:
14 | pytest tests
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Tikz-Python
2 | An object-oriented Python approach towards providing a giant wrapper for Tikz code, with the goal of streamlining the process of creating complex figures for TeX documents.
3 |
4 | To install it, run
5 | ```
6 | pip install tikz-python
7 | ```
8 |
9 | ## Documentation
10 |
11 | We have documentation now! Please visit the [documentation](https://ltrujello.github.io/Tikz-Python) site.
12 |
13 | ## Examples
14 | Want to see some nice examples of what this package can do? See [here](https://ltrujello.github.io/Tikz-Python/examples/).
15 |
16 | ## How to Use: Basics
17 | An example of this package in action is below.
18 | ```python
19 | from tikzpy import TikzPicture # Import the class TikzPicture
20 |
21 | tikz = TikzPicture()
22 | tikz.circle((0, 0), 3, options="thin, fill=orange!15")
23 |
24 | arc_one = tikz.arc((3, 0), 0, 180, x_radius=3, y_radius=1.5, options="dashed")
25 | arc_two = tikz.arc((-3, 0), 180, 360, x_radius=3, y_radius=1.5)
26 |
27 | tikz.show() # Displays a pdf of the drawing to the user
28 | ```
29 | which produces
30 |
31 |
32 | We explain line-by-line the above code.
33 |
34 | * `from tikzpy import TikzPicture` imports the `TikzPicture` class from the `tikzpy` package.
35 |
36 | * The second line of code is analogous to the TeX code `\begin{tikzpicture}` and `\end{tikzpicture}`. The variable `tikz` is now a tikz environment, specifically an instance of the class `TikzPicture`, and we can now append drawings to it.
37 |
38 | * The third, fourth, and fifth lines draw a filled circle and two elliptic arcs, which give the illusion of a sphere.
39 |
40 | * In the last line, the call `show()` immediately displays the PDF of the drawing to the user.
41 |
42 |
--------------------------------------------------------------------------------
/docs/API_Documentation/arc.md:
--------------------------------------------------------------------------------
1 | # Arc
2 |
3 | ::: tikzpy.drawing_objects.arc.Arc
4 | members: []
5 |
6 |
7 | ## Example
8 | Here we draw and fill a sequence of arcs. We also demonstrate `draw_from_start` set to `True` and `False`. In the code below, it is by default set to `True`.
9 | ```python
10 | from tikzpy import TikzPicture
11 | from tikzpy.utils import rainbow_colors
12 |
13 | tikz = TikzPicture()
14 |
15 | for i in range(1, 10):
16 | t = 4 / i
17 | arc = tikz.arc((0, 0), 0, 180, radius=t, options=f"fill={rainbow_colors(i)}")
18 |
19 | ```
20 | This generates the image
21 |
22 |
23 |
24 | If instead we would like these arcs sharing the same center, we can use the same code, but pass in `draw_from_start=False` to achieve
25 |
26 |
27 |
28 | Without this option, if we were forced to specify the point at which each arc should begin drawing, we would have to calculate the x-shift for every arc and apply such a shift to keep the centers aligned. That sounds inefficient and like a waste of time to achieve something so simple, right?
29 |
30 |
31 | ## Methods
32 | `Arc` has access to methods `.shift()`, `.scale()`, `.rotate()`, which behave as one would expect and takes in parameters as described before.
33 |
34 | ## A few comments...
35 | This class not only provides a wrapper to draw arcs, but it also fixes a few things that Tikz's `\draw arc` command simply gets wrong and confuses users with.
36 |
37 | 1. With Tikz in TeX, to draw a circular arc one must specify `start_angle` and `end_angle`. These make sense: they are the start and end angles of the arc relative to the horizontal. To draw an elliptic arc, one must again specify `start_angle` and `end_angle`, but these actually do not represent the starting and end angles of the elliptic arc. They are the parameters `t` which parameterize the ellipse `(a*cos(t), b*sin(t))`. This makes drawing elliptic arcs inconvenient.
38 |
39 | 2. With Tikz in TeX, the position of the arc is specified by where the arc should start drawing. However, it is sometimes easier to specify the *center* of the arc.
40 |
41 | With Tikz-Python, `start_angle` and `end_angle` will always coincide with the starting and end angles, so the user will not get weird unexpected behavior. Additionally, the user can specify the arc position via its center by setting `draw_from_start=False`, but they can also fall back on the default behavior.
42 |
43 |
--------------------------------------------------------------------------------
/docs/API_Documentation/circle.md:
--------------------------------------------------------------------------------
1 | # Circle
2 |
3 | ::: tikzpy.drawing_objects.circle.Circle
4 |
5 | ## Examples
6 | Here we create several circles, making use of the `action` parameter.
7 | ```python
8 | from tikzpy import TikzPicture
9 |
10 | tikz = TikzPicture()
11 | tikz.circle((0, 0), 1.25) #action="draw" by default
12 | tikz.line((0, 0), (0, 1.25), options="dashed")
13 | tikz.circle((3, 0), 1, options="thick, fill=red!60", action="filldraw")
14 | tikz.circle((6, 0), 1.25, options="Green!50", action="fill")
15 | tikz.show()
16 | ```
17 |
18 |
19 |
20 | We can also use circles to create the [Hawaiian Earing](https://en.wikipedia.org/wiki/Hawaiian_earring).
21 |
22 | ```python
23 | from tikzpy import TikzPicture
24 |
25 | tikz = TikzPicture()
26 |
27 | radius = 5
28 | for i in range(1, 60):
29 | n = radius / i
30 | tikz.circle((n, 0), n)
31 | tikz.show()
32 | ```
33 |
34 |
35 |
--------------------------------------------------------------------------------
/docs/API_Documentation/ellipse.md:
--------------------------------------------------------------------------------
1 | # Ellipse
2 |
3 | ::: tikzpy.drawing_objects.ellipse.Ellipse
4 |
5 | ## Example
6 | Here we draw and ellipse and define the major and minors axes.
7 | ```python
8 | import tikzpy
9 |
10 | tikz = tikzpy.TikzPicture()
11 |
12 | # x,y axes
13 | tikz.line((-5, 0), (5, 0), options="Gray!40, ->")
14 | tikz.line((0, -5), (0, 5), options="Gray!40, ->")
15 | # Ellipse
16 | ellipse = tikz.ellipse(
17 | (0, 0), 4, 3, options="fill=ProcessBlue!70, opacity=0.4", action="filldraw"
18 | )
19 | # Labels
20 | h_line = tikz.line((0, 0), (ellipse.x_axis, 0), options="thick, dashed, ->")
21 | v_line = tikz.line((0, 0), (0, ellipse.y_axis), options="thick, dashed, ->")
22 | tikz.node(h_line.midpoint, options="below", text="Major")
23 | tikz.node(v_line.midpoint, options="left", text="Minor")
24 | ```
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/docs/API_Documentation/line.md:
--------------------------------------------------------------------------------
1 | # Line
2 |
3 | ::: tikzpy.drawing_objects.line.Line
4 |
5 | # Examples
6 |
7 | Here's an example of us using the `Line` class.
8 | ```python
9 | import tikzpy
10 |
11 | tikz = tikzpy.TikzPicture()
12 | tikz.line((0, 0), (4, 0), options="->", control_pts=[(1, 1), (3, -1)]
13 | ```
14 | which generates
15 |
16 |
17 |
--------------------------------------------------------------------------------
/docs/API_Documentation/node.md:
--------------------------------------------------------------------------------
1 | # Node
2 |
3 | ::: tikzpy.drawing_objects.node.Node
4 |
5 | ## Examples
6 | Here we use some nodes to label a figure explaining the logarithm branch cut
7 | ```python
8 | import tikzpy
9 |
10 | tikz = tikzpy.TikzPicture()
11 | # x,y axes
12 | tikz.line((-4, 0), (4, 0), options="Gray!40, ->")
13 | tikz.line((0, -4), (0, 4), options="Gray!40, ->")
14 | # Cut
15 | tikz.line((-4, 0), (0, 0), options="thick")
16 | # Line out
17 | tikz.line((0, 0), (1.414, 1.414), options="-o")
18 | tikz.arc((1, 0), 0, 45, radius=1, options="dashed")
19 |
20 | # Labels
21 | tikz.node((3.6, -0.2), text="$x$")
22 | tikz.node((-0.24, 3.53), text="$iy$")
23 | tikz.node((1.3, 0.4), text="$\\theta$")
24 | tikz.node((2.1, 1.7), text="$z = re^{i\\theta}$")
25 | tikz.node((-2, 0.3), text="Cut")
26 | ```
27 | which produces
28 |
29 |
30 | Here's another example of usings nodes to illustrate the concept of a multivariable function.
31 | ```python
32 | import tikzpy
33 |
34 | tikz = tikzpy.TikzPicture()
35 |
36 | arrow_len = 2
37 | box_width = 2
38 | # Lines and rectangles
39 | input_arrow = tikz.line((0, 0), (arrow_len, 0), options="->")
40 | box = tikz.rectangle_from_west(input_arrow.end, width=box_width, height=1)
41 | output_arrow = tikz.line(box.east, box.east + (arrow_len, 0), options="->")
42 |
43 | # Labels
44 | tikz.node((-1.2, 0), text="$(x_1, \dots, x_n)$")
45 | tikz.node(input_arrow.midpoint() + (0, 0.3), text="input")
46 | tikz.node(box.center, text="$f$")
47 | tikz.node(output_arrow.midpoint() + (0, 0.3), text="output")
48 | tikz.node((7.3, 0), text="$f(x_1, \dots, x_n)$")
49 | tikz.show()
50 | ```
51 |
52 |
53 |
--------------------------------------------------------------------------------
/docs/API_Documentation/plot_coordinates.md:
--------------------------------------------------------------------------------
1 | # PlotCoordinates
2 |
3 | ::: tikzpy.drawing_objects.plotcoordinates.PlotCoordinates
4 |
5 | ## Examples
6 | Introducing examples of `PlotCoordinates` gives us an opportunity to illustrate the optional parameter `action`. By default, `action` is `"draw"` (analogous to `\draw` in Tikz) so the code below
7 | ```python
8 | import tikzpy
9 |
10 | tikz = tikzpy.TikzPicture()
11 | points = [(2, 2), (4, 0), (1, -3), (-2, -1), (-1, 3)]
12 | plot = tikz.plot_coordinates(points) # action="draw" by default
13 | plot.plot_options = "smooth cycle, tension = 0.5"
14 | ```
15 | produces the image
16 |
17 |
18 |
19 | Alternatively we can set `action = "fill"` (analogous to `\fill` in Tikz) as in the code below
20 | ```python
21 | import tikzpy
22 |
23 | tikz = tikzpy.TikzPicture()
24 | points = [(2, 2), (4, 0), (1, -3), (-2, -1), (-1, 3)]
25 | plot = tikz.plot_coordinates(points, options="Blue", action="fill")
26 | plot.plot_options = "smooth cycle, tension = 0.5"
27 | ```
28 | to produce the image
29 |
30 |
31 |
32 | If we want both, we can set `action = "filldraw"` (analogous to `\filldraw` in Tikz)
33 | ```python
34 | import tikzpy
35 |
36 | tikz = tikzpy.TikzPicture()
37 | points = [(2, 2), (4, 0), (1, -3), (-2, -1), (-1, 3)]
38 | plot = tikz.plot_coordinates(points, options="Blue", action="filldraw")
39 | plot.options = "fill=ProcessBlue!50"
40 | plot.plot_options = "smooth cycle, tension = 0.5"
41 | ```
42 | which produces.
43 |
44 |
45 | Finally, we can set `action = "path"` (analogous to `\path` in Tikz), but as one would expect this doesn't draw anything.
46 |
47 | `PlotCoordinates` has methods `.shift()`, `.scale`, and `.rotate`, similar to the class `Line`, and the parameters behave similarly. These methods are more interestingly used on `PlotCoordinates` than on `Line`. For example, the code
48 | ```python
49 | import tikzpy
50 |
51 | tikz = tikzpy.TikzPicture()
52 | points = [(14.4, 3.2), (16.0, 3.6), (16.8, 4.8), (16.0, 6.8), (16.4, 8.8), (13.6, 8.8), (12.4, 7.6), (12.8, 5.6), (12.4, 3.6)]
53 |
54 | for i in range(0, 20):
55 | options = f"fill = {rainbow_colors(i)}, opacity = 0.7"
56 | # Requires \usetikzlibrary{hobby} here
57 | plot_options = "smooth, tension=.5, closed hobby"
58 | plot = tikz.plot_coordinates(points, options, plot_options)
59 | plot.scale((20 - i) / 20) # Shrink it
60 | plot.rotate(15 * i) # Rotate it
61 | ```
62 | generates the image
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/docs/API_Documentation/point.md:
--------------------------------------------------------------------------------
1 | # Point
2 |
3 | ::: tikzpy.drawing_objects.point.Point
4 |
5 | # Examples
6 | The `Point` class can be instantiated from a tuple or at least two `Number`s. One can also represent a point in 3D with this class.
7 |
8 | ```python
9 | >>> from tikzpy import Point
10 | >>> my_point = Point(-1, 2)
11 | >>> my_point.x
12 | -1
13 | >>> my_point.y
14 | 2
15 | ```
16 | You can also perform arithmetic with `Point` objects, either with other `Point` objects or with Python tuples. For example, the following are all valid.
17 | ```python
18 | >>> my_point + (1, 1) # Add it to another tuple
19 | Point(0, 3)
20 | >>> my_point + Point(2, 2) # Add it with another point object
21 | Point(1, 4)
22 | >>> 2 * my_point # Can also do my_point * 2
23 | Point(-2, 4)
24 | >>> my_point / 3
25 | Point(-0.33333333, 0.666666666)
26 | ```
27 |
28 | This allows you to write things like
29 | ```python
30 | >>> circle = tikz.circle((0,0), radius=3)
31 | >>> circle.center += (1, 1) # This is valid
32 | >>> circle.center /= 3 # Also valid
33 | ```
34 | and this feature becomes quite useful in drawings that are highly complex.
35 |
--------------------------------------------------------------------------------
/docs/API_Documentation/rectangle.md:
--------------------------------------------------------------------------------
1 | # Rectangle
2 |
3 | ::: tikzpy.drawing_objects.rectangle.Rectangle
4 |
5 | ## Example
6 | Rectangles are often used as a background to many figures; in this case,
7 | we create a fancy colored background.
8 |
9 | ```python
10 | from tikzpy import TikzPicture, Rectangle
11 | import math
12 |
13 | tikz = TikzPicture(center=True)
14 |
15 | yellow_box: Rectangle = tikz.rectangle_from_center((0, 0), width=7, height=5, options="rounded corners, Yellow!30",action="filldraw")
16 | # Params
17 | r = 2
18 | n_nodes = 7
19 | nodes = []
20 | # Draw the nodes
21 | for i in range(1, n_nodes + 1):
22 | angle = 2 * math.pi * i / n_nodes
23 | x = r * math.cos(angle)
24 | y = r * math.sin(angle)
25 | node = tikz.node((x, y), text=f"$A_{{{i}}}$")
26 | nodes.append(node)
27 |
28 | # Draw the lines between the nodes
29 | for i in range(len(nodes)):
30 | start = nodes[i].position
31 | end = nodes[(i + 1) % len(nodes)].position
32 | tikz.line(start, end, options="->, shorten >= 10pt, shorten <=10pt")
33 | tikz.show()
34 | ```
35 |
36 |
37 |
--------------------------------------------------------------------------------
/docs/API_Documentation/scope.md:
--------------------------------------------------------------------------------
1 | # Scope
2 |
3 | ::: tikzpy.tikz_environments.scope.Scope
4 |
5 | ::: tikzpy.tikz_environments.clip.Clip
--------------------------------------------------------------------------------
/docs/API_Documentation/tikz_picture.md:
--------------------------------------------------------------------------------
1 | # TikzPicture
2 |
3 | ::: tikzpy.tikz_environments.tikz_picture.TikzPicture
4 | options:
5 | inherited_members: true
6 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | # Welcome to Tikz-Python!
2 |
3 | {align=right width=40%}
4 |
5 | Tikz-Python is intended to be an easy-to-use, no-nonsense python package meant to serve as an interface to TikZ code. With Tikz-Python, you can have your beautiful vector graphics and eat it too! (erh, maybe).
6 |
7 |
8 | Install it below as follows:
9 | ```
10 | pip install tikz-python
11 | ```
12 |
13 | Want to see some nice examples of what this package can do? See [here](examples.md).
14 |
15 | ## Usage
16 | Suppose we want to create a sphere. We can achieve this as follows.
17 | ```python
18 | from tikzpy import TikzPicture
19 |
20 | tikz = TikzPicture() # Initialize empty canvas
21 | tikz.circle((0, 0), 3, options="thin, fill=orange!15")
22 |
23 | # Draw two arcs to give 3d-illusion
24 | tikz.arc((3, 0), 0, 180, x_radius=3, y_radius=1.5, options="dashed")
25 | tikz.arc((-3, 0), 180, 360, x_radius=3, y_radius=1.5)
26 |
27 | tikz.show() # Displays a pdf of the drawing to the user
28 | ```
29 | which produces
30 |
31 |
32 | ## Why Tikz-Python
33 |
34 | With Tikz-Python, you generate TikZ code by writing python code. And Python is much nicer than TeX.
35 |
36 | * Instead of spending a lot of time tediously writing messy, unreadable TikZ code to generate your desired figure,
37 | you can use TikZ-Python to quickly create your figure *as a Python script*. Your Python code will definitely be much
38 | more modular and extensible than the raw TikZ code you'd end up writing.
39 |
40 | * At any time, you can compile and look at your figure in a sandbox environment by calling `TikzPicture.show()`
41 |
42 | * With a sandbox environment for compilation, compiling will be faster versus you editing and re-compiling your tikz code directly in whatever 100-page document you're working in.
43 |
44 | * Once you're happy with your drawing, you can copy the generated TikZ code and paste it into your LaTeX document. Or, you can
45 | save your code to a file by calling `TikzPicture.write(file_destination)`.
46 |
47 | Additionally, Tikz-Python encodes lines, circles, rectangles, etc. as data structures. These data structures have useful properties and methods
48 | that can be used to create other drawings.
49 |
50 | For example, suppose I want to create a line and two labels at the ends. The code below achieves this
51 | ```python
52 | from tikzpy import TikzPicture
53 |
54 | tikz = TikzPicture()
55 | line = tikz.line((0, 0), (1, 1), options="thick, blue, o-o")
56 | start_node = tikz.node(line.start, options="below", text="Start!")
57 | end_node = tikz.node(line.end, options="above", text="End!")
58 | tikz.show() # Displays a pdf of the drawing to the user
59 | ```
60 | which produces
61 |
62 | Saving the line as a variable `line` allows us to pass in `line.start` and `line.end` into the node positions, so we don't have to type out the exact coordinates.
63 | This is great, because it makes our code more modular and therefore easier to change. With TikZ alone, you'd need to type out exact coordinates, and update every single one each time you make a minor adjustment to your code.
64 |
65 | If we were working in an interactive python shell with the code above, we would be able to see that these functions return classes with useful attributes:
66 | ```python
67 | >>> line.start
68 | (0,0)
69 | >>> line.end
70 | (1,1)
71 | >>> start_node.text
72 | "Start!"
73 | ```
74 | Additionally, you can `print` your tikz object to see the code generated
75 |
76 | ```python
77 | >>> print(tikz)
78 | \begin{tikzpicture}
79 | \draw[thick, blue, o-o] (0, 0) to (1, 1);
80 | \node[below] at (0, 0) { Start! };
81 | \node[above] at (1, 1) { End! };
82 | \end{tikzpicture}
83 | ```
84 | which you can then use to export to your project.
85 |
86 |
87 | ## Background
88 | TikZ is a wrapper of the TeX-based graphics package PGF (see [here](https://github.com/pgf-tikz/pgf/blob/master/tex/generic/pgf/frontendlayer/tikz/tikz.code.tex)), and it is commonly used in LaTeX documents to produce beautiful graphics.
89 | However, the power of TikZ comes with a tradeoff: it is extremely tedious to use, learn, understand, and iterate on.
90 |
91 | The main problem with Tikz is that even though Tikz is very powerful, it is often the case that
92 | nonexperts who use TikZ end up producing subpar images.
93 | The reason for this is because of the fact that in order to create beautiful images with TikZ, you also need to deeply understand
94 | LaTeX, TeX, PGF, and the history, bugs, cryptic error messages, and ridiculous quirks (and there are *many* quirks) of these languages.
95 | This takes years of practice and for the average person this is not realistic or desirable.
96 |
97 | This can be seen in research papers; even in high quality research papers, the graphics are usually not that great and it totally makes sense why.
98 | It's probably because the authors are too busy being an expert in their own field of work to sit down and read a 1300 page manual on TikZ.
99 |
100 | ## About
101 | I started this project after realizing
102 |
103 | * most TikZ code is repetitive.
104 |
105 | * TikZ was designed smart; it has an inherent object oriented pattern in its usage which we can exploit and automate.
106 |
107 | * I really hate writing TeX and TikZ code.
108 |
109 | I wrote the first version of this as a math undergraduate while writing a large set of notes. As a heavy user of LaTeX, TikZ, PGF, I knew how to design the project to enable efficient development of TikZ code.
110 | Then I became a professional software engineer, and I refactored the code to a modern python package while maintaining the original desired goals of the package.
111 |
112 |
113 |
114 |
--------------------------------------------------------------------------------
/docs/installation.md:
--------------------------------------------------------------------------------
1 | # Installation
2 |
3 |
4 | To install Tikz-Python, run
5 | ```
6 | pip install tikz-python
7 | ```
8 | In order to compile your drawings, you need an installation of `LaTeX`, with PDF and TikZ libraries and latexmk. Unless you decided to customize your LaTeX installation, all of these should already be on your system.
9 |
10 | If you don't have these on your system, you can of course still use Tikz-Python to generate Tikz code, which you can then take to another system with LaTeX installed to compile on.
11 |
--------------------------------------------------------------------------------
/docs/png/PlotCoords_rotate_Example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/PlotCoords_rotate_Example.png
--------------------------------------------------------------------------------
/docs/png/arc_ex_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/arc_ex_1.png
--------------------------------------------------------------------------------
/docs/png/arc_ex_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/arc_ex_2.png
--------------------------------------------------------------------------------
/docs/png/barycentric.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/barycentric.png
--------------------------------------------------------------------------------
/docs/png/basic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/basic.png
--------------------------------------------------------------------------------
/docs/png/blowup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/blowup.png
--------------------------------------------------------------------------------
/docs/png/blowup_ex.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/blowup_ex.png
--------------------------------------------------------------------------------
/docs/png/cantor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/cantor.png
--------------------------------------------------------------------------------
/docs/png/cauchy_residue_thm_arc_ex.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/cauchy_residue_thm_arc_ex.png
--------------------------------------------------------------------------------
/docs/png/cauchy_residue_thm_ex.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/cauchy_residue_thm_ex.png
--------------------------------------------------------------------------------
/docs/png/circle_center.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/circle_center.png
--------------------------------------------------------------------------------
/docs/png/circle_east.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/circle_east.png
--------------------------------------------------------------------------------
/docs/png/circle_ex_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/circle_ex_1.png
--------------------------------------------------------------------------------
/docs/png/circle_ex_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/circle_ex_2.png
--------------------------------------------------------------------------------
/docs/png/circle_intersections.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/circle_intersections.png
--------------------------------------------------------------------------------
/docs/png/circle_intersections_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/circle_intersections_2.png
--------------------------------------------------------------------------------
/docs/png/circle_north.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/circle_north.png
--------------------------------------------------------------------------------
/docs/png/circle_point_at_arg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/circle_point_at_arg.png
--------------------------------------------------------------------------------
/docs/png/circle_south.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/circle_south.png
--------------------------------------------------------------------------------
/docs/png/circle_west.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/circle_west.png
--------------------------------------------------------------------------------
/docs/png/circles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/circles.png
--------------------------------------------------------------------------------
/docs/png/connect_circles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/connect_circles.png
--------------------------------------------------------------------------------
/docs/png/des.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/des.png
--------------------------------------------------------------------------------
/docs/png/dots.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/dots.png
--------------------------------------------------------------------------------
/docs/png/draw_segments.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/draw_segments.png
--------------------------------------------------------------------------------
/docs/png/ellipse_ex_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/ellipse_ex_1.png
--------------------------------------------------------------------------------
/docs/png/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/favicon.png
--------------------------------------------------------------------------------
/docs/png/fully_connected_neural_network.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/fully_connected_neural_network.png
--------------------------------------------------------------------------------
/docs/png/geometry_figure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/geometry_figure.png
--------------------------------------------------------------------------------
/docs/png/grid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/grid.png
--------------------------------------------------------------------------------
/docs/png/integration_ex.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/integration_ex.png
--------------------------------------------------------------------------------
/docs/png/intersection_blobs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/intersection_blobs.png
--------------------------------------------------------------------------------
/docs/png/intersection_circles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/intersection_circles.png
--------------------------------------------------------------------------------
/docs/png/line_and_two_nodes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/line_and_two_nodes.png
--------------------------------------------------------------------------------
/docs/png/line_end.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/line_end.png
--------------------------------------------------------------------------------
/docs/png/line_ex_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/line_ex_1.png
--------------------------------------------------------------------------------
/docs/png/line_ex_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/line_ex_2.png
--------------------------------------------------------------------------------
/docs/png/line_midpoint.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/line_midpoint.png
--------------------------------------------------------------------------------
/docs/png/line_pos_a_t.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/line_pos_a_t.png
--------------------------------------------------------------------------------
/docs/png/line_start.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/line_start.png
--------------------------------------------------------------------------------
/docs/png/linear_transformation_ex_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/linear_transformation_ex_1.png
--------------------------------------------------------------------------------
/docs/png/linear_transformation_ex_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/linear_transformation_ex_2.png
--------------------------------------------------------------------------------
/docs/png/lorenz_ex.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/lorenz_ex.png
--------------------------------------------------------------------------------
/docs/png/nn_nodes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/nn_nodes.png
--------------------------------------------------------------------------------
/docs/png/node_ex_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/node_ex_1.png
--------------------------------------------------------------------------------
/docs/png/node_ex_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/node_ex_2.png
--------------------------------------------------------------------------------
/docs/png/plotcoordinates_ex_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/plotcoordinates_ex_1.png
--------------------------------------------------------------------------------
/docs/png/plotcoordinates_ex_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/plotcoordinates_ex_2.png
--------------------------------------------------------------------------------
/docs/png/plotcoordinates_ex_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/plotcoordinates_ex_3.png
--------------------------------------------------------------------------------
/docs/png/polar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/polar.png
--------------------------------------------------------------------------------
/docs/png/projective_cone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/projective_cone.png
--------------------------------------------------------------------------------
/docs/png/rectangle_ex_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/rectangle_ex_1.png
--------------------------------------------------------------------------------
/docs/png/roots_of_unity.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/roots_of_unity.png
--------------------------------------------------------------------------------
/docs/png/rotate_circles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/rotate_circles.png
--------------------------------------------------------------------------------
/docs/png/rotate_plot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/rotate_plot.png
--------------------------------------------------------------------------------
/docs/png/shift_plot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/shift_plot.png
--------------------------------------------------------------------------------
/docs/png/sphere_loop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/sphere_loop.png
--------------------------------------------------------------------------------
/docs/png/spiral.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/spiral.png
--------------------------------------------------------------------------------
/docs/png/test_region_and.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/test_region_and.png
--------------------------------------------------------------------------------
/docs/png/test_region_not.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/test_region_not.png
--------------------------------------------------------------------------------
/docs/png/test_region_or.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/test_region_or.png
--------------------------------------------------------------------------------
/docs/png/test_region_xor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/test_region_xor.png
--------------------------------------------------------------------------------
/docs/png/tikz_write_ex_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/tikz_write_ex_1.png
--------------------------------------------------------------------------------
/docs/png/tikz_write_ex_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/tikz_write_ex_2.png
--------------------------------------------------------------------------------
/docs/png/transformer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/transformer.png
--------------------------------------------------------------------------------
/docs/png/tutorial_imgs/line.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/tutorial_imgs/line.png
--------------------------------------------------------------------------------
/docs/png/tutorial_imgs/log_cut_step_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/tutorial_imgs/log_cut_step_1.png
--------------------------------------------------------------------------------
/docs/png/tutorial_imgs/log_cut_step_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/tutorial_imgs/log_cut_step_2.png
--------------------------------------------------------------------------------
/docs/png/tutorial_imgs/log_cut_step_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/tutorial_imgs/log_cut_step_3.png
--------------------------------------------------------------------------------
/docs/png/tutorial_imgs/neural_network_diagram.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/tutorial_imgs/neural_network_diagram.png
--------------------------------------------------------------------------------
/docs/png/tutorial_imgs/neural_network_step_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/tutorial_imgs/neural_network_step_1.png
--------------------------------------------------------------------------------
/docs/png/tutorial_imgs/neural_network_step_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/tutorial_imgs/neural_network_step_2.png
--------------------------------------------------------------------------------
/docs/png/tutorial_imgs/neural_network_step_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/tutorial_imgs/neural_network_step_3.png
--------------------------------------------------------------------------------
/docs/png/tutorial_imgs/neural_network_step_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/tutorial_imgs/neural_network_step_4.png
--------------------------------------------------------------------------------
/docs/png/tutorial_imgs/neural_network_step_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/docs/png/tutorial_imgs/neural_network_step_5.png
--------------------------------------------------------------------------------
/docs/stylesheets/extra.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --md-primary-fg-color: #222b3a;
3 | --md-primary-fg-color--light: #5d6cc0;
4 | --md-primary-fg-color--dark: #303fa1;
5 | --md-primary-bg-color: #fff;
6 | --md-primary-bg-color--light: #ffffffb3;
7 | --md-accent-fg-color: #526cfe;
8 | --md-accent-fg-color--transparent: #526cfe1a;
9 | --md-accent-bg-color: #fff;
10 | --md-accent-bg-color--light: #ffffffb3
11 | }
12 |
13 |
14 | .md-content {
15 | --md-typeset-a-color: #09bfdd;
16 | }
17 |
18 |
--------------------------------------------------------------------------------
/examples/_run_all_examples.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Usage: If you want to run, compile, and view all the pdfs:
4 | # ./_run_all_examples.sh show
5 | # If you want to simply run the python code but not compile or view the pdfs
6 | # ./_run_all_examples.sh
7 |
8 | for pyfile in `find . -name "*.py" -type f`; do
9 | if [ "$1" = "show" ]
10 | then
11 | python3 $pyfile || break # Run the .py
12 | else
13 | sed -i "" "s|^tikz.show()|#tikz.show()|g" $pyfile # Comment out tikz.show(). Don't want to see the pdf.
14 | python3 $pyfile || break # Run the .py
15 | sed -i "" "s|^#tikz.show()|tikz.show()|g" $pyfile # Uncomment tikz.show()
16 | fi
17 | done
--------------------------------------------------------------------------------
/examples/barycentric/barycentric.py:
--------------------------------------------------------------------------------
1 | #!/bin/bash/python3
2 | import statistics
3 | import queue
4 | from tikzpy import TikzPicture
5 | from tikzpy.colors import xcolors
6 |
7 |
8 | def nth_subdivision(n):
9 | iters = 0
10 | for i in range(n):
11 | tikz = TikzPicture(center=True)
12 | if n > 0:
13 | iters += 6 ** (i)
14 | barycentric_subdivision(iters, tikz)
15 | else:
16 | barycentric_subdivision(0, tikz)
17 |
18 | tikz.show()
19 |
20 |
21 | def barycentric_subdivision(iterations, tikz):
22 | pt_one = (0, 0)
23 | pt_two = (6, 0)
24 | pt_three = (3, 4.24)
25 |
26 | tikz.line(pt_one, pt_two)
27 | tikz.line(pt_two, pt_three)
28 | tikz.line(pt_three, pt_one)
29 |
30 | triangles = queue.Queue() # queue of lists of tuples
31 | triangles.put([pt_one, pt_two, pt_three]) # Put goes to the end [... {]}
32 |
33 | while not triangles.empty() and iterations > 0:
34 | iterations -= 1
35 | coords = triangles.get() # Grabs from the front {[} ...]
36 | new = medians(coords, tikz, iterations)
37 | for tri in new:
38 | triangles.put(tri)
39 |
40 |
41 | def medians(coords, tikz, iteration):
42 | centroid = (
43 | statistics.mean([x[0] for x in coords]),
44 | statistics.mean([x[1] for x in coords]),
45 | )
46 | new_triangle = [] # save coords of new triangles
47 |
48 | # get median coordinates for all edges
49 | midpts = []
50 | for i in range(-len(coords), 0):
51 | ax, ay = coords[i]
52 | bx, by = coords[i + 1]
53 | cx, cy = coords[i + 2]
54 |
55 | # get median coords, draw median
56 | x = statistics.mean([bx, cx])
57 | y = statistics.mean([by, cy])
58 | tikz.line(
59 | (ax, ay),
60 | (x, y),
61 | options=f"color={xcolors(iteration)}, line width=0.1mm",
62 | )
63 | midpts.append((x, y))
64 |
65 | # get coords of all new triangles created
66 | for i in range(len(coords)):
67 | new_triangle.append([centroid, midpts[i], coords[i - 1]])
68 | new_triangle.append([centroid, midpts[i], coords[i - 2]])
69 |
70 | return new_triangle
71 |
72 | if __name__ == "__main__":
73 | nth_subdivision(3)
74 |
--------------------------------------------------------------------------------
/examples/basic/basic_ex.py:
--------------------------------------------------------------------------------
1 | #!/bin/bash/python3
2 | from tikzpy import TikzPicture # Import the class TikzPicture
3 |
4 | """ Draws a circle and two ellipses to create the illusion of sphere.
5 | """
6 |
7 | if __name__ == "__main__":
8 | tikz = TikzPicture()
9 | tikz.circle((0, 0), 3, options="thin, fill=orange!15")
10 |
11 | arc_one = tikz.arc((3, 0), 0, 180, x_radius=3, y_radius=1.5, options=f"dashed")
12 | arc_two = tikz.arc((-3, 0), 180, 360, x_radius=3, y_radius=1.5)
13 |
14 | tikz.show(quiet=True) # Displays a pdf of the drawing to the user
15 |
--------------------------------------------------------------------------------
/examples/blowup/blowup.py:
--------------------------------------------------------------------------------
1 | #!/bin/bash/python3
2 | from math import pi, cos, sin, tan, atan
3 | import numpy as np
4 | from tikzpy import TikzPicture
5 |
6 | """ Plots the blowup at a point.
7 | """
8 |
9 | tikz = TikzPicture(center=True)
10 | tikz.set_tdplotsetmaincoords(65, 25)
11 | tikz.options = "tdplot_main_coords"
12 |
13 | # The blowup
14 | def blowup(r, t):
15 | theta = 2 * atan(t * 2 / pi)
16 | return r * cos(theta), r * sin(theta), 6 * atan(tan(theta) / 6)
17 |
18 |
19 | if __name__ == "__main__":
20 | # Parameters for surface
21 | vmin = -pi / 2 + 0.1
22 | vmax = pi / 2 - 0.1
23 | umin = -5
24 | umax = 5
25 |
26 | # Draws the gray vertical line
27 | for j in np.linspace(umin, umax, 40):
28 | points = []
29 | for i in np.linspace(vmin, vmax, 50):
30 | points.append(blowup(j, i))
31 | tikz.plot_coordinates(points, options="gray!10, thin", plot_options="smooth")
32 |
33 | # Draws the main blue vertical lines for visual aid
34 | for j in np.linspace(umin, umax, 10):
35 | points = []
36 | for i in np.linspace(vmin, vmax, 50):
37 | points.append(blowup(j, i))
38 | tikz.plot_coordinates(points, options="ProcessBlue!70", plot_options="smooth")
39 |
40 | # Draws the horizontal lines
41 | for i in np.linspace(vmin, vmax, 50):
42 | new_points = []
43 | for j in np.linspace(umin, umax, 10):
44 | new_points.append(blowup(j, i))
45 | tikz.plot_coordinates(new_points, options="ProcessBlue!70", plot_options="smooth")
46 |
47 | tikz.show()
48 |
--------------------------------------------------------------------------------
/examples/cantor/cantor.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from tikzpy import TikzPicture, R2_Space
3 |
4 |
5 | def cantor(n):
6 | """A wrapper to perform cantor subdivision on [0, 1]."""
7 | return [0] + subdivide(0, 1, n) + [1]
8 |
9 |
10 | def subdivide(x_1, x_2, n):
11 | """Performs the n-th cantor subdivision of the interval (x_1, x_2), a subset [0, 1]"""
12 | if n == 0:
13 | return []
14 |
15 | new_x_1 = 2 * (x_1 / 3) + x_2 / 3
16 | new_x_2 = x_1 / 3 + 2 * (x_2 / 3)
17 | return (
18 | subdivide(x_1, new_x_1, n - 1)
19 | + [new_x_1, new_x_2]
20 | + subdivide(new_x_2, x_2, n - 1)
21 | )
22 |
23 |
24 | if __name__ == "__main__":
25 | tikz = TikzPicture()
26 | s = 4 # Scale
27 |
28 | # Set up xy-plane
29 | xy_plane = R2_Space(x_interval=(0, s), y_interval=(0, s))
30 | xy_plane.x_axis_options = "Gray!30, ->"
31 | xy_plane.y_axis_options = "Gray!30, ->"
32 | tikz.draw(xy_plane)
33 |
34 | # Collect (x,y) cantor data
35 | x = np.array(cantor(10))
36 | y = np.cumsum(np.ones(len(x)) / (len(x) - 2)) - 1 / (len(x) - 2)
37 | y[-1] = 1
38 |
39 | # Plot it
40 | points = tikz.plot_coordinates(list(zip(x, y)), options="ProcessBlue")
41 | points.scale_(4)
42 | tikz.show()
43 |
--------------------------------------------------------------------------------
/examples/cauchy_residue_thm/cauchy_residue_thm.py:
--------------------------------------------------------------------------------
1 | #!/bin/bash/python3
2 | from tikzpy import TikzPicture
3 | from tikzpy.styles import arrows_along_path_style
4 |
5 | """ Illustrates a use case of Cauchy's Residue Theorem. In this case
6 | we have two singularities that occur within the semicircle.
7 | """
8 |
9 | if __name__ == "__main__":
10 | tikz = TikzPicture()
11 |
12 | points = [
13 | (-3, -0.5),
14 | (-1, -1.5),
15 | (2, -1.5),
16 | (4, -1.5),
17 | (5, 0),
18 | (3, 2.4),
19 | (-1, 2.6),
20 | (-3, 2),
21 | ]
22 |
23 | tikz.add_styles(*arrows_along_path_style)
24 | tikz.options = "thick"
25 |
26 | # Draws the main boundary
27 | plot = tikz.plot_coordinates(
28 | options="arrows_along_path=red",
29 | plot_options="smooth, tension=.5, closed hobby",
30 | points=points,
31 | action="draw",
32 | )
33 |
34 | # Draws the inner circles
35 | singularity_1 = tikz.circle((3, -0.3), 0.7, "arrows_along_path=blue")
36 | singularity_2 = tikz.circle((1.3, 1.3), 0.7, "arrows_along_path=blue")
37 | singularity_3 = tikz.circle((-0.4, -0.2), 0.7, "arrows_along_path=blue")
38 | singularity_4 = tikz.circle((-2, 1.1), 0.7, "arrows_along_path=blue")
39 |
40 | # Draws the paths that connect the circles
41 | tikz.line((3.7, -0.5), (4.6, -1), options="bend left, <-")
42 | tikz.line((1.4, 0.6), (2.3, -0.3), options="bend right, <-")
43 | tikz.line((-0.25, 0.5), (0.6, 1.4), options="bend left, <-")
44 | tikz.line((-1.29, 1.1), (-0.6, 0.46), options="bend left, <-")
45 | tikz.line((-2.5, 1.6), (-2.8, 2.15), options="bend left, ->")
46 |
47 | # Draws and labels the points a_1, a_2, a_3, and a_4.
48 | for ind, singularity in enumerate(
49 | [singularity_1, singularity_2, singularity_3, singularity_4]
50 | ):
51 | tikz.circle(singularity.center, 0.05, action="fill")
52 | tikz.node(singularity.center, options="right", text=f"$a_{ind+1}$")
53 |
54 | tikz.show()
55 |
--------------------------------------------------------------------------------
/examples/cauchy_residue_thm/cauchy_residue_thm_arc.py:
--------------------------------------------------------------------------------
1 | #!/bin/bash/python3
2 | from tikzpy import TikzPicture
3 | from tikzpy.styles import arrows_along_path_style
4 |
5 | """ Illustrates Cauchy's Residue Theorem, which is a statement about integrating over
6 | finitely many singularities
7 | """
8 |
9 | if __name__ == "__main__":
10 | tikz = TikzPicture()
11 | tikz.add_styles(*arrows_along_path_style)
12 | tikz.options = "thick"
13 |
14 | # x and y axes
15 | x_axis = tikz.line((-4, 0), (4, 0), options="->")
16 | y_axis = tikz.line((0, -1), (0, 4), options="->")
17 | tikz.node(x_axis.end, options="below", text="$\\mathbb{R}$")
18 | tikz.node(y_axis.end, options="left", text="$i$")
19 |
20 | # Red Semicircle
21 | arc = tikz.arc(
22 | (0, 0),
23 | 0,
24 | 180,
25 | radius=2,
26 | options="arrows_along_path=red, red",
27 | draw_from_start=False,
28 | )
29 | # Draws the blue path [-R, R]
30 | bottom_path = tikz.line((-2, 0), (2, 0), options="blue, arrows_along_path=blue")
31 | tikz.node(bottom_path.start - (0, 0.5), text="$R$")
32 | tikz.node(bottom_path.end - (0, 0.5), text="$-R$")
33 |
34 | # % Draws the points z_1, z_2 of interest
35 | circle_1 = tikz.circle((0.7, 0.7), 0.05, action="fill")
36 | circle_2 = tikz.circle((-0.7, 0.7), 0.05, action="fill")
37 | tikz.node(circle_1.center, options="right", text="$z_1$")
38 | tikz.node(circle_2.center, options="left", text="$z_2$")
39 |
40 | tikz.show()
41 |
--------------------------------------------------------------------------------
/examples/circle_intersections/circle_intersections.py:
--------------------------------------------------------------------------------
1 | from tikzpy import TikzPicture, Point
2 | from tikzpy.drawing_objects.drawing_utils import calc_intersection
3 |
4 | tikz = TikzPicture(center=True)
5 |
6 | # Draw nodes A, B
7 | A = tikz.node((0,0), options="left", text="$A$")
8 | B = tikz.node((1.25, 0.25), options="right", text="$B$")
9 |
10 | # Draw circle about A, B
11 | AB_dist = A.position.distance(B.position) # Calc distance between A and B
12 | circle_A = tikz.circle(A.position, radius=AB_dist)
13 | circle_B = tikz.circle(B.position, radius=AB_dist)
14 |
15 | # Calculate intersection points between two circles
16 | intersections = calc_intersection(circle_A, circle_B)
17 |
18 | # Draw nodes C, D, E
19 | C = tikz.node(intersections[1], options="above", text="$C$")
20 | D = tikz.node(circle_A.west, text="$D$", options="left")
21 | E = tikz.node(circle_B.east, text="$E$", options="right")
22 |
23 | # Draw triangle
24 | tikz.line(A.position, C.position, options="red")
25 | tikz.line(B.position, C.position, options="red")
26 | tikz.line(A.position, B.position)
27 | tikz.show(quiet=False)
28 |
29 |
--------------------------------------------------------------------------------
/examples/circle_intersections_2/circle_intersections_2.py:
--------------------------------------------------------------------------------
1 | from tikzpy import TikzPicture
2 | from tikzpy.drawing_objects.drawing_utils import calc_intersection
3 |
4 | tikz = TikzPicture(center=True)
5 |
6 | # Draw nodes A, B
7 | A = tikz.node((0,0), options="left", text="$A$")
8 | B = tikz.node((1.25, 0.25), options="right", text="$B$")
9 |
10 | # Draw circle about A, B
11 | AB_dist = A.position.distance(B.position) # Calc distance between A and B
12 | circle_A = tikz.circle(A.position, radius=AB_dist)
13 | circle_B = tikz.circle(B.position, radius=AB_dist)
14 |
15 | # Calculate intersection points between two circles
16 | intersections = calc_intersection(circle_A, circle_B)
17 |
18 | # Draw nodes C, D, E
19 | C = tikz.node(intersections[1], options="above", text="$C$")
20 | C_prime = tikz.node(intersections[0], options="below", text="$C'$")
21 | D = tikz.node(circle_A.west, text="$D$", options="left")
22 | E = tikz.node(circle_B.east, text="$E$", options="right")
23 |
24 | # Draw lines
25 | line_a = tikz.line(A.position, B.position)
26 | line_b = tikz.line(C.position, C_prime.position, options="red")
27 |
28 | # Draw intersection
29 | intersections = calc_intersection(line_a, line_b)
30 | tikz.node(intersections[0], options="fill=red,inner sep=1pt")
31 |
32 | tikz.show(quiet=False)
33 |
34 |
--------------------------------------------------------------------------------
/examples/circles/circles.py:
--------------------------------------------------------------------------------
1 | #!/bin/bash/python3
2 | import numpy as np
3 | from tikzpy import TikzPicture
4 |
5 | if __name__ == "__main__":
6 | tikz = TikzPicture(center=True)
7 |
8 | for i in np.linspace(0, 1, 30):
9 | point = (np.sin(2 * np.pi * i), np.cos(2 * np.pi * i))
10 |
11 | # Create four circles of different radii with center located at point
12 | tikz.circle(point, 2, "ProcessBlue")
13 | tikz.circle(point, 2.2, "ForestGreen")
14 | tikz.circle(point, 2.4, "red") # xcolor Red is very ugly
15 | tikz.circle(point, 2.6, "Purple")
16 |
17 | tikz.show()
18 |
--------------------------------------------------------------------------------
/examples/controls/controls.py:
--------------------------------------------------------------------------------
1 | #!/bin/bash/python3
2 | from tikzpy import TikzPicture
3 | from tikzpy.colors import rainbow_colors
4 |
5 | """ Draws a series of rainbow lines in a manner such that their control points are shifted.
6 | """
7 |
8 | tikz = TikzPicture()
9 | for i in range(0, 15):
10 | line = tikz.line((i, 0), (0, 5))
11 | line.options = f"color={rainbow_colors(i)}"
12 | line.control_pts = [(i - 2, -1), (i + 2, -2)]
13 |
14 | tikz.show()
15 |
--------------------------------------------------------------------------------
/examples/des/des.py:
--------------------------------------------------------------------------------
1 | from tikzpy import TikzPicture
2 |
3 | if __name__ == "__main__":
4 | tikz = TikzPicture(center=True, options=">=stealth, thick")
5 |
6 | # Plaintext
7 | plaintext = tikz.rectangle((0, 0), 4, 0.8)
8 | tikz.node(plaintext.center, text="Plaintext")
9 |
10 | to_IP = tikz.line(plaintext.south, plaintext.south - (0, 0.5), options="->")
11 | # IP
12 | IP = tikz.rectangle_from_north(to_IP.end, height=0.7, width=2, options="rounded corners") # Rectangle who's .north position is the end of IP
13 | tikz.node(IP.center, text="IP")
14 | vertical = tikz.line(IP.south, IP.south - (0, 0.8))
15 |
16 | # Horizontal lines
17 | h1 = tikz.line(vertical.end, vertical.end - (5, 0))
18 | h2 = tikz.line(vertical.end, vertical.end + (5, 0))
19 |
20 | # To the blocks
21 | to_left_block = tikz.line(h1.end, h1.end - (0, 0.5), options="->")
22 | to_right_block = tikz.line(h2.end, h2.end - (0, 0.5), options="->")
23 |
24 | def DES_round(to_left_block, to_right_block, left_label, right_label, K_i=None, dotted=False, is_last_block=False):
25 | # Left block
26 | L_0 = tikz.rectangle_from_north(to_left_block.end, height=1, width=5)
27 | tikz.node(L_0.center, text=left_label)
28 |
29 | # Right block
30 | R_0 = tikz.rectangle_from_north(to_right_block.end, height=1, width=5)
31 | tikz.node(R_0.center, text=right_label)
32 |
33 | # Line to the XOR symbol
34 | to_xor = tikz.line(L_0.south, L_0.south - (0, 0.5))
35 |
36 | # Draw the XOR symbol
37 | XOR = tikz.circle(to_xor.end - (0, 0.25), radius=0.25)
38 | tikz.line(XOR.north, XOR.south)
39 | tikz.line(XOR.west, XOR.east)
40 |
41 | # Line to the dot
42 | to_dot = tikz.line(R_0.south, (R_0.south.x, XOR.center.y))
43 | # Draw the dot symbol
44 | dot = tikz.circle(to_dot.end, radius=0.1, options="fill")
45 |
46 | # Function f
47 | function_f = tikz.circle((dot.center + XOR.center) / 2 , radius = 0.5)
48 | tikz.node(function_f.center, text="$f$")
49 |
50 | # Line from dot to function f
51 | to_f = tikz.line(dot.west, function_f.east, options="->")
52 | # Line from function f to XOR symbol
53 | from_f = tikz.line(function_f.west, XOR.east, options="->")
54 |
55 | # From XOR to the right block
56 | from_XOR = tikz.line(XOR.south, (XOR.south.x, dot.south.y - 0.5))
57 | XOR_diagonal = tikz.line(from_XOR.end, (dot.center.x, from_XOR.end.y - 0.75))
58 | to_next_right_block = tikz.line(XOR_diagonal.end, XOR_diagonal.end - (0, 0.25), options="->")
59 |
60 | # From dot to left block block
61 | from_dot = tikz.line(dot.south, dot.south - (0, 0.5))
62 | dot_diagonal = tikz.line(from_dot.end, (XOR.center.x, from_dot.end.y - 0.75))
63 | to_next_left_block = tikz.line(dot_diagonal.end, dot_diagonal.end - (0, 0.25), options="->")
64 |
65 | # Dot the diagonal lines to show there are hidden steps
66 | if dotted:
67 | XOR_diagonal.options = "dotted"
68 | dot_diagonal.options = "dotted"
69 |
70 | # Incoming key
71 | if K_i is not None:
72 | joint = tikz.line(from_dot.midpoint() + (1, -0.2), (to_f.midpoint().x, from_dot.midpoint().y - 0.2))
73 | tikz.line(joint.end, function_f.point_at_arg(-30), options="->")
74 | tikz.node(joint.start + (0.3, 0), text=K_i)
75 |
76 | # If last block, straighten the diagonals so that they are vertical lines
77 | if is_last_block:
78 | XOR_diagonal.end = (XOR_diagonal.start.x, XOR_diagonal.end.y)
79 | dot_diagonal.end = (dot_diagonal.start.x, dot_diagonal.end.y)
80 |
81 |
82 | return to_next_left_block, to_next_right_block
83 |
84 |
85 | next_left_block, next_right_block = DES_round(to_left_block, to_right_block, "$L_0$", "$R_0$", "$K_1$" )
86 | next_left_block, next_right_block = DES_round(next_left_block, next_right_block, "$L_1=R_0$", "$R_1 = L_0 \oplus f(R_0, K_1)$", "$K_2$")
87 | next_left_block, next_right_block = DES_round(next_left_block, next_right_block, "$L_2=R_1$", "$R_2 = L_1 \oplus f(R_1, K_2)$", dotted=True)
88 | next_left_block, next_right_block = DES_round(next_left_block, next_right_block, "$L_{15}=R_{14}$", "$R_{15} = L_{14} \oplus f(R_{14}, K_{15})$", "$K_{16}$", is_last_block=True)
89 |
90 | # Left block
91 | L_0 = tikz.rectangle_from_north(next_left_block.end, height=1, width=5)
92 | tikz.node(L_0.center, text="$R_{16} = L_{15}\oplus f(R_{15}, K_{16})$")
93 |
94 | # Right block
95 | R_0 = tikz.rectangle_from_north(next_right_block.end, height=1, width=5)
96 | tikz.node(R_0.center, text="$L_{16} = R_{15}$")
97 |
98 | # Leaving the blocks
99 | from_left_block = tikz.line(L_0.south, L_0.south - (0, 0.5))
100 | from_right_block = tikz.line(R_0.south, R_0.south - (0, 0.5))
101 |
102 | # Horizontal lines to the middle
103 | h1 = tikz.line(from_left_block.end, (to_IP.end.x, from_left_block.end.y))
104 | h2 = tikz.line(from_right_block.end, (to_IP.end.x, from_right_block.end.y))
105 |
106 | # Line to IP inverse
107 | to_IP_inv = tikz.line(h1.end, h1.end - (0, 0.5), options="->")
108 | IP_inv = tikz.rectangle_from_north(to_IP_inv.end, height=0.7, width=2, options="rounded corners")
109 |
110 | # IP rectangle
111 | tikz.node(IP_inv.center, text="$\\text{IP}^{-1}$")
112 | to_cipher_text = tikz.line(IP_inv.south, IP_inv.south - (0, 0.5), options="->")
113 |
114 | # Ciphertext rectangle
115 | cipher_text = tikz.rectangle_from_north(to_cipher_text.end, height=0.8, width=4)
116 | tikz.node(cipher_text.center, text="Ciphertext")
117 | tikz.show()
118 |
119 |
120 |
--------------------------------------------------------------------------------
/examples/geometry_figure/geometry_figure.py:
--------------------------------------------------------------------------------
1 | import math
2 | from tikzpy import TikzPicture, Point, Line
3 | from tikzpy.drawing_objects.drawing_utils import calc_intersection
4 |
5 |
6 | def draw_point(point, text, label_spacing = 0.3):
7 | tikz.circle(point, radius=0.05, options="fill=black")
8 | angle = ref_angle(Point(point))
9 | x_spacing = label_spacing * math.cos(math.radians(angle))
10 | y_spacing = label_spacing * math.sin(math.radians(angle))
11 | tikz.node(Point(point) + (x_spacing, y_spacing), text=text)
12 |
13 | def ref_angle(point):
14 | return math.degrees(math.atan2(point.y, point.x))
15 |
16 | def angle_between_lines(line_a, line_b):
17 | m1 = line_a.slope()
18 | m2 = line_b.slope()
19 | return math.degrees(math.atan(abs((m2 - m1) / (1 + m1 * m2))))
20 |
21 | def arc_between_lines(line_a, line_b, radius):
22 | intersection = calc_intersection(line_a, line_b)[0]
23 | a = ref_angle(line_a.end - Point(intersection))
24 | b = angle_between_lines(line_a, line_b)
25 | tikz.arc(intersection, a, a + b, radius=radius, draw_from_start=False)
26 |
27 | if __name__ == "__main__":
28 | radius = 3
29 | tikz = TikzPicture(center=True)
30 | circle = tikz.circle((0,0), radius)
31 |
32 | # Points on circumference
33 | A = circle.point_at_arg(110)
34 | B = circle.point_at_arg(315)
35 | C = circle.point_at_arg(70)
36 | D = circle.point_at_arg(215)
37 |
38 | lines = tikz.draw_segments([A, B, C, D])
39 |
40 | X = lines[(D, A)].pos_at_t(0.65)
41 |
42 | # Draw intersections
43 | intersections = calc_intersection(lines[(A, B)], lines[(C, D)])
44 | M = intersections[0]
45 |
46 | XM_line = tikz.line(X, M)
47 | intersections = calc_intersection(lines[(B, C)], XM_line)
48 | Y = intersections[0]
49 |
50 | MY_line = tikz.line(M, Y)
51 | intersections = calc_intersection(MY_line, circle)
52 | P, Q = intersections[0], intersections[1]
53 | tikz.line(P, X)
54 | tikz.line(Y, Q)
55 |
56 | # Arc between MY and MC
57 | arc_between_lines(MY_line, Line(M, C), 0.55)
58 | arc_between_lines(MY_line, Line(M, C), 0.5)
59 |
60 | # Arc between MA and MX
61 | arc_between_lines(Line(M, A), Line(M, X), 0.35)
62 |
63 | # Arc between MX and MD
64 | arc_between_lines(Line(M, X), Line(M, D), 0.55)
65 | arc_between_lines(Line(M, X), Line(M, D), 0.5)
66 |
67 | # Arc between DM and DX
68 | arc_between_lines(Line(D, M), Line(D, X), 0.55)
69 | arc_between_lines(Line(D, M), Line(D, X), 0.5)
70 |
71 | # Arc between BY and BM
72 | arc_between_lines(Line(B, Y), Line(B, M), 0.55)
73 | arc_between_lines(Line(B, Y), Line(B, M), 0.5)
74 |
75 | # Arc between MB and MY
76 | arc_between_lines(Line(M, B), Line(M, Y), 0.35)
77 |
78 | # Draw points
79 | draw_point(A, "$A$",)
80 | draw_point(B, "$B$",)
81 | draw_point(C, "$C$",)
82 | draw_point(D, "$D$",)
83 | draw_point(X, "$X$",)
84 | draw_point(Y, "$Y$")
85 | draw_point(M, "$M$", label_spacing=0.5)
86 | draw_point(P, "$P$")
87 | draw_point(Q, "$Q$")
88 |
89 | tikz.show()
90 |
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/examples/line_and_two_nodes/line_and_two_nodes.py:
--------------------------------------------------------------------------------
1 | #!/bin/bash/python3
2 | from tikzpy import TikzPicture
3 |
4 | """ Draws a line and two nodes.
5 | """
6 |
7 | if __name__ == "__main__":
8 | tikz = TikzPicture()
9 | line = tikz.line((0, 0), (1, 1), options="thick, blue, o-o")
10 | start_node = tikz.node(line.start, options="below", text="Start!")
11 | end_node = tikz.node(line.end, options="above", text="End!")
12 |
13 | tikz.show()
14 |
--------------------------------------------------------------------------------
/examples/linear_model/linear_model.py:
--------------------------------------------------------------------------------
1 | #!/bin/bash/python3
2 | import math
3 | from tikzpy import TikzPicture, Point
4 | tikz = TikzPicture()
5 | radius = 0.25
6 |
7 | if __name__ == "__main__":
8 | x_2 = 6
9 | y_2 = 2
10 | epsilon = 0.2
11 | for i in range(5):
12 | x_1 = 0
13 | y_1 = 4 - i
14 | if y_1 - y_2 == 0:
15 | theta = math.pi/2
16 | else:
17 | theta = math.atan(abs(x_2 - x_1) / abs(y_1 - y_2))
18 | if y_2 > y_1:
19 | start = (x_1 + radius * math.sin(theta), y_1 + radius* math.cos(theta))
20 | end = (x_2 - radius * math.sin(theta), y_2 - radius * math.cos(theta))
21 | else:
22 | start = (x_1 + radius * math.sin(theta), y_1 - radius* math.cos(theta))
23 | end = (x_2 - radius * math.sin(theta), y_2 + radius * math.cos(theta))
24 |
25 | line = tikz.line(start, end, options="->")
26 | tikz.circle((x_1, y_1), radius)
27 | # Draw the input x_i
28 | tikz.node((x_1, y_1 + radius + epsilon), text=f"$x_{i}$")
29 | # Draw the weight w_i
30 | tikz.node(line.pos_at_t(0.3), options="above", text=f"$w_{i}$")
31 |
32 | # Draw the output node
33 | tikz.circle((x_2, y_2), radius)
34 | # Draw label for output node
35 | tikz.node((x_2, y_2 + radius + epsilon), text="$y$")
36 | tikz.show()
37 |
--------------------------------------------------------------------------------
/examples/linear_transformations/linear_transformations.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from tikzpy import TikzPicture
3 | from tikzpy.colors import rainbow_colors
4 |
5 | """ Plots the image of a 3 x 2 matrix in R^3 acting on R^2.
6 | Vectors in R^3 which lie below the positive Z-axis are drawn with lower opacity to aid the eye.
7 |
8 | Usage: Plug a 3x2 numpy array into plot_linear_transformation.
9 | Ex:
10 | >>> matrix = np.array([[0, 1], [1, 1], [0, 1]])
11 | >>> plot_linear_transformation(matrix)
12 |
13 | >>> matrix = np.array([[2, 0], [1, 1], [1, 1]])
14 | >>> plot_linear_transformation(matrix)
15 |
16 | >>> matrix = np.array([[2, 0], [1, 0], [1, 1]])
17 | >>> plot_linear_transformation(matrix)
18 | etc.
19 | """
20 |
21 |
22 | def build_scene():
23 | """Creates a Tikz Environment with two scope environments displayed side by side.
24 | The left scope environment displays R^2, while the right scope environment displays R^3.
25 | """
26 | tikz = TikzPicture(options=">=stealth")
27 |
28 | # xy plane
29 | xy_plane = tikz.scope(options="xshift=-8cm") # 2D plane
30 | axis_len = 2.5 # 2D axes length
31 | xy_plane.line((-axis_len, 0), (axis_len, 0)).add_node(
32 | options="right, ->", text="$x$"
33 | ) # X axis
34 | xy_plane.line((0, -axis_len), (0, axis_len)).add_node(
35 | options="above, ->", text="$y$"
36 | ) # Y axis
37 |
38 | # xyz space
39 | tikz.set_tdplotsetmaincoords(60, 110) # 3D perspective
40 | xyz_space = tikz.scope("tdplot_main_coords")
41 | O_xyz = (0, 0, 0) # 3D origin
42 | axis_len = 5 # 3D axes length
43 | X = (axis_len, 0, 0)
44 | Y = (0, axis_len - 1, 0)
45 | Z = (0, 0, axis_len - 2)
46 | # X, Y, Z axes
47 | xyz_space.line(O_xyz, X).add_node(options="below, ->", text="$x$") # X axis
48 | xyz_space.line(O_xyz, Y).add_node(options="right, ->", text="$y$") # Y axis
49 | xyz_space.line(O_xyz, Z).add_node(options="above, ->", text="$z$") # Z axis
50 |
51 | return tikz, xy_plane, xyz_space
52 |
53 |
54 | # Main function
55 | def plot_linear_transformation(matrix, num_vecs=40):
56 | """Plots a list of vectors in 2D and then displays their matrix product in 3D."""
57 |
58 | tikz, xy_plane, xyz_space = build_scene()
59 | O_xy = (0, 0) # 2D origin
60 | O_xyz = (0, 0, 0) # 3D origin
61 |
62 | for i in np.linspace(0, 2 * np.pi, num_vecs):
63 | i_x = 2 * np.cos(i)
64 | i_y = 2 * np.sin(i)
65 | color = "color=" + rainbow_colors(int(i / (2 * np.pi) * num_vecs))
66 | xy_plane.line(O_xy, (i_x, i_y), options=f"->, {color}")
67 |
68 | M = tuple(np.matmul(matrix, np.array([i_x, i_y]))) # The matrix calculation
69 |
70 | vec = xyz_space.line(O_xyz, M, options=f"->, {color}")
71 | if M[2] < 0: # If the Z-coordinate is negative, color it with low opacity
72 | vec.options += ", opacity=0.2"
73 |
74 | tikz.show()
75 |
76 | if __name__ == "__main__":
77 | matrix = np.array([[2, 0], [1, 1], [1, 1]])
78 | plot_linear_transformation(matrix)
79 |
--------------------------------------------------------------------------------
/examples/lorenz/lorenz.py:
--------------------------------------------------------------------------------
1 | #!/bin/bash/python3
2 | import numpy as np
3 | from scipy.integrate import odeint
4 | from tikzpy import TikzPicture
5 |
6 | """ Plots the Lorenz dynamical system.
7 |
8 | Note: This might not work for some users because of TeX's buffer size parameter, which is a fixed quantity that tells
9 | TeX when to quit a process (even if there are no errors with the program). Such a parameter exists
10 | because TeX was invented in the late 70s, and not during a time period with quad core 16GB ram computers.
11 |
12 | If one has trouble with this, one could increase their buffer size parameter (a good idea because your computer is in 2021+
13 | and it can definitely handle it) or one could take less iterations in the code below.
14 | """
15 |
16 | if __name__ == "__main__":
17 | tikz = TikzPicture(center=True)
18 | tikz.set_tdplotsetmaincoords(60, 45)
19 | tikz.options = "tdplot_main_coords"
20 |
21 | # lorenz parameters
22 | rho = 28.0
23 | sigma = 10.0
24 | beta = 8.0 / 3.0
25 |
26 |
27 | # Next state according to the ODEs
28 | def next(*state):
29 | x, y, z = state[0][0], state[0][1], state[0][2]
30 | return sigma * (y - x), x * (rho - z) - y, x * y - beta * z
31 |
32 |
33 | # Set initial conditions and time steps
34 | initial = [1.0, 1.0, 1.0]
35 | t = np.arange(
36 | 0.0, 80.0, 0.02
37 | ) # This might need to be changed, e.g., 0.02 to 0.08, to make the program run.
38 |
39 | # Solve for the next positions, scale them
40 | states = odeint(func=next, y0=initial, t=t)
41 | states = states * 0.25
42 |
43 | # We convert points from np.array to tuple for Tikz
44 | tuple_states = []
45 | for state in states:
46 | tuple_states.append(tuple(state))
47 |
48 | # Plot the lorenz system
49 | lorenz_plot = tikz.plot_coordinates(
50 | tuple_states, options="ProcessBlue!70", plot_options="smooth"
51 | )
52 | # Annotate the initial state
53 | tikz.circle(tuple_states[0], radius=0.1, action="fill")
54 | tikz.node(tuple_states[0], options="below", text="Initial: (1,1,1)")
55 |
56 | tikz.show()
57 |
--------------------------------------------------------------------------------
/examples/neural_network/neural_network.py:
--------------------------------------------------------------------------------
1 | from tikzpy import TikzPicture, Point
2 |
3 | node_radius = 0.5
4 | node_sep = 2
5 | layer_sep = 3
6 |
7 | input_layer_pos = (0, 0)
8 | hidden_layer_pos = (layer_sep, 0)
9 |
10 | def network_layer(init_pos, num_nodes, symbol, color):
11 | layer_nodes = []
12 | for idx, _ in enumerate(range(num_nodes)):
13 | pos = Point(init_pos) + (0, -node_sep * idx)
14 | # Draw the circle
15 | circle = tikz.circle(pos, radius=node_radius, options=f"fill={color}!40")
16 | # Draw the node
17 | tikz.node(pos, text=f"${symbol}_{idx}$")
18 | layer_nodes.append(circle)
19 | return layer_nodes
20 |
21 |
22 | def draw_layer_connection(curr_layer, next_layer):
23 | for curr_node in curr_layer:
24 | for next_node in next_layer:
25 | tikz.connect_circle_edges(curr_node, next_node, "->", dst_delta=0.1)
26 |
27 | def draw_neural_network(layer_sizes):
28 | max_size = max(layer_sizes)
29 | layers = []
30 | init_pos = Point((0, 0))
31 | for idx, size in enumerate(layer_sizes):
32 | x_shift = idx * layer_sep
33 | y_shift = - (max_size - size) / 2 * node_sep
34 | pos = init_pos + (x_shift, y_shift)
35 | if idx == 0:
36 | symbol = "x"
37 | color = "green"
38 | elif idx == len(layer_sizes) - 1:
39 | symbol = "y"
40 | color = "red"
41 | else:
42 | symbol = f"h^{{({idx})}}"
43 | color = "blue"
44 |
45 | nodes = network_layer(pos, size, symbol, color)
46 | layers.append(nodes)
47 |
48 | for idx, layer in enumerate(range(len(layers) - 1)):
49 | draw_layer_connection(layers[idx], layers[idx + 1])
50 |
51 |
52 | if __name__ == "__main__":
53 | tikz = TikzPicture(center=True)
54 | draw_neural_network([4, 5, 3, 4, 3, 2])
55 | tikz.show()
56 |
57 |
--------------------------------------------------------------------------------
/examples/neural_network_connection/neural_network_connection.py:
--------------------------------------------------------------------------------
1 | from tikzpy import TikzPicture
2 |
3 | tikz = TikzPicture(center=True) # center it in the PDF
4 | radius = 0.25
5 | pos_a = (0, 0)
6 | pos_b = (4, 1)
7 |
8 | # Draw the nodes
9 | node_a = tikz.circle(pos_a, radius)
10 | node_b = tikz.circle(pos_b, radius)
11 |
12 | # Draw the line between the nodes
13 | line = tikz.connect_circle_edges(node_a, node_b)
14 | line.options = "->"
15 |
16 | # Annotate the drawing with mathematical variables
17 | h_j = tikz.node(node_a.center + (0.3, 0.75), text="$h_j^{(n-1)}$")
18 | h_i = tikz.node(node_b.center + (0.3, 0.75), text="$h_i^{(n)}$")
19 | w_ij = tikz.node(line.pos_at_t(0.5) + (0, 0.5), text="$w_{ij}^{(n)}$")
20 |
21 | # Add ellipses on each side to illustrate more nodes are present
22 | tikz.node(node_a.center + (0, 1.5), text="\\vdots")
23 | tikz.node(node_a.center + (0, -0.75), text="\\vdots")
24 | tikz.node(node_b.center + (0, 1.5), text="\\vdots")
25 | tikz.node(node_b.center + (0, -0.75), text="\\vdots")
26 | tikz.show()
27 |
28 |
--------------------------------------------------------------------------------
/examples/neural_network_simple/neural_network_simple.py:
--------------------------------------------------------------------------------
1 | import math
2 | from tikzpy import TikzPicture
3 |
4 | def calc_start_end_between_nodes(pos_a, rad_a, pos_b, rad_b):
5 | x_1, y_1 = pos_a
6 | x_2, y_2 = pos_b
7 | if y_1 - y_2 == 0:
8 | theta = math.pi/2
9 | else:
10 | theta = math.atan(abs(x_2 - x_1) / abs(y_1 - y_2))
11 |
12 | if y_2 > y_1:
13 | start = (x_1 + radius * math.sin(theta), y_1 + radius* math.cos(theta))
14 | end = (x_2 - radius * math.sin(theta), y_2 - radius * math.cos(theta))
15 | else:
16 | start = (x_1 + radius * math.sin(theta), y_1 - radius* math.cos(theta))
17 | end = (x_2 - radius * math.sin(theta), y_2 + radius * math.cos(theta))
18 | return start, end
19 |
20 | if __name__ == "__main__":
21 | tikz = TikzPicture()
22 | layers = []
23 | horiz_space = 3
24 | vert_space = 2.5
25 | radius=0.25
26 | epsilon = 0.3
27 |
28 | # x_0 node
29 | x_0_node = tikz.circle((0, vert_space), radius)
30 | tikz.node((0, vert_space + radius + epsilon), text="$x_1$")
31 | # x_2 node
32 | x_1_node = tikz.circle((0, 0), radius)
33 | tikz.node((0, 0 + radius + epsilon), text="$x_2$")
34 | # x_2 node
35 | x_2_node = tikz.circle((0, -vert_space), radius)
36 | tikz.node(x_2_node.center + (0, radius + epsilon), text="$1$")
37 |
38 | # h_0 node
39 | h_0_node = tikz.circle((horiz_space, vert_space), radius)
40 | tikz.node((horiz_space, vert_space + radius + epsilon), text="$h_1$")
41 | # h_1 node
42 | h_1_node = tikz.circle((horiz_space, 0), radius)
43 | tikz.node((horiz_space, 0 + radius + epsilon), text="$h_2$")
44 | # h_2 node
45 | h_2_node = tikz.circle((horiz_space, -vert_space), radius)
46 | tikz.node((horiz_space, -vert_space + radius + epsilon), text="$1$")
47 |
48 | # Add the final output layer
49 | x_2 = 1.75*horiz_space
50 | y_2 = 0
51 |
52 | output_node = tikz.circle((x_2, y_2), radius)
53 | tikz.node((x_2, y_2 + radius + epsilon), text="$y$")
54 |
55 | layers = [[x_0_node, x_1_node, x_2_node], [h_0_node, h_1_node, h_2_node], [output_node]]
56 |
57 | # Draw labels for hidden layer
58 | lines = []
59 | for i in range(len(layers) - 1):
60 | curr_nodes = layers[i]
61 | next_nodes = layers[i + 1]
62 |
63 | for ind, node in enumerate(curr_nodes):
64 | pos_a = node.center
65 | rad_a = node.radius
66 | for j, next_node in enumerate(next_nodes):
67 | if i == 0 and j == len(next_nodes) - 1:
68 | continue
69 | pos_b = next_node.center
70 | rad_b = next_node.radius
71 | start, end = calc_start_end_between_nodes(pos_a, rad_a, pos_b, rad_b)
72 | lines.append(tikz.line(start, end, options="->"))
73 |
74 | # label the weights in the hidden layer
75 | tikz.node(lines[0].pos_at_t(0.5), options="above", text="3")
76 | tikz.node(lines[1].pos_at_t(0.2), options="above", text="4")
77 | tikz.node(lines[2].pos_at_t(0.2), options="above", text="2")
78 | tikz.node(lines[3].pos_at_t(0.3), options="above", text="3")
79 | # label the biases in the first layer
80 | tikz.node(lines[4].pos_at_t(0.2), options="left", text="-2")
81 | tikz.node(lines[5].pos_at_t(0.5), options="below", text="-4")
82 |
83 | # label the weights in the final layer
84 | tikz.node(lines[6].pos_at_t(0.4), options="right", text="5")
85 | tikz.node(lines[7].pos_at_t(0.5), options="above", text="-5")
86 | # label the bias in the final layer
87 | tikz.node(lines[8].pos_at_t(0.5), options="left", text="-2")
88 |
89 | tikz.show()
90 |
--------------------------------------------------------------------------------
/examples/polar/polar.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from tikzpy import TikzPicture, PlotCoordinates, Circle
3 | from tikzpy.colors import rainbow_colors
4 |
5 | def to_cartesian(r, ang):
6 | """Returns the cartesian coordinates of a polar point."""
7 | x = r * np.cos(ang)
8 | y = r * np.sin(ang)
9 | return x, y
10 |
11 |
12 | if __name__ == "__main__":
13 | xy_plane = TikzPicture(options="thick")
14 | polar_plane = TikzPicture(options="thick")
15 |
16 | # XY_plane
17 | xy_plane.line((0, 0), (2 * np.pi, 0)).add_node(options="above", text="$x$")
18 | xy_plane.line((0, 0), (0, 2)).add_node(options="above", text="$y$")
19 |
20 | # Polar Plane: lines and concentric circles
21 | for ang in np.linspace(0, 2 * np.pi, 16):
22 | polar_plane.line((0, 0), to_cartesian(2.1, ang), options="Gray!30")
23 |
24 | for radius in [1, 1.5, 2]:
25 | polar_plane.circle((0, 0), radius=radius, options="Gray!30")
26 |
27 | # Sin curve and Cardioid
28 | sin_curve = xy_plane.plot_coordinates([], plot_options="smooth")
29 | cardioid = polar_plane.plot_coordinates([], plot_options="smooth")
30 |
31 | for ang in np.linspace(0, 2 * np.pi, 200):
32 | x, y = to_cartesian(1 + np.sin(ang), ang)
33 | sin_curve.add_point(ang, 1 + np.sin(ang))
34 | cardioid.add_point(x, y)
35 |
36 | # Rainbow points
37 | for ang in np.linspace(0, 2 * np.pi, 16):
38 | r = 1 + np.sin(ang)
39 | x, y = to_cartesian(r, ang)
40 | color = rainbow_colors(int(ang / (np.pi / 6)))
41 | xy_plane.circle((ang, r), radius=0.075, options=f"fill={color}", action="fill")
42 | polar_plane.circle((x, y), radius=0.075, options=f"fill={color}", action="fill")
43 |
44 | xy_plane.write()
45 | polar_plane.write()
46 | polar_plane.show()
47 |
--------------------------------------------------------------------------------
/examples/projective_cone/projective_cone.py:
--------------------------------------------------------------------------------
1 | #!/bin/bash/python3
2 | import numpy as np
3 | from tikzpy import TikzPicture, PlotCoordinates
4 |
5 | if __name__ == "__main__":
6 | tikz = TikzPicture(center=True, options="tdplot_main_coords")
7 | tikz.set_tdplotsetmaincoords(75, 120)
8 |
9 | HEIGHT = 3.75 # Height of the surace (from the floor)
10 | O = (1, 2, HEIGHT / 2) # The origin
11 |
12 | # Draw the rectangle
13 | floor = tikz.plot_coordinates([(0, 0, 0), (5, 0, 0), (5, 5, 0), (0, 5, 0), (0, 0, 0)])
14 | floor.options = "shade, left color = NavyBlue!30, right color = NavyBlue, opacity = 0.3"
15 |
16 | # Initialize the top and bottom curves. Note the points are empty. We'll add to them.
17 | top_curve = PlotCoordinates([], options="line width = 0.07mm")
18 | bottom_curve = PlotCoordinates([], options="line width = 0.2mm")
19 |
20 | for t in np.linspace(0, 1, 100):
21 | # Compute points on the top and bottom curves. I guessed this formula in trying to mimic Hartshorne's image.
22 | top_pt = (1.5 + np.sin(10 * t), 3 * t + 0.5, HEIGHT)
23 | bottom_pt = (1.5 + np.sin(10 * t), 3 * t + 0.5, 0)
24 |
25 | # Lines from the origin to the current points on the curves
26 | line_1 = tikz.line(O, top_pt, "gray, opacity = 0.75, line width = 0.05mm")
27 | line_2 = tikz.line(O, bottom_pt, "gray, opacity = 0.75, line width = 0.05mm")
28 |
29 | # Collect points to later draw the top and bottom curves
30 | top_curve.points.append(top_pt)
31 | bottom_curve.points.append(bottom_pt)
32 |
33 | # We now draw the top and bottom curves
34 | tikz.draw(top_curve, bottom_curve)
35 |
36 | # Annotate the origin (O), rectangle (P^2), cone (C(Y)), variety (Y), and ambient space (A^3)
37 | tikz.circle(O, radius=0.03, action="fill")
38 | tikz.node(O, options="left", text="$O$")
39 | tikz.node((3, 3, 0), text="$Y$")
40 | tikz.node((1, 4.5, 0), text="$\mathbf{P}^2$")
41 | tikz.node((0, 2.5, 1), text="$C(Y)$")
42 | tikz.node((0, 4.2, 3.5), text="$\mathbf{A}^3$")
43 | with open("code.tex", "w") as f:
44 | f.write(tikz.code())
45 |
46 | tikz.show(quiet=True)
47 |
--------------------------------------------------------------------------------
/examples/relu/relu.py:
--------------------------------------------------------------------------------
1 | from tikzpy import TikzPicture, R2_Space
2 |
3 |
4 | if __name__ == "__main__":
5 | tikz = TikzPicture()
6 | axes_len = 4
7 | vert_scale = 4.5
8 |
9 | # Set up xy-plane
10 | xy_plane = R2_Space(x_interval=(-axes_len, axes_len), y_interval=(0, vert_scale + .5))
11 | xy_plane.x_axis_options = "Gray!30, ->"
12 | xy_plane.y_axis_options = "Gray!30, ->"
13 | tikz.draw(xy_plane)
14 |
15 | line = tikz.line((-3.5, 4.5), (4.5, 4.5), options="dashed")
16 | tikz.node(line.start, options="left", text="$y=1$")
17 | # Plot it
18 | origin = (0, 0)
19 | tikz.line((-axes_len, 0), origin, options="ProcessBlue, <-")
20 | tikz.line(origin, (4.5, 4.5), options="ProcessBlue, ->")
21 |
22 | tikz.show()
23 |
--------------------------------------------------------------------------------
/examples/roots_of_unity/roots_of_unity.py:
--------------------------------------------------------------------------------
1 | #!/bin/bash/python3
2 | from math import pi, sin, cos
3 | from tikzpy import TikzPicture
4 |
5 |
6 | def roots_of_unity(n, scale=5):
7 | """Creates a diagram for the n-th roots of unity.
8 | n (int) : The number of roots of unity we display
9 | scale (float) : A parameter which controls the size of the image. This should be >5 for larger roots of unity.
10 | """
11 | tikz = TikzPicture(center=True)
12 |
13 | for i in range(n):
14 | theta = (2 * pi * i) / n
15 |
16 | # Draw line to nth root of unity
17 | line_to_root = tikz.line(
18 | (0, 0), (scale * cos(theta), scale * sin(theta)), options="-o"
19 | )
20 |
21 | if 0 <= theta <= pi:
22 | node_option = "above"
23 | else:
24 | node_option = "below"
25 |
26 | # Label the nth root of unity
27 | tikz.node(
28 | line_to_root.end,
29 | options=node_option,
30 | text=f"$e^{{ (2 \cdot \pi \cdot {i})/ {n} }}$",
31 | )
32 |
33 | tikz.show()
34 |
35 | if __name__ == "__main__":
36 | roots_of_unity(10)
37 |
--------------------------------------------------------------------------------
/examples/rotate_circles/rotate_circles.py:
--------------------------------------------------------------------------------
1 | #!/bin/bash/python3
2 | from math import pi, sin, cos
3 | from tikzpy import TikzPicture
4 | from tikzpy.colors import rainbow_colors
5 |
6 | if __name__ == "__main__":
7 | tikz = TikzPicture(center=True)
8 |
9 | n = 30
10 | for i in range(n):
11 | point = (sin(2 * pi * i / n), cos(2 * pi * i / n))
12 |
13 | for j in range(0, 8):
14 | tikz.circle(point, 2 + j * 0.2, options="color=" + rainbow_colors(i + j)).shift_(
15 | 0, -2
16 | )
17 |
18 | tikz.show()
19 |
--------------------------------------------------------------------------------
/examples/rotate_plot/rotate_plot.py:
--------------------------------------------------------------------------------
1 | #!/bin/bash/python3
2 | from math import exp
3 | from tikzpy import TikzPicture
4 | from tikzpy.colors import rainbow_colors
5 |
6 | if __name__ == "__main__":
7 | tikz = TikzPicture()
8 | # points = [(14.4, 3.2), (16.0, 3.6), (16.8, 4.8), (16.0, 6.8), (16.4, 8.8), (13.6, 8.8), (12.4, 7.6), (12.8, 5.6), (12.4, 3.6)]
9 | # points = [(6.6,11.4), (5.3,8.8), (3.6,9.9), (2.8,7.9), (3.7,6.1), (4.5,4), (6.2,4.2), (6.7,5.5), (8.5,4.3), (9.5,6.7), (8.8,8.5), (9.4,11.1), (7.7,11)]
10 | points = [
11 | (5.6, 11.1),
12 | (5.2, 9.6),
13 | (3.2, 10.6),
14 | (4.3, 7.3),
15 | (3, 4.1),
16 | (5.6, 5.2),
17 | (7.2, 3.9),
18 | (8.4, 5.6),
19 | (10.2, 4.5),
20 | (8.7, 6.9),
21 | (10, 8.6),
22 | (8.1, 8.8),
23 | (9.3, 11.8),
24 | (7.2, 11.1),
25 | (6.2, 12.5),
26 | ]
27 |
28 | for theta in range(0, 540, 5):
29 | # Plot the points
30 | plot = tikz.plot_coordinates(
31 | options=f"fill = {rainbow_colors(theta)}",
32 | plot_options="smooth, tension=.5, closed hobby",
33 | points=points,
34 | )
35 | # Rotate them
36 | plot.rotate_(theta, about_pt=(0, 0))
37 | # Scale them
38 | plot.scale_(exp(-1.5 * theta / 180))
39 |
40 | tikz.show()
41 |
--------------------------------------------------------------------------------
/examples/shift_plot/shift_plot.py:
--------------------------------------------------------------------------------
1 | #!/bin/bash/python3
2 | from tikzpy import TikzPicture
3 | from tikzpy.colors import rainbow_colors
4 |
5 |
6 | if __name__ == "__main__":
7 | tikz = TikzPicture()
8 | # points = [(14.4, 3.2), (16.0, 3.6), (16.8, 4.8), (16.0, 6.8), (16.4, 8.8), (13.6, 8.8), (12.4, 7.6), (12.8, 5.6), (12.4, 3.6)]
9 | # points = [(6.6,11.4), (5.3,8.8), (3.6,9.9), (2.8,7.9), (3.7,6.1), (4.5,4), (6.2,4.2), (6.7,5.5), (8.5,4.3), (9.5,6.7), (8.8,8.5), (9.4,11.1), (7.7,11)]
10 | points = [
11 | (5.6, 11.1),
12 | (5.2, 9.6),
13 | (3.2, 10.6),
14 | (4.3, 7.3),
15 | (3, 4.1),
16 | (5.6, 5.2),
17 | (7.2, 3.9),
18 | (8.4, 5.6),
19 | (10.2, 4.5),
20 | (8.7, 6.9),
21 | (10, 8.6),
22 | (8.1, 8.8),
23 | (9.3, 11.8),
24 | (7.2, 11.1),
25 | (6.2, 12.5),
26 | ]
27 |
28 | for i in range(1, 20):
29 | plot = tikz.plot_coordinates(
30 | options=f"fill = {rainbow_colors(i)}, opacity = 0.5",
31 | plot_options="smooth, tension=.5, closed hobby",
32 | points=points,
33 | )
34 | plot.shift_(0, i / 5)
35 | plot.rotate_(45, about_pt=plot.center, radians=False)
36 |
37 | tikz.show()
38 |
--------------------------------------------------------------------------------
/examples/sigmoid/sigmoid.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from tikzpy import TikzPicture, R2_Space
3 | import math
4 |
5 | vert_scale = 4.5
6 | horiz_scale = 1.5
7 |
8 | def sigmoid(val):
9 | val = horiz_scale * val
10 | return 1/(1 + math.pow(math.e, -val))
11 |
12 |
13 | if __name__ == "__main__":
14 | tikz = TikzPicture()
15 | xrange = (-3, 3)
16 |
17 | # Set up xy-plane
18 | xy_plane = R2_Space(x_interval=(-4, 4), y_interval=(0, vert_scale + .5))
19 | xy_plane.x_axis_options = "Gray!30, ->"
20 | xy_plane.y_axis_options = "Gray!30, ->"
21 | tikz.draw(xy_plane)
22 |
23 | # Collect (x,y) data
24 | x = horiz_scale * np.linspace(xrange[0], xrange[1], 200)
25 | y = [vert_scale*sigmoid(val) for val in x]
26 |
27 | line = tikz.line((-3.5, 4.5), (4.5, 4.5), options="dashed")
28 | tikz.node(line.start, options="left", text="$y=1$")
29 | # Plot it
30 | points = tikz.plot_coordinates(list(zip(x, y)), options="ProcessBlue")
31 | tikz.show()
32 |
--------------------------------------------------------------------------------
/examples/sphere_loop/sphere_loop.py:
--------------------------------------------------------------------------------
1 | #!/bin/bash/python3
2 | from tikzpy import TikzPicture
3 | from tikzpy.colors import rainbow_colors
4 | import numpy as np
5 |
6 | if __name__ == "__main__":
7 | tikz = TikzPicture()
8 | tikz.circle((0, 0), 3, options="thick, gray!10", action="filldraw")
9 |
10 | x_r = 3
11 | y_r = 1.5
12 |
13 | for t in np.linspace(0, 2 * np.pi, 200):
14 | x_pos = 3 * np.cos(t)
15 | y_pos = 3 * np.sin(t)
16 |
17 | arc_one = tikz.arc(
18 | (x_pos, y_pos),
19 | 0,
20 | 180,
21 | x_radius=x_r,
22 | y_radius=y_r,
23 | options=f"color={rainbow_colors(int(t*180/np.pi))}",
24 | )
25 | arc_two = tikz.arc(
26 | (x_pos - 2 * x_r, y_pos),
27 | 180,
28 | 360,
29 | x_radius=x_r,
30 | y_radius=y_r,
31 | options=f"color={rainbow_colors(int(t*180/np.pi))}",
32 | )
33 |
34 | tikz.show()
35 |
--------------------------------------------------------------------------------
/examples/spiral/spiral.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from tikzpy import TikzPicture, PlotCoordinates, Circle
3 |
4 | if __name__ == "__main__":
5 | tikz = TikzPicture(center=True)
6 |
7 | for height in [0, 1.25, 2.5, 3.75]:
8 | """Idea: No Tikz solution exists for drawing this spiral such that self intersections are drawn with
9 | a "crossing over" feature. All documented methods (e.g., preaction, decorations, knots library) don't work.
10 | Instead, we :
11 | Create two curves, one before the self intersection and one after.
12 | At the self intersection, we draw a white circle.
13 | We then draw in the order: first curve half, white circle, second curve half. This provides the necessary crossing over feature.
14 | """
15 | curve_under = PlotCoordinates([], plot_options="smooth") # Curve before cross over
16 | curve_over = PlotCoordinates([], plot_options="smooth") # Curve after cross over
17 | points = [] # Points on the spiral
18 | under = True # Controls if we are drawing curve_under or curve_over
19 | t = 0
20 | while t < 2 * np.pi / 5 + 0.1:
21 | x = 2 * np.cos(5 * t)
22 | y = 2 * np.sin(5 * t) * 0.3 + t + height
23 |
24 | if np.abs(t - 0.1731) < 3.5e-3: # Draw white space at self intersection
25 | under = False
26 | white_space = Circle(
27 | (x, y), radius=0.05, options="fill=white", action="fill"
28 | )
29 |
30 | else: # Otherwise, collect points on the curve
31 | if under:
32 | curve_under.points.append((x, y))
33 | else:
34 | curve_over.points.append((x, y))
35 | t += 2 * np.pi / 500
36 |
37 | # Draw everything in this specific order
38 | tikz.draw(curve_under, white_space, curve_over)
39 |
40 | # Annotations: \\vdots, S^1, x, \mathbb{R}^1
41 | tikz.node((0, -0.4), text="$\\vdots$")
42 | tikz.node((-2.8, -0.2), text="$\\vdots$")
43 | tikz.node((-2.8, 5.3), text="$\\vdots$")
44 | tikz.node((2.4, -2), text="$S^1$")
45 | tikz.node((-1.5, -2.6), text="$x$")
46 | tikz.node((2.4, 0.7), text="$\\mathbb{R}^1$")
47 | # Arrow
48 | tikz.line((0, -0.9), (0, -1.2), options="->, =>Stealth")
49 | # Circles and x's
50 | for i, math in [(0, "x-1"), (1, "x"), (2, "x+1"), (3, "x+2")]:
51 | x = -1.5
52 | y = 0.37 + 1.25 * i
53 | tikz.circle((x, y), radius=0.05, action="fill")
54 | tikz.node((x - 1.3, y + 0.2), text=f"${math}$")
55 |
56 | # S^1
57 | S1 = tikz.ellipse((0, -2), x_axis=2, y_axis=2 * 0.2)
58 | tikz.circle((-1.5, -2.255), radius=0.05, action="fill")
59 |
60 | tikz.show()
61 |
--------------------------------------------------------------------------------
/examples/symbolic_integration/integrate_and_plot.py:
--------------------------------------------------------------------------------
1 | #!/bin/bash/python3
2 | import numpy as np
3 | from sympy import * # Without caution, import * can become a very stupid idea. Here it is okay.
4 | from sympy.abc import x # Don't name any function, variable, etc, as 'x'.
5 | from tikzpy import TikzPicture
6 | from tikzpy.colors import rainbow_colors
7 |
8 | """ Uses Sympy to iteratively calculate n-order integrals. These are then used by tikzpy
9 | to plot.
10 | Ex:
11 | >>> integrate_n_times(poly(x**2), 5)
12 | >>> integrate_n_times(poly(x**2)-2, 5)
13 | >>> integrate_n_times(sin(x)+x, 3)
14 | >>> integrate_n_times(log(x), 3, 0.1, 5)
15 | """
16 |
17 |
18 | def integrate_n_times(func, n, x_start=-2.5, x_end=2.5):
19 | """Given a function func, we integrate and plot each of the i-order integrals of
20 | func, where i = 1, 2, ... , n.
21 |
22 | func (sympy.core.function.FunctionClass) : A Sympy function. Ex: sin(x), x**2, log(x), etc.
23 | n (int) : The number of integrals we would like to see
24 | x_start : The value of x we should begin plotting
25 | x_end : The value of x we should stop plotting
26 | """
27 | # Create a TikzPicture
28 | tikz = TikzPicture()
29 | # x- and y- axis
30 | tikz.line((-4, 0), (4, 0), options="Gray!40, thick, <->")
31 | tikz.line((0, -4), (0, 4), options="Gray!40, thick, <->")
32 |
33 | integrals = [(func, [])]
34 | for i in range(1, n):
35 | next_int = integrate(integrals[i - 1][0], x)
36 | integrals.append((next_int, []))
37 |
38 | n_samples = 100
39 | for i in np.linspace(x_start, x_end, 100): # 100 samples from (x_start, x_end)
40 | for integ in integrals:
41 | integ_val = (i, float(integ[0].subs(x, i)))
42 | integ[1].append(integ_val)
43 |
44 | for i, integ in enumerate(integrals):
45 | tikz.plot_coordinates(integ[1], options=f"<->, color= {rainbow_colors(3*i)}")
46 | if i % 2:
47 | pos = integ[1][-1]
48 | options = "right"
49 | else:
50 | pos = integ[1][0]
51 | options = "left"
52 | tikz.node(pos, options=options, text=f"${latex(integ[0])}$")
53 |
54 | tikz.show()
55 |
56 | if __name__ == "__main__":
57 | integrate_n_times(poly(x**2), 5)
58 |
59 |
--------------------------------------------------------------------------------
/examples/ven_diagrams/intersections_scope_clip.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from itertools import combinations
3 | from tikzpy import TikzPicture, Circle, PlotCoordinates, Rectangle
4 | from tikzpy.colors import rainbow_colors
5 |
6 | tikz = TikzPicture()
7 |
8 | def ven_diagram(blobs, show_outlines=False):
9 | """A function that takes in an arbitrary number of 2D blobs and intersects and colors every
10 | single intersection area.
11 | """
12 | # First we fill in the blobs
13 | for blob in blobs:
14 | tikz.draw(blob)
15 | # Next we compute all of the intersections of the blobs we must make.
16 | tuple_indices = []
17 | for tuple_size in range(2, len(blobs) + 1):
18 | tuple_indices += list(combinations(range(1, len(blobs) + 1), tuple_size))
19 |
20 | # Now we clip and draw the blobs.
21 | for j, tuple_index in enumerate(tuple_indices):
22 | scope = tikz.scope()
23 | clip_draw_blobs = []
24 | for ind in tuple_index:
25 | clip_draw_blobs.append(blobs[ind - 1])
26 |
27 | # We create the scope environment
28 | scope = tikz.scope()
29 | # Now we clip and draw. We clip all the blobs except the last one, which we draw.
30 | for i, blob in enumerate(clip_draw_blobs):
31 | blob_copy = blob.copy()
32 |
33 | if i != len(clip_draw_blobs) - 1:
34 | scope.clip(blob_copy)
35 | else:
36 | blob_copy.options = f"fill = {rainbow_colors(j)}, opacity = 0.7"
37 | scope.append(blob_copy)
38 |
39 | # We're done, draw the outline
40 | if show_outlines:
41 | for blob in blobs:
42 | draw_blob = blob.copy(options="", action="draw")
43 | tikz.draw(draw_blob)
44 | tikz.write()
45 | tikz.show()
46 |
47 | # A set of three plots
48 | pts_one = [
49 | (-2.1, 1.5),
50 | (-1, 2.5),
51 | (1.5, 1),
52 | (4, 0.5),
53 | (3, -0.5),
54 | (2.5, -3),
55 | (0, -0.2),
56 | (-3, -2.5),
57 | ]
58 |
59 |
60 | pts_two = [
61 | (-3.5, -0.5),
62 | (-3, -2.5),
63 | (-1.5, -3.5),
64 | (1, -2),
65 | (3.5, -2.5),
66 | (3.5, 0.5),
67 | (2, 2),
68 | (-0.5, -1.5),
69 | (-3, 2),
70 | ]
71 |
72 | pts_three = [(-1.5, 0), (1, 1), (2, 0), (1, -1), (0, -2), (-2, -1)]
73 |
74 | plot_options = "smooth, tension=.7, closed hobby"
75 | blob1 = PlotCoordinates(
76 | pts_one, options="red!80", plot_options=plot_options, action="fill"
77 | )
78 | blob2 = PlotCoordinates(
79 | pts_two, options="ProcessBlue!80", plot_options=plot_options, action="fill"
80 | )
81 | blob3 = PlotCoordinates(
82 | pts_three, options="Green!80", plot_options=plot_options, action="fill"
83 | )
84 | blobs = [blob1, blob2, blob3]
85 |
86 | if __name__ == "__main__":
87 | # Four specific circles
88 | circle1 = Circle((0, 0), 2, options="purple, opacity = 0.7", action="fill")
89 | circle2 = Circle((1, 0), 2, options="ProcessBlue, opacity = 0.7", action="fill")
90 | circle3 = Circle((1, 1), 2, options="Magenta, opacity = 0.7", action="fill")
91 | circle4 = Circle((0, 1), 2, options="ForestGreen, opacity = 0.7", action="fill")
92 |
93 | # 9 rainbow circles
94 | circles = []
95 | for i in range(1, 4):
96 | for j in range(1, 4):
97 | circ = Circle(
98 | (i, j),
99 | 1.5,
100 | options=f"fill = {rainbow_colors(i*j)}, fill opacity = 0.7",
101 | action="fill",
102 | )
103 | circles.append(circ)
104 | ven_diagram(circles)
105 | #ven_diagram(blobs)
106 |
--------------------------------------------------------------------------------
/mkdocs.yml:
--------------------------------------------------------------------------------
1 | site_name: Tikz-Python
2 | theme:
3 | name: material
4 | features:
5 | - content.code.copy
6 | - navigation.tabs
7 | icon:
8 | repo: fontawesome/brands/github
9 | favicon: png/favicon.png
10 | logo: png/favicon.png
11 |
12 |
13 | plugins:
14 | - search
15 | - mkdocstrings:
16 | handlers:
17 | python:
18 | paths: [src] # search packages in the src folder
19 | options:
20 | allow_inspection: true
21 | show_source: false
22 | show_symbol_type_toc: true
23 | show_symbol_type_heading: true
24 | show_root_heading: true
25 | show_root_full_path: false
26 | separate_signature: true
27 | show_signature_annotations: true
28 | watch:
29 | - src/tikzpy
30 |
31 |
32 | markdown_extensions:
33 | - pymdownx.highlight:
34 | anchor_linenums: true
35 | line_spans: __span
36 | pygments_lang_class: true
37 | - pymdownx.inlinehilite
38 | - pymdownx.snippets
39 | - pymdownx.superfences
40 | - attr_list
41 |
42 | extra_css:
43 | - stylesheets/extra.css
44 |
45 | repo_url: https://github.com/ltrujello/Tikz-Python
46 |
47 | nav:
48 | - Home: index.md
49 | - Tutorials: tutorials.md
50 | - Examples: examples.md
51 | - API Documentation:
52 | - TikzPicture: API_Documentation/tikz_picture.md
53 | - Circle: API_Documentation/circle.md
54 | - Ellipse: API_Documentation/ellipse.md
55 | - Line: API_Documentation/line.md
56 | - Node: API_Documentation/node.md
57 | - PlotCoordinates: API_Documentation/plot_coordinates.md
58 | - Point: API_Documentation/point.md
59 | - Rectangle: API_Documentation/rectangle.md
60 | - Arc: API_Documentation/arc.md
61 | - Scope: API_Documentation/scope.md
62 | - Installation: installation.md
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [build-system]
2 | requires = ["setuptools>=61.0"]
3 | build-backend = "setuptools.build_meta"
4 |
5 | [project]
6 | name = "tikz_python"
7 | version = "0.0.5"
8 | authors = [
9 | { name="Luke Trujillo", email="ltrujillo@hmc.edu" },
10 | ]
11 | description = "A python wrapper for tikz"
12 | readme = "README.md"
13 | requires-python = ">=3.7"
14 | classifiers = [
15 | "Programming Language :: Python :: 3",
16 | "License :: OSI Approved :: MIT License",
17 | "Operating System :: OS Independent",
18 | ]
19 |
20 | [tool.ruff]
21 | ignore-init-module-imports = true
22 | [project.urls]
23 | "Homepage" = "https://github.com/ltrujello/Tikz-Python/"
24 | "Documentation" = "https://ltrujello.github.io/Tikz-Python/"
25 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | black==23.7.0
2 | click==8.1.6
3 | iniconfig==2.0.0
4 | mypy-extensions==1.0.0
5 | numpy==1.25.1
6 | packaging==23.1
7 | pathspec==0.11.1
8 | platformdirs==3.9.1
9 | pluggy==1.2.0
10 | pytest==7.4.0
11 | ruff==0.0.280
12 |
--------------------------------------------------------------------------------
/src/tikzpy/__init__.py:
--------------------------------------------------------------------------------
1 | from tikzpy.tikz_environments.tikz_picture import TikzPicture
2 | from tikzpy.tikz_environments.scope import Scope
3 | from tikzpy.tikz_environments.clip import Clip
4 | from tikzpy.drawing_objects.line import Line
5 | from tikzpy.drawing_objects.plotcoordinates import PlotCoordinates
6 | from tikzpy.drawing_objects.circle import Circle
7 | from tikzpy.drawing_objects.node import Node
8 | from tikzpy.drawing_objects.rectangle import Rectangle
9 | from tikzpy.drawing_objects.ellipse import Ellipse
10 | from tikzpy.drawing_objects.arc import Arc
11 | from tikzpy.drawing_objects.xy_plane import R2_Space
12 | from tikzpy.drawing_objects.point import Point
13 |
14 | __all__ = [
15 | TikzPicture,
16 | Scope,
17 | Clip,
18 | Line,
19 | PlotCoordinates,
20 | Circle,
21 | Node,
22 | Rectangle,
23 | Ellipse,
24 | Arc,
25 | R2_Space,
26 | Point,
27 | ]
28 |
--------------------------------------------------------------------------------
/src/tikzpy/colors/__init__.py:
--------------------------------------------------------------------------------
1 | from .colors import rgb, rainbow_colors, xcolors
2 |
--------------------------------------------------------------------------------
/src/tikzpy/colors/colors.py:
--------------------------------------------------------------------------------
1 | def rgb(r: float, g: float, b: float) -> str:
2 | """A wrapper function that outputs xcolor/Tikz code for coloring via rgb values.
3 |
4 | When calling rgb, it is necessary to specify "color = " or "fill =" right before.
5 | E.g., tikz.line(... options = "color =" + rgb(r,g,b) ...)
6 |
7 | This is an annoying aspect with Tikz.
8 | """
9 | return f"{{ rgb,255:red, {r}; green, {g}; blue, {b} }}"
10 |
11 |
12 | def rainbow_colors(i: int) -> str:
13 | """A wrapper function for obtaining rainbow colors."""
14 | return rainbow_cols[i % len(rainbow_cols)]
15 |
16 |
17 | def xcolors(i: int) -> str:
18 | """A wrapper function to obtain xcolors.
19 | Any integer is valid.
20 | """
21 | return xcols[i % len(xcols)]
22 |
23 |
24 | # Collect xcolor names
25 | xcols = [
26 | "Apricot",
27 | "Aquamarine",
28 | "Bittersweet",
29 | "Black",
30 | "Blue",
31 | "BlueGreen",
32 | "BlueViolet",
33 | "BrickRed",
34 | "Brown",
35 | "BurntOrange",
36 | "CadetBlue",
37 | "CarnationPink",
38 | "Cerulean",
39 | "CornflowerBlue",
40 | "Cyan",
41 | "Dandelion",
42 | "DarkOrchid",
43 | "Emerald",
44 | "ForestGreen",
45 | "Fuchsia",
46 | "Goldenrod",
47 | "Gray",
48 | "Green",
49 | "GreenYellow",
50 | "JungleGreen",
51 | "Lavender",
52 | "LimeGreen",
53 | "Magenta",
54 | "Mahogany",
55 | "Maroon",
56 | "Melon",
57 | "MidnightBlue",
58 | "Mulberry",
59 | "NavyBlue",
60 | "OliveGreen",
61 | "Orange",
62 | "OrangeRed",
63 | "Orchid",
64 | "Peach",
65 | "Periwinkle",
66 | "PineGreen",
67 | "Plum",
68 | "ProcessBlue",
69 | "Purple",
70 | "RawSienna",
71 | "Red",
72 | "RedOrange",
73 | "RedViolet",
74 | "Rhodamine",
75 | "RoyalBlue",
76 | "RoyalPurple",
77 | "RubineRed",
78 | "Salmon",
79 | "SeaGreen",
80 | "Sepia",
81 | "SkyBlue",
82 | "SpringGreen",
83 | "Tan",
84 | "TealBlue",
85 | "Thistle",
86 | "Turquoise",
87 | "Violet",
88 | "VioletRed",
89 | "White",
90 | "WildStrawberry",
91 | "Yellow",
92 | "YellowGreen",
93 | "YellowOrange",
94 | ]
95 |
96 | rainbow_cols = [
97 | "{rgb,255:red, 255; green, 0; blue, 0 }", # red
98 | "{rgb,255:red, 255; green, 125; blue, 0 }", # orange
99 | "{rgb,255:red, 255; green, 240; blue, 105 }", # yellow
100 | "{rgb,255:red, 125; green, 255; blue, 0 }", # spring
101 | "{rgb,255:red, 0; green, 255; blue, 0 }", # green
102 | "{rgb,255:red, 0; green, 255; blue, 125 }", # turquoise
103 | "{rgb,255:red, 0; green, 255; blue, 255 }", # cyan
104 | "{rgb,255:red, 0; green, 125; blue, 255 }", # ocean
105 | "{rgb,255:red, 0; green, 0; blue, 255 }", # blue
106 | "{rgb,255:red, 125; green, 0; blue, 255 }", # violet
107 | "{rgb,255:red, 255; green, 0; blue, 12 }", # magenta
108 | "{rgb,255:red, 255; green, 0; blue, 255 }", # raspberry
109 | ]
110 |
--------------------------------------------------------------------------------
/src/tikzpy/drawing_objects/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ltrujello/Tikz-Python/67c0e42d0fe1a0697ada37d1214eb733b9358f3e/src/tikzpy/drawing_objects/__init__.py
--------------------------------------------------------------------------------
/src/tikzpy/drawing_objects/circle.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 | import math
3 | from typing import Tuple, Union
4 | from tikzpy.drawing_objects.point import Point
5 | from tikzpy.drawing_objects.drawing_object import DrawingObject
6 |
7 |
8 | class Circle(DrawingObject):
9 | r"""
10 | A class to create circles in the tikz environment.
11 |
12 | Parameters:
13 | center: Pair of floats representing the center of the circle
14 | radius: Length (in cm) of the radius
15 | options: String containing the drawing options (e.g, "Blue")
16 | action: The type of TikZ action to use. Default is "draw".
17 | """
18 |
19 | def __init__(
20 | self,
21 | center: Union[Tuple[float, float], Point],
22 | radius: float,
23 | options: str = "",
24 | action: str = "draw",
25 | ) -> None:
26 | self._center = Point(center)
27 | self.radius = radius
28 | self.options = options
29 | super().__init__(action, self.options)
30 |
31 | @property
32 | def center(self) -> Point:
33 | """
34 | Returns a Point object representing the center of the circle.
35 |
36 | ```python
37 | from tikzpy import TikzPicture
38 | tikz = TikzPicture()
39 | circle = tikz.circle((0,0), 1, options="fill=ProcessBlue!30", action="filldraw")
40 | tikz.node(circle.center, text="O")
41 | tikz.show()
42 | ```
43 |
44 |
45 | """
46 | return self._center
47 |
48 | @center.setter
49 | def center(self, new_center: Union[tuple, Point]) -> None:
50 | if isinstance(new_center, (tuple, Point)):
51 | self._center = Point(new_center)
52 | else:
53 | raise TypeError(f"Invalid type '{type(new_center)}' for center")
54 |
55 | @property
56 | def north(self) -> Point:
57 | """
58 | Returns a Point object representing the north point on the circle.
59 |
60 | ```python
61 | from tikzpy import TikzPicture
62 | tikz = TikzPicture()
63 | circle = tikz.circle((0,0), 1, options="fill=ProcessBlue!30", action="filldraw")
64 | tikz.node(circle.north, text="$N$", options="above")
65 | tikz.show()
66 | ```
67 |
68 |
69 | """
70 | return self._center + (0, self.radius)
71 |
72 | @property
73 | def east(self) -> Point:
74 | """
75 | Returns a Point object representing the east point on the circle.
76 |
77 | ```python
78 | from tikzpy import TikzPicture
79 | tikz = TikzPicture()
80 | circle = tikz.circle((0,0), 1, options="fill=ProcessBlue!30", action="filldraw")
81 | tikz.node(circle.east, text="$E$", options="right")
82 | tikz.show()
83 | ```
84 |
85 |
86 | """
87 | return self._center + (self.radius, 0)
88 |
89 | @property
90 | def south(self) -> Point:
91 | """
92 | Returns a Point object representing the south point on the circle.
93 |
94 | ```python
95 | from tikzpy import TikzPicture
96 | tikz = TikzPicture()
97 | circle = tikz.circle((0,0), 1, options="fill=ProcessBlue!30", action="filldraw")
98 | tikz.node(circle.south, text="$S$", options="below")
99 | tikz.show()
100 | ```
101 |
102 |
103 | """
104 | return self._center - (0, self.radius)
105 |
106 | @property
107 | def west(self) -> Point:
108 | """
109 | Returns a Point object representing the west point on the circle.
110 |
111 | ```python
112 | from tikzpy import TikzPicture
113 | tikz = TikzPicture()
114 | circle = tikz.circle((0,0), 1, options="fill=ProcessBlue!30", action="filldraw")
115 | tikz.node(circle.west, text="$W$", options="left")
116 | tikz.show()
117 | ```
118 |
119 |
120 | """
121 | return self._center - (self.radius, 0)
122 |
123 | @property
124 | def _command(self) -> str:
125 | return f"{self._center} circle ({self.radius}cm)"
126 |
127 | def point_at_arg(self, theta: float, radians: bool = False) -> tuple:
128 | r"""Returns the point on the circle at angle theta. Both degrees and radians can be specified.
129 |
130 |
131 | ```python
132 | from tikzpy import TikzPicture
133 | tikz = TikzPicture(center=True)
134 | circle = tikz.circle((0,0), 1, options="fill=ProcessBlue!30", action="filldraw")
135 | tikz.node(circle.point_at_arg(45), text="$\pi/2$", options="right")
136 | tikz.node(circle.point_at_arg(135), text="$3\pi/4$", options="left")
137 | tikz.node(circle.point_at_arg(270), text="$3\pi/2$", options="below")
138 | tikz.show()
139 | ```
140 |
141 |
142 | """
143 | if not radians:
144 | theta = math.radians(theta)
145 | return self.center.x + self.radius * math.cos(
146 | theta
147 | ), self.center.y + self.radius * math.sin(theta)
148 |
149 | def shift_(self, xshift: float, yshift: float) -> None:
150 | self._center.shift_(xshift, yshift)
151 |
152 | def scale_(self, scale: float) -> None:
153 | self._center.scale_(scale)
154 | self.radius *= scale
155 |
156 | def rotate_(
157 | self, angle: float, about_pt: Tuple[float, float] = None, radians: bool = False
158 | ) -> None:
159 | self._center.rotate_(angle, about_pt, radians)
160 |
161 | def shift(self, xshift: float, yshift: float) -> "Circle":
162 | new_circle = self.copy()
163 | new_circle.shift_(xshift, yshift)
164 | return new_circle
165 |
166 | def scale(self, scale: float) -> "Circle":
167 | new_circle = self.copy()
168 | new_circle.scale_(scale)
169 | return new_circle
170 |
171 | def rotate(
172 | self, angle: float, about_pt: Tuple[float, float] = None, radians: bool = False
173 | ) -> "Circle":
174 | new_circle = self.copy()
175 | new_circle.rotate_(angle, about_pt, radians)
176 | return new_circle
177 |
--------------------------------------------------------------------------------
/src/tikzpy/drawing_objects/drawing_object.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 | from abc import ABC, abstractmethod
3 | from typing import Tuple
4 | from copy import deepcopy
5 | from tikzpy.utils.helpers import brackets
6 | from tikzpy.drawing_objects.node import Node
7 |
8 |
9 | class DrawingObject(ABC):
10 | r"""A generic class for our drawing objects to inherit properties from.
11 |
12 | Attributes :
13 | action (str) : A string containing either "draw", "filldraw", "fill", or "path". This controls
14 | the type of command statement we generate: \draw, \filldraw, \fill, or \path, ...
15 | By default, it is draw.
16 | options (str) : A string of valid Tikz options for the drawing object.
17 | command (str) : A string consisting of the latter half of our tikz code to create the full statement.
18 | node (Node object) : A Node object which can be appended to the end of the statement.
19 | """
20 |
21 | def __init__(self, action: str = "draw", options: str = "") -> None:
22 | self.action = action
23 | self.options = options
24 | self.node: Node = None
25 |
26 | if not isinstance(self.action, str):
27 | raise TypeError(f"The action argument {self.action} is not of type str")
28 |
29 | if self.action.replace(" ", "") not in ["draw", "fill", "filldraw", "path"]:
30 | raise ValueError(
31 | f"The action {self.action} is not a valid action ('draw', 'fill', 'filldraw', 'path'). Perhaps you mispelled it."
32 | )
33 |
34 | @property
35 | @abstractmethod
36 | def _command(self) -> str:
37 | r"""The latter half of the Tikz Code for the drawing object.
38 | E.g.: For a Line with code "\draw (0,0) to (1,1), _command corresponds to "(0,0) to (1,1)".
39 | """
40 |
41 | @abstractmethod
42 | def shift(self, xshift: float, yshift: float) -> "DrawingObject":
43 | """Shift the coordinates of the drawing object by (xshift, yshift)"""
44 |
45 | @abstractmethod
46 | def scale(self, scale: float) -> "DrawingObject":
47 | """Scale the coordinates of the drawing object by amount "scale"."""
48 |
49 | @abstractmethod
50 | def rotate(
51 | self, angle: float, about_pt: Tuple[float, float] = None, radians: bool = False
52 | ) -> "DrawingObject":
53 | """Rotate the coordinates of the drawing object (counterclockwise) by "angle" about the
54 | point "about_pt".
55 | """
56 |
57 | @property
58 | def code(self) -> str:
59 | """Full Tikz code for this drawing object."""
60 | draw_cmd = f"\\{self.action}{brackets(self.options)} {self._command}"
61 | if self.node is None:
62 | return f"{draw_cmd};"
63 | else:
64 | return f"{draw_cmd} node{brackets(self.node.options)} {self.node._command};"
65 |
66 | def add_node(
67 | self, position: tuple = None, options: str = "", text: str = ""
68 | ) -> None:
69 | """A method to build a node on a drawing object directly.
70 | This bypasses having to (1) define a Node object and then (2) use node.setter.
71 | """
72 | new_node = Node(position, options, text)
73 | self.node = new_node
74 |
75 | def __deepcopy__(self, memo: dict) -> DrawingObject:
76 | """Creates a deep copy of a class object. This is useful since in our classes, we chose to set
77 | our methods to modify objects, but not return anything.
78 | """
79 | cls = self.__class__
80 | draw_obj = cls.__new__(cls)
81 | memo[id(self)] = draw_obj
82 | for attr, value in self.__dict__.items():
83 | setattr(draw_obj, attr, deepcopy(value, memo))
84 | return draw_obj
85 |
86 | def copy(self, **kwargs: dict) -> DrawingObject:
87 | """Allows one to simultaneously make a (deep) copy of a drawing object and modify
88 | attributes of the drawing object in one step.
89 | """
90 | new_copy = deepcopy(self)
91 | for attr, val in kwargs.items():
92 | setattr(new_copy, attr, val)
93 | return new_copy
94 |
95 | def __repr__(self) -> str:
96 | return self.code
97 |
--------------------------------------------------------------------------------
/src/tikzpy/drawing_objects/drawing_utils.py:
--------------------------------------------------------------------------------
1 | import math
2 | from tikzpy.drawing_objects.line import Line
3 | from tikzpy.drawing_objects.circle import Circle
4 | from tikzpy.drawing_objects.point import Point
5 |
6 |
7 | def line_connecting_circle_edges(
8 | circle_a: Circle, circle_b: Circle, options="", src_delta=0, dst_delta=0
9 | ) -> Line:
10 | """
11 | Returns a line that connects the outer edges of circle_a
12 | to circle_b.
13 | """
14 | pos_a = circle_a.center
15 | pos_b = circle_b.center
16 | rad_a = circle_a.radius + src_delta
17 | rad_b = circle_b.radius + dst_delta
18 |
19 | start, end = calc_start_end_between_nodes(
20 | pos_a=pos_a, rad_a=rad_a, pos_b=pos_b, rad_b=rad_b
21 | )
22 | return Line(start, end, options=options)
23 |
24 |
25 | def calc_start_end_between_nodes(pos_a, rad_a, pos_b, rad_b):
26 | """
27 | Given two circles A and B with
28 | - coordinates pos_a, pos_b
29 | - radii rad_a, rad_b
30 | return the start and end coordinates of the shortest
31 | line that connects A and B.
32 | """
33 | x_1, y_1 = pos_a
34 | x_2, y_2 = pos_b
35 |
36 | # Determine the angle between the points
37 | if y_1 - y_2 == 0:
38 | theta = math.pi / 2
39 | else:
40 | theta = math.atan(abs(x_2 - x_1) / abs(y_1 - y_2))
41 |
42 | # Use the angle and relative sign of the coordinates to calculate x, y positions
43 | if y_2 > y_1:
44 | if x_2 > x_1:
45 | start = (x_1 + rad_a * math.sin(theta), y_1 + rad_a * math.cos(theta))
46 | end = (x_2 - rad_b * math.sin(theta), y_2 - rad_b * math.cos(theta))
47 | else:
48 | start = (x_1 - rad_a * math.sin(theta), y_1 + rad_a * math.cos(theta))
49 | end = (x_2 + rad_b * math.sin(theta), y_2 - rad_b * math.cos(theta))
50 | else:
51 | if x_2 > x_1:
52 | start = (x_1 + rad_a * math.sin(theta), y_1 - rad_a * math.cos(theta))
53 | end = (x_2 - rad_b * math.sin(theta), y_2 + rad_b * math.cos(theta))
54 | else:
55 | start = (x_1 - rad_a * math.sin(theta), y_1 - rad_a * math.cos(theta))
56 | end = (x_2 + rad_b * math.sin(theta), y_2 + rad_b * math.cos(theta))
57 | return start, end
58 |
59 |
60 | def draw_segments(tikz, points, circular=True, options=""):
61 | """
62 | Given a list of points, draw a sequence of line segments between the points.
63 | Returns a dictionary collection of the lines for later modification if necessary.
64 | """
65 | idx = 0
66 | lines = {}
67 | while idx < len(points):
68 | if idx == len(points) - 1:
69 | if not circular:
70 | break
71 | first_pt = points[-1]
72 | second_pt = points[0]
73 | else:
74 | first_pt = points[idx]
75 | second_pt = points[idx + 1]
76 |
77 | line = tikz.line(first_pt, second_pt, options=options)
78 | lines[(first_pt, second_pt)] = line
79 | idx += 1
80 | return lines
81 |
82 |
83 | def calc_intersection(item_a, item_b):
84 | intersection_map = {
85 | (Circle, Circle): circle_circle_intersection,
86 | (Line, Line): line_line_intersection,
87 | (Line, Circle): line_circle_intersection,
88 | (Circle, Line): circle_line_intersection,
89 | }
90 |
91 | func = intersection_map.get((type(item_a), type(item_b)))
92 | if func:
93 | return func(item_a, item_b)
94 | else:
95 | raise NotImplementedError(
96 | f"No intersection logic for {type(item_a)} and {type(item_b)}"
97 | )
98 |
99 |
100 | def circle_circle_intersection(circle_a, circle_b):
101 | intersections = _circle_circle_intersection(
102 | circle_a.center.x,
103 | circle_a.center.y,
104 | circle_a.radius,
105 | circle_b.center.x,
106 | circle_b.center.y,
107 | circle_b.radius,
108 | )
109 | return [Point(pt) for pt in intersections]
110 |
111 |
112 | def _circle_circle_intersection(x1, y1, r1, x2, y2, r2):
113 | # Distance between circle centers
114 | d = math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
115 |
116 | # No solutions if circles are separate or one completely contains the other
117 | if d > r1 + r2 or d < abs(r1 - r2) or d == 0:
118 | return None # No intersection
119 |
120 | # Find a and h for intersection calculations
121 | a = (r1**2 - r2**2 + d**2) / (2 * d)
122 | h = math.sqrt(r1**2 - a**2)
123 |
124 | # Find the point P2, which is the base point of the perpendicular
125 | x3 = x1 + a * (x2 - x1) / d
126 | y3 = y1 + a * (y2 - y1) / d
127 |
128 | # Intersection points
129 | x_int1 = x3 + h * (y2 - y1) / d
130 | y_int1 = y3 - h * (x2 - x1) / d
131 |
132 | x_int2 = x3 - h * (y2 - y1) / d
133 | y_int2 = y3 + h * (x2 - x1) / d
134 |
135 | # One intersection if circles touch at one point, otherwise two
136 | if d == r1 + r2 or d == abs(r1 - r2):
137 | return [(x_int1, y_int1)]
138 | return [(x_int1, y_int1), (x_int2, y_int2)]
139 |
140 |
141 | def line_line_intersection(line_a, line_b):
142 | intersections = _line_line_intersection(
143 | line_a.start.x,
144 | line_a.start.y,
145 | line_a.end.x,
146 | line_a.end.y,
147 | line_b.start.x,
148 | line_b.start.y,
149 | line_b.end.x,
150 | line_b.end.y,
151 | )
152 | return [Point(pt) for pt in intersections]
153 |
154 |
155 | def _line_line_intersection(x1, y1, x2, y2, x3, y3, x4, y4):
156 | # Compute coefficients A, B, C for both lines in form: Ax + By = C
157 | A1 = y2 - y1
158 | B1 = x1 - x2
159 | C1 = A1 * x1 + B1 * y1
160 |
161 | A2 = y4 - y3
162 | B2 = x3 - x4
163 | C2 = A2 * x3 + B2 * y3
164 |
165 | det = A1 * B2 - A2 * B1
166 | # Parallel lines
167 | if det == 0:
168 | return None
169 |
170 | x = (C1 * B2 - C2 * B1) / det
171 | y = (A1 * C2 - A2 * C1) / det
172 | return [(x, y)]
173 |
174 |
175 | def circle_line_intersection(circle, line):
176 | intersections = line_circle_intersection(line, circle)
177 | return [Point(pt) for pt in intersections]
178 |
179 |
180 | def line_circle_intersection(line, circle):
181 | m = line.slope()
182 | b = line.y_intercept()
183 | h, k = circle.center # Center of the circle
184 | r = circle.radius
185 | intersections = _line_circle_intersection(m, b, h, k, r)
186 | return [Point(pt) for pt in intersections]
187 |
188 |
189 | def _line_circle_intersection(m, b, h, k, r):
190 | # Quadratic equation coefficients (Ax^2 + Bx + C = 0)
191 | A = 1 + m**2
192 | B = 2 * (m * (b - k) - h)
193 | C = h**2 + (b - k) ** 2 - r**2
194 |
195 | # Discriminant
196 | D = B**2 - 4 * A * C
197 | if D < 0:
198 | # No intersection
199 | return None
200 | elif D == 0:
201 | # One intersection (tangent)
202 | x = -B / (2 * A)
203 | y = m * x + b
204 | return [(x, y)]
205 |
206 | sqrt_D = math.sqrt(D)
207 | x_1 = (-B + sqrt_D) / (2 * A)
208 | x_2 = (-B - sqrt_D) / (2 * A)
209 | y_1 = m * x_1 + b
210 | y_2 = m * x_2 + b
211 |
212 | return [(x_1, y_1), (x_2, y_2)]
213 |
--------------------------------------------------------------------------------
/src/tikzpy/drawing_objects/ellipse.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 | from typing import Tuple, Union
3 | from tikzpy.drawing_objects.point import Point
4 | from tikzpy.drawing_objects.drawing_object import DrawingObject
5 |
6 |
7 | class Ellipse(DrawingObject):
8 | r"""
9 | A class to create ellipses in the tikz environment.
10 |
11 | The ellipse class handles ellipses in Tikz. It it analagous to the Tikz command
12 | ```
13 | \draw[options]