├── huygens
├── doc
│ ├── __init__.py
│ ├── requirements.txt
│ ├── images
│ │ ├── Christiaan_Huygens.png
│ │ ├── output-0.svg
│ │ ├── empty.svg
│ │ ├── output-2.svg
│ │ ├── output-4.svg
│ │ ├── output-6.svg
│ │ ├── output-3.svg
│ │ ├── vbox-empty.svg
│ │ ├── output-9.svg
│ │ ├── table.svg
│ │ ├── obox.svg
│ │ ├── output-11.svg
│ │ ├── output-12.svg
│ │ ├── output-7.svg
│ │ ├── output-8.svg
│ │ ├── output-1.svg
│ │ ├── text.svg
│ │ ├── yang-baxter.svg
│ │ ├── canbox.svg
│ │ ├── braid-Z.svg
│ │ ├── table-2.svg
│ │ ├── braid-strands.svg
│ │ ├── vbox-text.svg
│ │ └── hbox-text.svg
│ ├── test_sat.py
│ ├── superstyle.css
│ ├── test_turtle.py
│ ├── test_canvas.py
│ ├── test_text.py
│ ├── index.md
│ ├── run_tests.py
│ ├── test_box.py
│ ├── mkdoc.py
│ ├── template.html
│ └── test_sat.html
├── .gitignore
├── all.py
├── wiggle
│ ├── __init__.py
│ ├── test_wiggle.py
│ └── shapes.py
├── tool.py
├── __init__.py
├── argv.py
├── variable.py
├── namespace.py
├── loadsvg.py
├── text.py
├── huytex.py
├── live.py
├── flatten.py
└── turtle.py
├── requirements.txt
├── doc
├── Makefile
├── turtle.py
├── canvas.py
└── make_nb.py
├── pyproject.toml
├── setup.py
└── README.md
/huygens/doc/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/huygens/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | __huygens__
3 | __pycache__
4 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | pycairo>=1.11.0
2 | cairosvg
3 | scipy
4 |
--------------------------------------------------------------------------------
/huygens/doc/requirements.txt:
--------------------------------------------------------------------------------
1 | scipy
2 | Pygments
3 | markdown
4 |
--------------------------------------------------------------------------------
/huygens/all.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | from huygens.namespace import *
4 |
5 |
--------------------------------------------------------------------------------
/huygens/doc/images/Christiaan_Huygens.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/punkdit/huygens/HEAD/huygens/doc/images/Christiaan_Huygens.png
--------------------------------------------------------------------------------
/huygens/wiggle/__init__.py:
--------------------------------------------------------------------------------
1 |
2 | from huygens import config, back
3 | if back.the_text_cls is back.CairoText:
4 | config(text="pdflatex", latex_header=r"""
5 | \usepackage{amsmath}
6 | \usepackage{amssymb}
7 | """)
8 |
9 | import huygens
10 | if huygens.EXPERIMENTAL:
11 | from huygens.wiggle.cell_experimental import *
12 | else:
13 | from huygens.wiggle.cell import *
14 |
15 | from huygens.wiggle.shapes import *
16 |
17 |
--------------------------------------------------------------------------------
/doc/Makefile:
--------------------------------------------------------------------------------
1 |
2 |
3 | canvas.html: canvas.ipynb
4 | jupyter nbconvert --to html canvas.ipynb
5 |
6 | canvas.ipynb: canvas.py
7 | ./make_nb.py convert_to_nb canvas.py canvas.ipynb
8 |
9 | turtle.html: turtle.ipynb
10 | jupyter nbconvert --to html turtle.ipynb
11 |
12 | turtle.ipynb: turtle.py
13 | ./make_nb.py convert_to_nb turtle.py turtle.ipynb
14 |
15 | wiggle.html: wiggle.ipynb
16 | jupyter nbconvert --to html wiggle.ipynb
17 |
18 | wiggle.ipynb: wiggle.py
19 | ./make_nb.py convert_to_nb wiggle.py wiggle.ipynb
20 |
21 |
22 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [build-system]
2 | requires = ["setuptools>=61.0"]
3 | build-backend = "setuptools.build_meta"
4 |
5 | [project]
6 | name = "huygens"
7 | version = "0.0.1"
8 | authors = [
9 | { name="Simon Burton", email="simon@arrowtheory.com" },
10 | ]
11 | description = "compositional vector graphics"
12 | readme = "README.md"
13 | requires-python = ">=3.0"
14 | classifiers = [
15 | "Programming Language :: Python :: 3",
16 | "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
17 | "Operating System :: OS Independent",
18 | ]
19 |
20 | [project.urls]
21 | "Homepage" = "https://github.com/punkdit/huygens"
22 | "Bug Tracker" = "https://github.com/punkdit/huygens/issues"
23 |
24 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import setuptools
4 |
5 | with open("README.md", "r") as fh:
6 | long_description = fh.read()
7 |
8 | setuptools.setup(
9 | name="huygens",
10 | version="0.1",
11 | author="Simon Burton",
12 | author_email="simon@arrowtheory.com",
13 | description="huygens graphic design package",
14 | long_description=long_description,
15 | long_description_content_type="text/markdown",
16 | url="https://github.com/punkdit/huygens",
17 | packages=setuptools.find_packages(),
18 | classifiers=[
19 | "Programming Language :: Python :: 3",
20 | "License :: OSI Approved :: GPL-3.0 License",
21 | "Operating System :: OS Independent",
22 | ],
23 | python_requires='>=3.5',
24 | )
25 |
26 |
--------------------------------------------------------------------------------
/huygens/doc/images/output-0.svg:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/huygens/tool.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | from random import random
4 |
5 | def rnd(a=0., b=1.):
6 | return (b-a)*random() + a
7 |
8 |
9 | def conv(a, b, alpha=0.5):
10 | return (1.-alpha)*a + alpha*b
11 |
12 |
13 | def smooth(val0, val1, alpha):
14 | #assert 0.<=alpha<=1.
15 | if alpha <= 0:
16 | return val0
17 | if alpha >= 1.:
18 | return val1
19 | s = -2*(alpha**3) + 3*(alpha**2) # 0.<=s<=1.
20 | val = (1-s)*val0 + s*val1 # conv
21 | return val
22 |
23 |
24 | def bump(val0, val1, alpha):
25 | if alpha <= 0.5:
26 | val = smooth(val0, val1, 2*alpha)
27 | else:
28 | val = smooth(val0, val1, 2*(1.-alpha))
29 | return val
30 |
31 |
32 | def clamp(value, vmin=0., vmax=1.):
33 | value = max(vmin, value)
34 | value = min(vmax, value)
35 | return value
36 |
37 |
38 |
--------------------------------------------------------------------------------
/huygens/doc/test_sat.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 |
4 |
5 | def test_sat():
6 |
7 | #
8 | # [<<< table of contents](index.html)
9 | #
10 | # ---
11 | #
12 | # Constraint satisfier
13 | # ====================
14 | #
15 | #
16 | #
17 |
18 |
19 | from huygens.sat import Variable, Solver, System
20 |
21 | x = Variable('x')
22 | y = Variable('y')
23 | z = Variable('z')
24 |
25 | items = [
26 | x+y >= 1.,
27 | x+z == 5,
28 | y >= 3.
29 | ]
30 |
31 | solver = Solver(items)
32 |
33 | result = solver.solve()
34 | print(result)
35 |
36 | system = System()
37 | v = system.get_var()
38 | u = system.get_var()
39 | w = system.get_var()
40 | system.add(v+u+w == 3.)
41 | system.solve()
42 | print(system[v] + system[u] + system[w])
43 |
44 |
45 | if __name__ == "__main__":
46 |
47 | test_variable()
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/huygens/doc/images/empty.svg:
--------------------------------------------------------------------------------
1 |
2 |
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | huygens
5 | =======
6 |
7 | This is a python package for drawing diagrams.
8 | Intended to have multiple backends, currently the only
9 | backend implemented uses [cairo](https://www.cairographics.org/).
10 |
11 | It can also include text from TeX via pdftex, pdflatex, xetex or xelatex.
12 |
13 | Diagrams are able to be composed in various ways, based
14 | on a linear constraint solver. Therefore, this system becomes
15 | a declarative graphics layout package.
16 |
17 | User guide
18 | ----------
19 |
20 | Read it online
21 | [here](https://arrowtheory.com/huygens/huygens/doc/index.html).
22 | The documentation is distributed in [huygens/doc](huygens/doc/).
23 |
24 | wiggle.py
25 | ----------
26 |
27 | This is a package for rendering string/surface diagrams
28 | for monoidal bicategories.
29 | Here are
30 | the slides from a [talk at SYCO 11](https://arrowtheory.com/wiggle.pdf),
31 | and there's more [demo code here](https://arrowtheory.com/wiggle_demo.pdf).
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/huygens/doc/images/output-2.svg:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/doc/turtle.py:
--------------------------------------------------------------------------------
1 |
2 | # ## Turtle graphics
3 | #
4 | # Use a turtle to keep track of a path as you build it.
5 |
6 | from huygens import canvas, style, color
7 | from huygens.turtle import Turtle
8 |
9 | cvs = canvas.canvas()
10 | turtle = Turtle(cvs=cvs)
11 |
12 | n = 8
13 | angle = 360. / n
14 | R = 3.0
15 | for i in range(n):
16 | turtle.fwd(1.*R)
17 | turtle.left((1./3)*angle)
18 | turtle.back(0.5*R)
19 | turtle.left((1./3)*angle)
20 | turtle.back(0.7*R)
21 | turtle.left((1./3)*angle)
22 | turtle.stroke()
23 | cvs
24 |
25 | # We can change the stroke style that the turtle uses
26 | # to a thick green line.
27 |
28 | attrs = [style.linewidth.THIck, color.rgb(0.2, 0.6, 0.2)]
29 |
30 | cvs = canvas.canvas()
31 | turtle = Turtle(cvs=cvs, attrs=attrs)
32 |
33 | turtle.fwd(2.)
34 | turtle.right(300, 1.)
35 | turtle.fwd(2.)
36 | turtle.arrow(0.4)
37 | turtle.stroke()
38 |
39 | cvs
40 |
41 | # Another example:
42 |
43 | cvs = canvas.canvas()
44 | turtle = Turtle(cvs=cvs, attrs=attrs)
45 |
46 | R = 1.0
47 | for i in range(24*2):
48 | turtle.left(320, 0.6*R)
49 | turtle.left(-60, 0.3*R)
50 | turtle.right(90, 0.6*R)
51 | turtle.stroke(attrs=attrs)
52 |
53 | cvs
54 |
55 |
--------------------------------------------------------------------------------
/huygens/doc/superstyle.css:
--------------------------------------------------------------------------------
1 | /* Base styles */
2 |
3 | * { -webkit-tap-highlight-color:rgba(0,0,0,0); }
4 |
5 |
6 | canvas {
7 | align-items: center;
8 | /* border: 2px solid #000000; */
9 | }
10 |
11 | footer {
12 | align-items: center;
13 | display: flex;
14 | justify-content: center;
15 | margin-top: 4em;
16 | text-align: center;
17 | }
18 |
19 | ol {
20 | list-style-type: disc;
21 | }
22 |
23 | /* 01 Centering */
24 |
25 | header,
26 | main {
27 | margin: 0 auto;
28 | max-width: 40em;
29 | }
30 |
31 | /* 03 Spacing */
32 |
33 | body {
34 | line-height: 1.5;
35 | }
36 |
37 | /* 04 Color and contrast */
38 |
39 | body {
40 | color: #333;
41 | }
42 |
43 | h1,
44 | h2,
45 | strong {
46 | color: #222;
47 | }
48 |
49 |
50 | code,
51 | pre {
52 | background: #eee;
53 | }
54 |
55 | code {
56 | padding: 2px 4px;
57 | vertical-align: text-bottom;
58 | }
59 |
60 | pre {
61 | padding: 1em;
62 | }
63 |
64 | /* 06 Primary color */
65 |
66 | /* a { color: #e81c4f; } */
67 |
68 |
69 |
70 | span.frac {
71 | display: inline-block;
72 | font-size: 80%;
73 | text-align: center;
74 | }
75 | span.frac > sup {
76 | display: block;
77 | border-bottom: 1.3px solid;
78 | font: inherit;
79 | }
80 | span.frac > span {
81 | display: none;
82 | }
83 | span.frac > sub {
84 | display: block;
85 | font: inherit;
86 | }
87 |
88 |
89 |
--------------------------------------------------------------------------------
/huygens/__init__.py:
--------------------------------------------------------------------------------
1 | from huygens import front, back
2 |
3 | from huygens.front import canvas, style, path, color, trafo, linestyle
4 | from huygens import box, diagram
5 | import huygens.text
6 |
7 | tex_header = None
8 | latex_header = None
9 | xelatex_header = None
10 | font_size = "11pt"
11 |
12 | _prev = {} # previous config
13 |
14 | def config(text=None, tex_header=None, latex_header=None, xelatex_header=None, font_size="11pt"):
15 |
16 | args = {
17 | "text":text,
18 | "tex_header":tex_header,
19 | "latex_header":latex_header,
20 | "xelatex_header":xelatex_header,
21 | "font_size":font_size,
22 | }
23 |
24 | #print("huygens.config(%r)"%(args,))
25 |
26 | if text is None:
27 | pass
28 | elif text == "cairo":
29 | back.the_text_cls = back.CairoText
30 | elif text in "pdftex xetex xelatex pdflatex".split():
31 | back.the_text_cls = back.MkText
32 | back.MkText.tex_engine = text
33 | else:
34 | raise Exception("config text option %r not understood" % text)
35 |
36 | huygens.text.tex_header = tex_header
37 | huygens.text.latex_header = latex_header
38 | huygens.text.xelatex_header = xelatex_header
39 | huygens.text.font_size = font_size
40 |
41 | global _prev
42 | prev = _prev
43 | _prev = args
44 |
45 | return prev
46 |
47 |
48 | EXPERIMENTAL = False
49 |
50 |
--------------------------------------------------------------------------------
/huygens/argv.py:
--------------------------------------------------------------------------------
1 |
2 | import sys
3 |
4 | class Argv(object):
5 | def __init__(self):
6 | self.args = sys.argv[:]
7 | self._args = []
8 | self.kw = self.argmap = {}
9 | for arg in sys.argv:
10 | if '=' in arg:
11 | items = arg.split('=')
12 | name, val = items[0], '='.join(items[1:])
13 | self.argmap[name] = self.parse(val)
14 | self.args.remove(arg)
15 |
16 | def next(self):
17 | return self.args.pop(1) if len(self.args)>1 else None
18 |
19 | def parse(self, value):
20 | try:
21 | return eval(value)
22 | except:
23 | return value
24 |
25 | def get(self, name, default=None):
26 | return self.argmap.get(name, default)
27 |
28 | def __str__(self):
29 | return ' '.join(sys.argv)
30 |
31 | def __getattr__(self, name):
32 | value = self.argmap.get(name)
33 | if value is None:
34 | if name in self.args:
35 | self.args.remove(name)
36 | value = True
37 | setattr(self, name, True)
38 | return value
39 |
40 | def __len__(self):
41 | return len(self.args)
42 |
43 | def __getitem__(self, index):
44 | return self.args[index]
45 |
46 | def __len__(self):
47 | return len(self.args)
48 |
49 |
50 | argv = Argv()
51 |
52 |
--------------------------------------------------------------------------------
/doc/canvas.py:
--------------------------------------------------------------------------------
1 |
2 | #
3 | # ## Canvas
4 | #
5 | # The canvas is a front-end drawing API
6 | # modelled after the amazing
7 | # [PyX](https://pyx-project.org/) package.
8 | #
9 | # The canvas coordinates are positive in the
10 | # upper right quadrant.
11 |
12 | from huygens import canvas, path, color
13 |
14 | cvs = canvas.canvas()
15 | cvs.stroke(path.line(0., 0., 3., 2.))
16 | cvs.fill(path.circle(3., 2., 0.2), [color.rgb.red])
17 | cvs.writeSVGfile("output.svg")
18 |
19 | # This produces the following SVG image:
20 |
21 | cvs
22 |
23 | # The canvas also has a `writePDFfile` method.
24 | #
25 | # Unlike PyX, angles are specified in
26 | # radians in calls to `path.arc` and `trafo.rotate`.
27 |
28 | from math import pi
29 | from huygens import style, trafo, linestyle
30 |
31 | cvs = canvas.canvas()
32 |
33 | cvs.stroke(path.circle(0., 0., 1.),
34 | [style.linewidth.thick, color.rgb.blue, linestyle.dashed])
35 | cvs.text(0., 0., "hey there!", [trafo.rotate(0.5*pi)])
36 |
37 | # Composite paths are built using the `path.path` constructor:
38 |
39 | cvs = canvas.canvas()
40 | p = path.path([
41 | path.moveto(0., 0.),
42 | path.arc(0., 0., 1., 0., 0.5*pi),
43 | path.lineto(-1., 1.), path.arc(-1., 0., 1., 0.5*pi, 1.0*pi),
44 | path.arc(-1.5, 0., 0.5, 1.0*pi, 2.0*pi), path.closepath() ])
45 |
46 | cvs.fill(p, [color.rgb.red, trafo.scale(1.2, 1.2)])
47 | cvs.stroke(p, [color.rgb.black, style.linewidth.THick])
48 |
49 | # Building these paths are easier using
50 | # the [turtle](turtle.html) module.
51 |
52 |
53 |
--------------------------------------------------------------------------------
/huygens/doc/images/output-4.svg:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/huygens/doc/test_turtle.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 |
4 | #st = [color.rgb.blue, style.linewidth.THick, style.linecap.round]
5 | #attrs = [style.linewidth.THIck, color.rgb(0.2, 0.6, 0.2, 0.6), style.linejoin.bevel]
6 |
7 |
8 | def test_turtle():
9 |
10 | #
11 | # [<<< table of contents](index.html)
12 | #
13 | # ---
14 | #
15 | # Turtle graphics
16 | # ===============
17 | # Use a turtle to keep track of a path as you build it.
18 |
19 | from huygens import canvas, style, color
20 | from huygens.turtle import Turtle
21 |
22 | cvs = canvas.canvas()
23 | turtle = Turtle(cvs=cvs)
24 |
25 | n = 8
26 | angle = 360. / n
27 | R = 3.0
28 | for i in range(n):
29 | turtle.fwd(1.*R)
30 | turtle.left((1./3)*angle)
31 | turtle.back(0.5*R)
32 | turtle.left((1./3)*angle)
33 | turtle.back(0.7*R)
34 | turtle.left((1./3)*angle)
35 | turtle.stroke()
36 |
37 | cvs.writeSVGfile("output.svg")
38 |
39 | yield cvs
40 |
41 | # We can change the stroke style that the turtle uses
42 | # to a thick green line.
43 |
44 | attrs = [style.linewidth.THIck, color.rgb(0.2, 0.6, 0.2)]
45 |
46 | cvs = canvas.canvas()
47 | turtle = Turtle(cvs=cvs, attrs=attrs)
48 |
49 | turtle.fwd(2.)
50 | turtle.right(300, 1.)
51 | turtle.fwd(2.)
52 | turtle.arrow(0.4)
53 | turtle.stroke()
54 |
55 | yield cvs
56 |
57 |
58 | cvs = canvas.canvas()
59 | turtle = Turtle(cvs=cvs, attrs=attrs)
60 |
61 | R = 1.0
62 | for i in range(24*2):
63 | turtle.left(320, 0.6*R)
64 | turtle.left(-60, 0.3*R)
65 | turtle.right(90, 0.6*R)
66 | turtle.stroke(attrs=attrs)
67 |
68 | yield cvs
69 |
70 |
--------------------------------------------------------------------------------
/huygens/doc/images/output-6.svg:
--------------------------------------------------------------------------------
1 |
2 |
12 |
--------------------------------------------------------------------------------
/huygens/doc/test_canvas.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 |
4 | #st = [color.rgb.blue, style.linewidth.THick, style.linecap.round]
5 |
6 |
7 | def test_canvas():
8 |
9 | #
10 | # [<<< table of contents](index.html)
11 | #
12 | # ---
13 | #
14 | # Canvas
15 | # ======
16 | #
17 | # The canvas is a front-end drawing API
18 | # modelled after the amazing
19 | # [PyX](https://pyx-project.org/) package.
20 | #
21 | # The canvas coordinates are positive in the
22 | # upper right quadrant.
23 |
24 | from huygens import canvas, path, color
25 |
26 | cvs = canvas.canvas()
27 | cvs.stroke(path.line(0., 0., 3., 2.))
28 | cvs.fill(path.circle(3., 2., 0.2), [color.rgb.red])
29 | cvs.writeSVGfile("output.svg")
30 |
31 | # This produces the following SVG image:
32 |
33 | yield cvs
34 |
35 | # The canvas also has a `writePDFfile` method.
36 | #
37 | # Unlike PyX, angles are specified in
38 | # radians in calls to `path.arc` and `trafo.rotate`.
39 |
40 | from math import pi
41 | from huygens import style, trafo, linestyle
42 |
43 | cvs = canvas.canvas()
44 |
45 | cvs.stroke(path.circle(0., 0., 1.),
46 | [style.linewidth.thick, color.rgb.blue, linestyle.dashed])
47 | cvs.text(0., 0., "hey there!", [trafo.rotate(0.5*pi)])
48 |
49 | yield cvs
50 |
51 | # Composite paths are built using the `path.path` constructor:
52 |
53 | cvs = canvas.canvas()
54 | p = path.path([
55 | path.moveto(0., 0.),
56 | path.arc(0., 0., 1., 0., 0.5*pi),
57 | path.lineto(-1., 1.), path.arc(-1., 0., 1., 0.5*pi, 1.0*pi),
58 | path.arc(-1.5, 0., 0.5, 1.0*pi, 2.0*pi), path.closepath() ])
59 |
60 | cvs.fill(p, [color.rgb.red, trafo.scale(1.2, 1.2)])
61 | cvs.stroke(p, [color.rgb.black, style.linewidth.THick])
62 |
63 | yield cvs
64 |
65 | # Building these paths are easier using
66 | # the [turtle](test_turtle.html) module.
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/huygens/variable.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 |
4 | class Variable(object):
5 | "Turn into a float at the _slightest provocation."
6 |
7 | def __str__(self):
8 | #return "%s(%s)"%(self.__class__.__name__, float(self)) # ?
9 | return str(float(self))
10 |
11 | def __repr__(self):
12 | ks = list(self.__dict__.keys())
13 | ks.sort()
14 | ns = ", ".join("%s=%s"%(k,self.__dict__[k]) for k in ks)
15 | return "%s(%s, %s, %s)"%(self.__class__.__name__, id(self), float(self), ns)
16 |
17 | def __lt__(self, other):
18 | return float(self) < other
19 |
20 | def __le__(self, other):
21 | return float(self) <= other
22 |
23 | def __eq__(self, other):
24 | return float(self) == other
25 |
26 | def __gt__(self, other):
27 | return float(self) > other
28 |
29 | def __ge__(self, other):
30 | return float(self) >= other
31 |
32 | def __add__(self, other):
33 | return other + float(self)
34 | __radd__ = __add__
35 |
36 | def __sub__(self, other):
37 | return float(self) - other
38 |
39 | def __rsub__(self, other):
40 | return other - float(self)
41 |
42 | def __mul__(self, other):
43 | return float(self) * other
44 | __rmul__ = __mul__
45 |
46 | def __truediv__(self, other):
47 | return float(self) / other
48 | __floordiv__ = __truediv__
49 |
50 | def __rtruediv__(self, other):
51 | return other / float(self)
52 | __rfloordiv__ = __rtruediv__
53 |
54 | def __mod__(self, other):
55 | return float(self) % other
56 |
57 | def __rmod__(self, other):
58 | return other % float(self)
59 |
60 | def __pow__(self, other):
61 | return float(self) ** other
62 |
63 | def __rpow__(self, other):
64 | return other ** float(self)
65 |
66 | def __neg__(self):
67 | return -float(self)
68 |
69 | def __pos__(self):
70 | return float(self)
71 |
72 | def __abs__(self):
73 | return abs(float(self))
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/huygens/doc/test_text.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | from huygens import config
4 | #config(text="xetex") # works, but pdf-->svg buggy on my system, lines too thick
5 | config(text="xelatex") # works
6 | #config(text="pdftex") # works
7 | #config(text="pdflatex") # works
8 |
9 | from huygens.front import *
10 |
11 |
12 | north = [text.halign.boxcenter, text.valign.top]
13 | northeast = [text.halign.boxright, text.valign.top]
14 | northwest = [text.halign.boxleft, text.valign.top]
15 | south = [text.halign.boxcenter, text.valign.bottom]
16 | southeast = [text.halign.boxright, text.valign.bottom]
17 | southwest = [text.halign.boxleft, text.valign.bottom]
18 | east = [text.halign.boxright, text.valign.middle]
19 | west = [text.halign.boxleft, text.valign.middle]
20 | center = [text.halign.boxcenter, text.valign.middle]
21 |
22 |
23 | x, y = 0., 0.
24 | h = 0.7
25 |
26 | def show(text, attrs=[]):
27 | global x, y
28 | r = 0.1
29 | st = [color.rgb.red]
30 | cvs.stroke(path.line(x, y-r, x, y+r), st)
31 | cvs.stroke(path.line(x-r, y, x+r, y), st)
32 | cvs.text(x, y, text, attrs)
33 | y -= h
34 |
35 |
36 | cvs = canvas.canvas()
37 |
38 | show("great!")
39 | show("great!", [text.halign.boxright])
40 | show("great!", [text.halign.boxcenter])
41 |
42 | if 1:
43 | x, y = 2., 0.
44 | show("great!", [text.valign.top])
45 | show("great!", [text.valign.middle])
46 | show("great!", [text.valign.bottom])
47 |
48 | x, y = 6., 0.
49 | show("north", north)
50 | show("northeast", northeast)
51 | show("northwest", northwest)
52 | show("south", south)
53 | show("southeast", southeast)
54 | show("southwest", southwest)
55 | show("east", east)
56 | show("west", west)
57 | show("center", center)
58 |
59 | if 1:
60 | x, y = 10., 0
61 | show("tiny", [text.size.tiny])
62 | show("script", [text.size.script])
63 | show("footnote", [text.size.footnote])
64 | show("small", [text.size.small])
65 | show("normal", [text.size.normal])
66 | show("large", [text.size.large])
67 | show("Large", [text.size.Large])
68 | show("LARGE", [text.size.LARGE])
69 | show("huge", [text.size.huge])
70 | show("Huge", [text.size.Huge])
71 |
72 |
73 | cvs.writePDFfile("text.pdf")
74 |
75 |
76 |
--------------------------------------------------------------------------------
/huygens/doc/images/output-3.svg:
--------------------------------------------------------------------------------
1 |
2 |
14 |
--------------------------------------------------------------------------------
/huygens/doc/images/vbox-empty.svg:
--------------------------------------------------------------------------------
1 |
2 |
15 |
--------------------------------------------------------------------------------
/huygens/namespace.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | from huygens.front import *
4 | from huygens.turtle import mkpath
5 |
6 |
7 | def conv(alpha, a, b):
8 | return (1.-alpha)*a + alpha*b
9 |
10 |
11 | red = color.rgb(1.0, 0.0, 0.0)
12 | darkred = color.rgb(0.8, 0.2, 0.2)
13 | green = color.rgb(0.0, 0.6, 0.0)
14 | yellow = color.rgb(1.0, 1.0, 0.0)
15 | blue = color.rgb(0.2, 0.2, 0.7)
16 | white = color.rgb(1., 1., 1.)
17 | grey = color.rgb(0.8, 0.8, 0.8)
18 | darkgrey = color.rgb(0.5, 0.5, 0.5)
19 | black = color.rgb(0, 0, 0)
20 |
21 | st_red = [red]
22 | st_darkred = [darkred]
23 | st_green = [green]
24 | st_blue = [blue]
25 | st_white = [white]
26 | st_grey = [grey]
27 | st_darkgrey = [darkgrey]
28 | st_black = [black]
29 |
30 | st_bevel = st_joinbevel = [style.linejoin.bevel]
31 | st_miter = st_joinmiter = [style.linejoin.miter]
32 | st_joinround = [style.linejoin.round]
33 | st_butt = st_capbutt = [style.linecap.butt]
34 | st_capround = [style.linecap.round]
35 | st_square = st_capsquare = [style.linecap.square]
36 |
37 | st_round = [style.linecap.round, style.linejoin.round]
38 |
39 | st_dashed = [style.linestyle.dashed]
40 | st_dotted = [style.linestyle.dotted]
41 |
42 | st_north = [text.halign.boxcenter, text.valign.top]
43 | st_northeast = [text.halign.boxright, text.valign.top]
44 | st_northwest = [text.halign.boxleft, text.valign.top]
45 | st_south = [text.halign.boxcenter, text.valign.bottom]
46 | st_southeast = [text.halign.boxright, text.valign.bottom]
47 | st_southwest = [text.halign.boxleft, text.valign.bottom]
48 | st_east = [text.halign.boxright, text.valign.middle]
49 | st_west = [text.halign.boxleft, text.valign.middle]
50 | st_hcenter = [text.halign.boxcenter]
51 | st_center = [text.halign.boxcenter, text.valign.middle]
52 | st_hcenter = [text.halign.boxcenter]
53 |
54 | st_footnote = [text.size.footnote] # does not work...??
55 | st_small = [text.size.small] # does not work...??
56 | st_large = [text.size.large] # does not work...??
57 |
58 | st_barrow = [deco.barrow]
59 | st_arrow = [deco.earrow]
60 | st_marrow = [deco.marrow]
61 |
62 | orange = color.rgb(0.8, 0.2, 0.0)
63 | st_arrow = [deco.earrow()]
64 | st_rarrow = [deco.earrow(reverse=True)]
65 |
66 | thin = style.linewidth.thin
67 | normal = style.linewidth.normal
68 | st_normal = [normal]
69 | st_THIN = [style.linewidth.THIN]
70 | st_THIn = [style.linewidth.THIn]
71 | st_THin = [style.linewidth.THin]
72 | st_Thin = [style.linewidth.Thin]
73 | st_thin = [style.linewidth.thin]
74 | st_thick = [style.linewidth.thick]
75 | st_Thick = [style.linewidth.Thick]
76 | st_THick = [style.linewidth.THick]
77 | st_THIck = [style.linewidth.THIck]
78 | st_THICk = [style.linewidth.THICk]
79 | st_THICK = [style.linewidth.THICK]
80 |
81 |
82 | lw = style.linewidth
83 |
84 |
--------------------------------------------------------------------------------
/huygens/loadsvg.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | """
4 | Hijack cairosvg to load svg into our internal data structures.
5 |
6 | Does not work on all svg files, but seems good enough for text.
7 | """
8 |
9 | from cairosvg.parser import Tree
10 | from cairosvg.surface import Surface
11 |
12 | from huygens.argv import argv
13 | from huygens import back
14 | from huygens.flatten import Flatten
15 |
16 |
17 | class DummySurf(Surface):
18 |
19 | def __init__(self, tree, output, dpi, context=None):
20 |
21 | W, H = 600., 200. # point == 1/72 inch
22 |
23 | if context is None:
24 | context = Flatten()
25 | self.context = context
26 |
27 | self.dpi = dpi
28 |
29 | self._old_parent_node = self.parent_node = None
30 | self.output = output
31 | self.font_size = None
32 |
33 | self.context_width = W
34 | self.context_height = H
35 |
36 | self.cursor_position = [0, 0]
37 | self.cursor_d_position = [0, 0]
38 | self.text_path_width = 0
39 | self.stroke_and_fill = True
40 |
41 | self.tree_cache = {(tree.url, tree.get('id')): tree}
42 |
43 | self.markers = {}
44 | self.gradients = {}
45 | self.patterns = {}
46 | self.masks = {}
47 | self.paths = {}
48 | self.filters = {}
49 |
50 | self.map_rgba = None
51 | self.map_image = None
52 |
53 | self.draw(tree)
54 |
55 | #surface.finish()
56 |
57 | self.paths = self.context.paths
58 |
59 |
60 | class SkipColors(Flatten):
61 | def set_source_rgba(self, r, g, b, a):
62 | pass
63 |
64 |
65 | def loadsvg(name, dpi=72., keep_colors=True):
66 | assert name.endswith(".svg")
67 | s = open(name).read()
68 | tree = Tree(bytestring=s)
69 | if keep_colors:
70 | context = Flatten()
71 | else:
72 | context = SkipColors()
73 | dummy = DummySurf(tree, None, dpi, context=context)
74 | item = back.Compound(dummy.paths)
75 | return item
76 |
77 |
78 |
79 | def loadtex(filename, width=1.):
80 | # XXX does not work very well... XXX
81 | from huygens.front import Canvas, Translate, Scale
82 | tex = loadsvg(filename)
83 | bb = tex.get_bound_box()
84 | llx, lly, urx, ury = bb
85 |
86 | conv = lambda a,b:(a+b)/2
87 | x0, y0 = conv(llx, urx), conv(lly, ury)
88 | w, h = urx-llx, ury-lly
89 |
90 | X0, Y0 = 0., 0.
91 | cvs = Canvas()
92 | cvs.append(Translate(X0-x0, Y0-y0))
93 | cvs.append(Scale(width/w, width/w, x0, y0))
94 | cvs.append(tex)
95 | return cvs
96 |
97 |
98 |
99 |
100 | if __name__ == "__main__":
101 |
102 | name = argv.next()
103 | s = open(name).read()
104 | tree = Tree(bytestring=s)
105 | my = DummySurf(tree, None, 72.)
106 | from huygens.front import Canvas
107 | cvs = Canvas(my.paths)
108 | if 0:
109 | for item in cvs:
110 | print(item.__class__)
111 | for sub in item:
112 | print(sub)
113 | print()
114 | cvs.writePDFfile("output.pdf")
115 | cvs.writeSVGfile("output.svg")
116 |
117 |
118 |
119 |
120 |
121 |
122 |
--------------------------------------------------------------------------------
/huygens/doc/index.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | huygens
5 | =======
6 |
7 | This is a python package for drawing diagrams.
8 | Intended to have multiple backends, currently the only
9 | backend implemented uses [cairo](https://www.cairographics.org/).
10 | Perhaps there will also be a [PyX](https://pyx-project.org/) backend, or a
11 | [TikZ](https://ctan.org/pkg/pgf?lang=en) backend.
12 |
13 | The huygens package can also include text from $\TeX$ via pdftex, pdflatex, xetex or xelatex.
14 |
15 | The [huygens.box](test_box.html) and [huygens.diagram](test_diagram.html) modules build on the basic graphics primitives
16 | to define composable elements.
17 | Geometry and layout of these elements is generated using a linear constraint solver.
18 | Therefore, this system becomes a declarative graphics layout package.
19 |
20 | User guide
21 | ----------
22 |
23 | __[huygens](test_canvas.html)__
24 | Basic drawing functions. Lines, curves, text and so on.
25 |
26 | __[huygens.turtle](test_turtle.html)__
27 | A simple Turtle class for sequentially building paths.
28 | Also fun.
29 |
30 | __[huygens.sat](test_sat.html)__
31 | A convenient interface to a linear programming constraint solver.
32 | Currently uses
33 | [scipy.optimize.linprog](https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.linprog.html)
34 |
35 | __[huygens.box](test_box.html)__
36 | Structure figures into rectangular `Box`s.
37 | Various ways of combining these: `HBox`, `VBox`, `TableBox`, etc.
38 | The layout is determined by constraints, and so this uses
39 | the `huygens.sat` module.
40 |
41 | __[huygens.diagram](test_diagram.html)__
42 | Building on the `box` module to make string diagrams.
43 |
44 | See also
45 | --------
46 |
47 | Some related projects:
48 |
49 | [Foundations of brick diagrams](https://arxiv.org/abs/1908.10660)
50 | Describes horizontal and vertical composition of string diagrams.
51 |
52 | [diagrams](https://archives.haskell.org/projects.haskell.org/diagrams/)
53 | A declarative, domain-specific language written in Haskell.
54 |
55 | [Compose.jl](https://github.com/GiovineItalia/Compose.jl)
56 | A declarative vector graphics library for Julia.
57 |
58 | [Experiments in Constraint-based Graphic Design](https://www.anishathalye.com/2019/12/12/constraint-based-graphic-design/#case-studies)
59 | Describes a DSL called "Basalt" written in Python, based on
60 | the logic of non-linear real arithmetic, which is an
61 | extension of linear programming. No code available.
62 |
63 | [Penrose](http://penrose.ink)
64 | "Create beautiful diagrams just by typing mathematical notation in plain text."
65 | [Source here](https://github.com/penrose/penrose)
66 | Written in Haskell. Users not welcome (yet).
67 |
68 | [Graphviz](https://graphviz.org/)
69 | Declarative language for rendering heirarchical data structures.
70 | [Apparently](https://news.ycombinator.com/item?id=23477034)
71 | "20 year old code that was basically a prototype that escaped from the lab".
72 | See also
73 | [this blog post.](https://ncona.com/2020/06/create-diagrams-with-code-using-graphviz/)
74 |
75 |
76 | [TikZ](https://ctan.org/pkg/pgf?lang=en)
77 | Possibly the most widely used package for creating
78 | graphics for integration with latex documents.
79 |
80 | [Asymptote](https://asymptote.sourceforge.io/)
81 | A custom language for doing vector graphics inspired by MetaPost.
82 |
83 | [manim](https://github.com/3b1b/manim)
84 | Primarily used for animations, this library also has
85 | an interface to latex. It also is a python library using pycairo.
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/huygens/text.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import os
4 | import hashlib
5 | from subprocess import Popen, PIPE
6 |
7 |
8 | def file_exists(name):
9 | try:
10 | os.stat(name)
11 | return True
12 | except:
13 | pass
14 | return False
15 |
16 |
17 | def verbose_command(cmd):
18 | ret = os.system(cmd)
19 | assert ret == 0, "%r failed with return value %d"%(cmd, ret)
20 |
21 |
22 | def command(cmd):
23 | p = Popen(cmd, shell=True,
24 | stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True)
25 | (child_stdin,
26 | child_stdout,
27 | child_stderr) = (p.stdin, p.stdout, p.stderr)
28 | ret = p.wait()
29 | assert ret == 0, "%r failed with return value %d"%(cmd, ret)
30 |
31 | command = verbose_command
32 |
33 | tex_header = None
34 | latex_header = None
35 | xelatex_header = None
36 | font_size = "11pt"
37 |
38 | def tex_output(text):
39 | lines = []
40 | lines.append(r"\def\folio{}")
41 | if tex_header:
42 | lines += tex_header.split("\n")
43 | lines.append(r"%s\bye"%text)
44 | return '\n'.join(lines)
45 |
46 |
47 | def latex_output(text):
48 | lines = []
49 | lines.append(r"\documentclass[%s]{article}"%font_size)
50 | lines.append(r"\pagenumbering{gobble}")
51 | lines.append(r"\def\folio{}")
52 | if latex_header:
53 | lines += latex_header.split("\n")
54 | lines.append(r"\begin{document}")
55 | lines.append(text)
56 | lines.append(r"\end{document}")
57 | return '\n'.join(lines)
58 |
59 |
60 | def xelatex_output(text):
61 | lines = []
62 | lines.append(r"\documentclass[%s]{article}"%font_size)
63 | lines.append(r"\pagenumbering{gobble}")
64 | lines.append(r"\usepackage{fontspec}")
65 | lines.append(r"\setromanfont{Gentium Book Basic}")
66 | lines.append(r"\def\folio{}")
67 | if xelatex_header:
68 | lines += xelatex_header.split("\n")
69 | lines.append(r"\begin{document}")
70 | lines.append(text)
71 | lines.append(r"\end{document}")
72 | return '\n'.join(lines)
73 |
74 |
75 | def make_text(text, tex_engine="pdftex"):
76 | assert tex_engine in "pdftex xetex xelatex pdflatex".split()
77 | cache = "__huygens__"
78 | if not file_exists(cache):
79 | os.mkdir(cache)
80 |
81 | os.chdir(cache) # <---------- chdir <-----
82 |
83 | try:
84 |
85 | if tex_engine == "pdftex" or tex_engine == "xetex":
86 | output = tex_output(text)
87 | elif tex_engine=="pdflatex":
88 | output = latex_output(text)
89 | elif tex_engine == "xelatex":
90 | output = xelatex_output(text)
91 | else:
92 | assert 0, tex_engine
93 |
94 | data = output.encode('utf-8')
95 | stem = hashlib.sha1(data).hexdigest()
96 |
97 | tex_name = "%s.tex"%stem
98 | svg_name = "%s.svg"%stem
99 | pdf_name = "%s.pdf"%stem
100 |
101 | if not file_exists(svg_name):
102 |
103 | f = open(tex_name, 'w')
104 | print(output, file=f)
105 | f.close()
106 |
107 | command("%s %s"%(tex_engine, tex_name))
108 | command("pdf2svg %s %s" % (pdf_name, svg_name))
109 |
110 | from huygens import loadsvg
111 | item = loadsvg.loadsvg(svg_name)
112 |
113 | finally:
114 |
115 | os.chdir("..") # <---------- chdir <-----
116 |
117 | return item
118 |
119 |
120 | def test():
121 |
122 | item = make_text(".")
123 | print(item)
124 |
125 |
126 | if __name__ == "__main__":
127 |
128 | test()
129 |
130 |
--------------------------------------------------------------------------------
/huygens/doc/run_tests.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | """
4 | Note: use mkdoc.py to rebuild all docs & images.
5 | """
6 |
7 | import os
8 | import collections
9 |
10 | import huygens.doc
11 | from huygens.front import Canvas, Scale, Base
12 | from huygens.box import Box
13 |
14 |
15 | class TestRun(Base):
16 | def __init__(self, func, start=None, end=None, img=None, result=None):
17 | self.func = func
18 | self.start = start
19 | self.end = end
20 | self.img = img
21 | self.result = result
22 |
23 |
24 | all_names = set()
25 | counter = 0
26 |
27 |
28 | def run_test(func, dummy=False):
29 |
30 | global counter
31 |
32 | items = func()
33 |
34 | if not isinstance(items, collections.abc.Iterator):
35 | yield TestRun(func, func.__code__.co_firstlineno, result=items)
36 | return
37 |
38 | start = items.gi_frame.f_lineno # index
39 |
40 | while 1:
41 | try:
42 | box = None
43 | cvs = None
44 | name = None
45 |
46 | result = items.__next__()
47 |
48 | if isinstance(result, tuple):
49 | result, name = result
50 |
51 | if isinstance(result, Box):
52 | box = result
53 | elif isinstance(result, Canvas):
54 | cvs = result
55 | else:
56 | assert 0, "%r not understood" % (result,)
57 |
58 | if not name:
59 | name = "output-%d"%counter
60 | counter += 1
61 |
62 | assert name not in all_names, "name dup: %r"%name
63 | all_names.add(name)
64 |
65 | svgname = "images/%s.svg"%name
66 | pdfname = "images/%s.pdf"%name
67 |
68 | end = items.gi_frame.f_lineno-1 # index
69 | test = TestRun(func, start, end, svgname)
70 | yield test
71 | start = end+1
72 |
73 | if dummy:
74 | svgname = "/dev/null"
75 | pdfname = "/dev/null"
76 |
77 | try:
78 | print("run_tests: rendering", name, func)
79 | if cvs is None:
80 | cvs = Canvas()
81 | cvs.append(Scale(2.0))
82 | box.render(cvs)
83 | else:
84 | cvs = Canvas([Scale(2.0), cvs])
85 |
86 | cvs.writeSVGfile(svgname)
87 | #cvs.writePDFfile(pdfname) # pdf's change on every write :-(
88 | print()
89 | except:
90 | print("run_tests: render failed for",
91 | name, func.__name__, "line", end)
92 | raise
93 |
94 | except StopIteration:
95 | break
96 |
97 |
98 |
99 | def harvest(path, name, dummy=False):
100 | print("run_tests.harvest", name)
101 | assert name.endswith(".py")
102 | stem = name[:-len(".py")]
103 | desc = "huygens.doc."+stem
104 | __import__(desc)
105 | m = getattr(huygens.doc, stem)
106 | funcs = []
107 | for attr in dir(m):
108 | value = getattr(m, attr)
109 | if attr.startswith("test_") and isinstance(value, collections.abc.Callable):
110 | funcs.append(value)
111 |
112 | funcs.sort(key = lambda f : (f.__module__, f.__code__.co_firstlineno))
113 | for func in funcs:
114 | for test in run_test(func, dummy=dummy):
115 | yield test
116 |
117 |
118 | def run():
119 |
120 | path = os.path.dirname(__file__)
121 | names = os.listdir(path)
122 | names = [name for name in names
123 | if name.endswith(".py") and name.startswith("test_")]
124 | names.sort()
125 |
126 | for name in names:
127 | for test in harvest(path, name, True):
128 | yield test
129 |
130 |
131 | def main():
132 | for test in run():
133 | pass
134 |
135 | print("run_tests.main: finished")
136 |
137 |
138 | if __name__ == "__main__":
139 | main()
140 |
141 |
142 |
--------------------------------------------------------------------------------
/doc/make_nb.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import sys
4 |
5 | import json
6 |
7 | import nbformat
8 | from nbformat.v4 import new_notebook, new_code_cell, new_markdown_cell, new_output
9 |
10 | import papermill
11 |
12 | from huygens.argv import argv
13 |
14 |
15 | def save_nb(nb, name):
16 | f = open(name, 'w')
17 | json.dump(nb, f, indent=1)
18 | f.close()
19 |
20 |
21 | def main():
22 |
23 | nb = new_notebook()
24 | append = lambda s : nb.cells.append(new_code_cell(s))
25 |
26 | append("a=1")
27 | append("a+5")
28 |
29 | append("from huygens.namespace import *")
30 | append("from huygens.wiggle import Cell0, Cell1, Cell2")
31 | append("pink = color.rgba(1.0, 0.37, 0.36, 0.5)")
32 | append("Cell0('n', fill=pink)")
33 |
34 | save_nb(nb, "test.ipynb")
35 | nb = papermill.execute_notebook("test.ipynb", None, kernel_name="python3")
36 | save_nb(nb, "test_exec_1.ipynb")
37 |
38 | if 0:
39 | text, image = ['text/plain', 'image/svg+xml']
40 | #print(nb)
41 | for cell in nb.cells:
42 | for output in cell["outputs"]:
43 | data = output["data"]
44 | #print(list(data.keys()))
45 | if image in data and text in data:
46 | item = data[text]
47 | del data[text]
48 | data[text] = [item]
49 | if image in data:
50 | item = data[image]
51 | item = [item+"\n" for item in item.split("\n")]
52 | data[image] = item
53 |
54 |
55 |
56 | def convert_to_py(name, output=None):
57 |
58 | assert name.endswith(".ipynb")
59 |
60 | s = open(name).read()
61 | nb = nbformat.reads(s, as_version=4)
62 |
63 | output = sys.stdout if output is None else open(output, 'w')
64 |
65 | for cell in nb.cells:
66 | #print(list(cell.keys()))
67 | source = cell["source"]
68 | tp = cell["cell_type"]
69 | if tp == "markdown":
70 | lines = source.split('\n')
71 | source = '\n'.join("# "+line for line in lines)
72 | print(source+"\n", file=output)
73 |
74 | def convert_to_nb(name, output):
75 |
76 | assert name.endswith(".py")
77 | assert output and output.endswith(".ipynb")
78 |
79 | s = open(name).read()
80 | lines = s.split('\n')
81 |
82 | lines.append("# ") # trigger last code block
83 |
84 | nb = new_notebook()
85 | add_code = lambda s : nb.cells.append(new_code_cell(s))
86 | add_md = lambda s : nb.cells.append(new_markdown_cell(s))
87 |
88 | code = None
89 | md = None
90 | for line in lines:
91 | if line.startswith("# ") and md is None:
92 | if code is not None:
93 | while code and not code[-1].strip():
94 | code.pop()
95 | if code:
96 | code = '\n'.join(code)
97 | add_code(code)
98 | code = None
99 | md = []
100 | if line.startswith("# "):
101 | assert code is None
102 | line = line[2:]
103 | md.append(line)
104 | else:
105 | if code is None:
106 | if md is not None:
107 | md = '\n'.join(md)
108 | if md.strip():
109 | add_md(md)
110 | md = None
111 | code = []
112 | if code or line.strip():
113 | code.append(line)
114 |
115 |
116 | save_nb(nb, output)
117 | nb = papermill.execute_notebook(output, None, kernel_name="python3")
118 | save_nb(nb, output)
119 |
120 |
121 |
122 | if __name__ == "__main__":
123 |
124 | if argv.convert_to_py:
125 | convert_to_py(argv.next(), argv.next())
126 |
127 | elif argv.convert_to_nb:
128 | convert_to_nb(argv.next(), argv.next())
129 |
130 |
131 |
--------------------------------------------------------------------------------
/huygens/doc/images/output-9.svg:
--------------------------------------------------------------------------------
1 |
2 |
17 |
--------------------------------------------------------------------------------
/huygens/doc/images/table.svg:
--------------------------------------------------------------------------------
1 |
2 |
21 |
--------------------------------------------------------------------------------
/huygens/huytex.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | """
4 | replace %py lines with appropriate latex commands,
5 | and exec the %py
6 | """
7 |
8 | import os
9 | import hashlib
10 |
11 | def exists(name):
12 | try:
13 | open(name).close()
14 | return True
15 | except OSError:
16 | return False
17 |
18 |
19 | #import config
20 | #config.weld = False
21 | #from config import *
22 |
23 |
24 | state = []
25 | stems = set()
26 | lookup = {}
27 |
28 | match_py = "%py "
29 | match_PY = "%PY "
30 | GRI = r"\raisebox{-0.4\height}{\includegraphics{%s/%s.pdf}}%%"
31 | GRD = r"{\begin{center}\includegraphics{%s/%s.pdf}\end{center}}%%"
32 |
33 | suspend = False
34 | def process(line, ns):
35 | global suspend
36 | if "get_latex" not in ns:
37 | ns["get_latex"] = get_latex
38 |
39 | if match_py in line and line.index("%") == line.index(match_py):
40 | idx = line.index(match_py)
41 | cmd = GRI
42 | #size = 0.4
43 | elif match_PY in line and line.index("%") == line.index(match_PY):
44 | idx = line.index(match_PY)
45 | cmd = GRD
46 | #size = 1.0
47 | else:
48 | return line
49 |
50 | pre = line[:idx]
51 | post = line[idx+4:]
52 |
53 | if not post.strip():
54 | # nothing to do here
55 | return pre+"\n" if pre else pre # <------ return
56 |
57 | if post.strip() == "%py exit":
58 | suspend = True # arfff...
59 | return pre+"\n" if pre else pre # <------ return
60 |
61 | data = state
62 | try:
63 | #print("eval: %r"%post)
64 | value = eval(post, ns, ns)
65 | if value is None: # function call...
66 | state.append(post)
67 | return pre+"\n" if pre else pre # <------ return
68 | # WARNING: we _assume expr's don't mutate state
69 | # otherwise, uncomment the next line:
70 | #state.append(post)
71 | data = state + [post]
72 | except SyntaxError:
73 | exec(post, ns, ns)
74 | state.append(post)
75 | value = None
76 | return pre+"\n" if pre else pre # <------ return
77 | except:
78 | print("proc.py failed at %r"%(post,))
79 | raise
80 |
81 | if hasattr(value, "_latex_"):
82 | value = value._latex_()
83 |
84 | if type(value) is str:
85 | return pre + value
86 | #value = value.replace("_", r"\_")
87 | #return r"{\tt %s}"%(value,)
88 |
89 | data = '\n'.join(data)
90 | data = data.encode('utf-8')
91 | stem = hashlib.sha1(data).hexdigest()
92 |
93 | if stem not in stems:
94 | name = "%s/%s.pdf"%(imdir, stem,)
95 | if not exists(name):
96 | try:
97 | save(stem, value, imdir=imdir) # monkeypatch from local config
98 | except:
99 | print("proc.py failed at %r"%(post,))
100 | raise
101 | stems.add(stem)
102 |
103 | latex = cmd%(imdir, stem,) + "\n"
104 | lookup[id(value)] = latex
105 | line = pre + latex
106 |
107 | return line
108 |
109 |
110 | def get_latex(value, cmd=GRI, **kw):
111 | key = id(value)
112 | if key in lookup:
113 | return lookup[key]
114 | data = str(key).encode('utf-8') # ?
115 | stem = hashlib.sha1(data).hexdigest()
116 | save(stem, value, imdir=imdir, **kw) # monkeypatch from local config
117 | latex = cmd%(imdir, stem,) + "\n"
118 | lookup[id(value)] = latex
119 | return latex
120 |
121 |
122 | def main(tgt, src, ns={}):
123 | global imdir
124 |
125 | f = open(src)
126 | if exists(tgt):
127 | print("file %r exists!"%tgt)
128 | return
129 |
130 | g = open(tgt, 'w')
131 |
132 | assert tgt.endswith(".tex")
133 | imdir = tgt[:-len(".tex")] + ".imcache"
134 | try:
135 | os.mkdir(imdir)
136 | except FileExistsError:
137 | pass
138 |
139 | for line in f.readlines():
140 | line = process(line, ns)
141 | print(line, end="", file=g)
142 |
143 | if line.strip() == r"\end{document}":
144 | break
145 |
146 |
147 | if __name__ == "__main__":
148 |
149 | import sys
150 | for item in sys.argv[3:]:
151 | state.append(open(item).read())
152 |
153 | main(sys.argv[1], sys.argv[2])
154 |
155 |
--------------------------------------------------------------------------------
/huygens/doc/images/obox.svg:
--------------------------------------------------------------------------------
1 |
2 |
22 |
--------------------------------------------------------------------------------
/huygens/doc/test_box.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 |
4 | def test_box():
5 |
6 |
7 | #
8 | # [<<< table of contents](index.html)
9 | #
10 | # ---
11 | #
12 | # Layout with Box's
13 | # =================
14 | #
15 |
16 | from random import random, seed
17 | seed(0)
18 | from huygens.front import canvas, path
19 | from huygens.box import (Box, EmptyBox, CanBox, TextBox,
20 | HBox, VBox, OBox, TableBox, FillBox, MarginBox, AlignBox)
21 |
22 | # First we set a debug flag so we can see the shape of every box
23 | #
24 |
25 | Box.DEBUG = True
26 |
27 | # Every Box has an anchor point, shown with a cross.
28 | # Then, the distance to the four sides of the box
29 | # are stored as attributes `top`, `bot`, `left`, and `right`.
30 |
31 | box = EmptyBox(top=0.5, bot=1.0, left=0.2, right=2.)
32 |
33 | # To render a box onto a canvas:
34 | cvs = canvas.canvas()
35 | box.render(cvs)
36 |
37 | # We can then call `cvs.writeSVGfile("output.svg")` to save an svg file:
38 |
39 | yield box, "empty"
40 |
41 | #--------------------------------------------------
42 |
43 | box = TextBox("Hey there!")
44 | yield box, "text"
45 |
46 | #--------------------------------------------------
47 |
48 | cvs = canvas.canvas()
49 | cvs.stroke(path.line(0., 0., 1., 1.))
50 | cvs.text(0., 0., "hello everyone")
51 | box = CanBox(cvs)
52 | yield box, 'canbox'
53 |
54 | #--------------------------------------------------
55 |
56 | # You cannot use the same box more than once in a container Box:
57 |
58 | box = TextBox("hello")
59 | box = HBox([box, box]) # FAIL
60 | #yield box, "hbox-fail" # raises assert error
61 |
62 | #--------------------------------------------------
63 |
64 | box = HBox("geghh xxde xyeey".split())
65 | yield box, "hbox-text"
66 |
67 | #--------------------------------------------------
68 |
69 | box = VBox("geghh xxde xyeey".split())
70 | yield box, "vbox-text"
71 |
72 | #--------------------------------------------------
73 |
74 | r = 1.0
75 | a = EmptyBox(top=r, bot=r)
76 | b = EmptyBox(top=r, bot=r)
77 | c = EmptyBox(left=r, right=r)
78 | #box = StrictVBox([a, c])
79 | box = VBox([a, c])
80 | yield box, 'vbox-empty'
81 |
82 | #--------------------------------------------------
83 |
84 | box = OBox([
85 | EmptyBox(.4, .1, 0., 2.2),
86 | EmptyBox(.3, 0., .5, 2.5),
87 | EmptyBox(1., .5, .5, .5),
88 | FillBox(.2, .2),
89 | ])
90 | yield box, "obox"
91 |
92 |
93 | #--------------------------------------------------
94 |
95 | box = HBox([
96 | VBox([TextBox(text) for text in "xxx1 ggg2 xxx3 xx4".split()]),
97 | VBox([TextBox(text) for text in "123 xfdl sdal".split()]),
98 | ])
99 | yield box, "hbox-vbox"
100 |
101 |
102 | #--------------------------------------------------
103 |
104 | box = TableBox([
105 | [EmptyBox(.4, .1, 0.2, 2.2), EmptyBox(.3, 1.2, .5, 2.5),],
106 | [EmptyBox(.8, .1, 0.4, 1.2), EmptyBox(.5, 0.4, .5, 1.5),]
107 | ])
108 | yield box, "table"
109 |
110 |
111 | #--------------------------------------------------
112 |
113 | def rnd(a, b):
114 | return (b-a)*random() + a
115 |
116 | a, b = 0.2, 1.0
117 | rows = []
118 | for row in range(3):
119 | row = []
120 | for col in range(3):
121 | box = EmptyBox(rnd(a,b), rnd(a,b), rnd(a,b), rnd(a,b))
122 | row.append(box)
123 | rows.append(row)
124 |
125 | box = TableBox(rows)
126 | yield box, "table-2"
127 |
128 |
129 | #--------------------------------------------------
130 |
131 | rows = []
132 | for i in range(3):
133 | row = []
134 | for j in range(3):
135 | box = TextBox(("xbcgef"[i+j])*(i+1)*(j+1))
136 | box = MarginBox(box, 0.1)
137 | box = AlignBox(box, "north")
138 | row.append(box)
139 | row.append(EmptyBox(bot=1.))
140 | rows.append(row)
141 | box = TableBox(rows)
142 | yield box, "table-3"
143 |
144 |
145 | #--------------------------------------------------
146 |
147 | a, b = 0.2, 2.0
148 | rows = []
149 | for row in range(2):
150 | boxs = []
151 | for col in range(2):
152 | top = rnd(a, b)
153 | bot = rnd(a, b)
154 | left = rnd(a, b)
155 | right = rnd(a, b)
156 | box = EmptyBox(top, bot, left, right)
157 | boxs.append(box)
158 | boxs.append(TextBox("Hig%d !"%row))
159 | box = HBox(boxs)
160 | rows.append(box)
161 | box = VBox(rows)
162 | yield box, "table-4"
163 |
164 |
165 |
166 |
167 |
--------------------------------------------------------------------------------
/huygens/doc/images/output-11.svg:
--------------------------------------------------------------------------------
1 |
2 |
24 |
--------------------------------------------------------------------------------
/huygens/doc/images/output-12.svg:
--------------------------------------------------------------------------------
1 |
2 |
24 |
--------------------------------------------------------------------------------
/huygens/doc/images/output-7.svg:
--------------------------------------------------------------------------------
1 |
2 |
24 |
--------------------------------------------------------------------------------
/huygens/doc/images/output-8.svg:
--------------------------------------------------------------------------------
1 |
2 |
24 |
--------------------------------------------------------------------------------
/huygens/doc/images/output-1.svg:
--------------------------------------------------------------------------------
1 |
2 |
47 |
--------------------------------------------------------------------------------
/huygens/wiggle/test_wiggle.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | #from huygens.wiggle import cell_experimental
4 | #assert 0
5 | import huygens
6 | huygens.EXPERIMENTAL = True
7 |
8 | from huygens.namespace import *
9 | from huygens.wiggle import Cell0, Cell1, Cell2
10 | from huygens.wiggle.strict import l_unit, mul, comul, pull_over, unit, braid_over, pull_over, pull_over_i, assoc
11 |
12 | import warnings
13 | warnings.filterwarnings('ignore')
14 |
15 | pink = color.rgba(1.0, 0.37, 0.36, 0.5)
16 | cream = color.rgba(1.0, 1.0, 0.92, 0.5)
17 | cyan = color.rgba(0.0, 0.81, 0.80, 0.5)
18 | yellow = color.rgba(1.0, 0.80, 0.3, 0.5)
19 |
20 | grey = color.rgba(0.75, 0.75, 0.75, 0.5)
21 | red = color.rgba(1., 0.2, 0.2, 0.5)
22 | green = color.rgba(0.3, 0.7, 0.2, 0.5)
23 | blue = color.rgba(0.2, 0.2, 0.7, 0.5)
24 |
25 | scheme = [grey, blue, yellow, red, pink] # colour scheme
26 |
27 |
28 | def dump(cell):
29 | cell = cell.translate()
30 | print(cell)
31 | for path in cell.get_paths():
32 | print("\t", ' '.join(str(c) for c in path))
33 |
34 |
35 | def test_compose():
36 | M = Cell0("M", fill=scheme[0])
37 | N = Cell0("N", fill=scheme[1])
38 |
39 | mul2 = lambda M,N : (mul(M)@mul(N)) << (M.i @ braid_over(N,M) @ N.i)
40 | src = mul2(M,N) << (mul2(M,N) @ (M.i@N.i))
41 | tgt = mul2(M,N) << ((M.i@N.i) @ mul2(M,N))
42 | top = (mul(M).i @ mul(N).i) << (M.i.i @ pull_over_i(N, mul(M)) @ mul(N).i) << (M.i.i@N.i.i@M.i.i@braid_over(N,M).i@N.i.i)
43 | mid = ((mul(M)<<(M.i@mul(M))).i @ assoc(N)) << (M.i@M.i@braid_over(N,M)@N.i@N.i).i << (M.i@braid_over(N,M)@braid_over(N,M)@N.i).i
44 | bot = (assoc(M)@mul(N).i) << (M.i.i@M.i.i @ pull_over(mul(N), M) @ N.i.i) << (M.i.i@braid_over(N,M).i@N.i.i@M.i.i@N.i.i)
45 |
46 | # these don't compose because we need to insert identity 1-cells
47 | cell = top * mid
48 | cell = mid * bot
49 | cell.render_cvs()
50 |
51 |
52 | #counter = 0
53 | #def save(cell):
54 | # global counter
55 |
56 | def save(cell, name):
57 | print("writePDFfile: %s.pdf"%name)
58 | cell = cell.layout() # ARGHHH
59 | cell.render_cvs(pos="northeast").writePDFfile(name)
60 | print()
61 | print("_"*79)
62 |
63 |
64 | def test_strict():
65 | M = Cell0("M", fill=scheme[0])
66 | N = Cell0("N", fill=scheme[1])
67 |
68 | f = Cell1(N, M)
69 | m_mm = mul(M)
70 | n_nn = mul(N)
71 | m_ = unit(M)
72 | n_ = unit(N)
73 |
74 | f_mul = Cell2(f << m_mm, n_nn << (f@f), pip_color=blue)
75 | f_unit = Cell2(f << m_, n_, pip_color=blue)
76 |
77 | mid = f_mul << (unit(M).i @ M.i.i)
78 | bot = mul(N).i << (f_unit @ f.i) << (M.i.i)
79 |
80 | #bot = (f_unit @ f.i) << (M.i.i)
81 |
82 | # print("\n\ntranslate")
83 | # bot.translate()
84 |
85 | save(mid, "test_mid")
86 | save(bot, "test_bot")
87 | return
88 |
89 | print("mid*bot")
90 | cell = mid * bot
91 |
92 | print("\n\n")
93 | save(cell, "test_strict")
94 |
95 |
96 |
97 | def test_compose_units():
98 | M = Cell0("M", fill=scheme[0])
99 | N = Cell0("N", fill=scheme[1])
100 |
101 | cell = (M @ unit(N)) << (M.i << M.i)
102 | save(cell, "test_0")
103 |
104 | cell = M.i
105 | cell = cell.insert_identity_src(1)
106 |
107 | unitor = Cell2(M.i, mul(M)<<(unit(M) @ M))
108 | cell = unitor << Cell1(M,M, stroke=blue, pip_color=blue).i
109 | save(cell, "test_unitor")
110 |
111 | #cell = unitor << mul(M)
112 | src = mul(M) << comul(M)
113 | cell = Cell2(M.i, src)
114 | #cell = cell.v_op()
115 | counitor = unitor.h_op()
116 | cell = unitor << counitor
117 | bot = cell.v_op()
118 | cell = cell * bot
119 | save(cell, "test_mul")
120 |
121 | cell = M @ unit(N) @ M
122 | cell = cell << comul(M)
123 | cell = cell.translate()
124 | print(cell.src)
125 | for path in cell.get_paths():
126 | print(path)
127 | #for cell in path:
128 | # if isinstance(cell, Cell0) and cell.is_identity():
129 | # print(repr(cell))
130 | assert len(list(cell.get_paths())) == 3
131 | save(cell, "test_1")
132 |
133 | return
134 |
135 | cell = (unit(N) @ M) << M
136 | dump(cell)
137 | cell = (M @ unit(N) @ M) << (M @ M)
138 | dump(cell)
139 | return
140 |
141 | # ((M<---M)@((N<---N@N)<<((N<---ident)@(N<---N))))
142 | # (((M<---M)@(N<---N@N))<<((((M<---M)@(N<---ident))<<(M<---M))@(N<---N)))
143 |
144 | bot = (l_unit(M) @ mul(N).i) << (pull_over(unit(N), M) @ N.i.i)
145 | top = (M.i.i @ l_unit(N))
146 |
147 | lhs = top.src.translate()
148 | rhs = bot.tgt.translate()
149 |
150 | dump(lhs)
151 | dump(rhs)
152 |
153 | dump(rhs[1])
154 | dump(rhs[1][0])
155 |
156 | return
157 |
158 | # compose fail
159 | cvs = Canvas([top.render_cvs(pos="northeast"), Translate(6,0), bot.render_cvs(pos="northeast")])
160 | cvs.writePDFfile("compose.pdf")
161 |
162 | cell = top * bot
163 | cvs = cell.render_cvs()
164 |
165 | # compose fail
166 | #top.d_rev()
167 | #bot.d_rev()
168 |
169 |
170 |
171 | def test_level():
172 |
173 | m, n = Cell0("m"), Cell0("n")
174 | A = Cell1(m, n)
175 | B = Cell1(m, n)
176 | f = Cell2(B, A)
177 |
178 | mn = m @ n
179 | assert mn.level == 0
180 |
181 | mA = m @ A
182 | assert mA.level == 1
183 | assert mA[0].level == 1
184 | assert mA[1].level == 1
185 |
186 | Am = A @ m
187 | assert Am.level == 1
188 | assert Am[0].level == 1
189 | assert Am[1].level == 1
190 |
191 |
192 |
193 |
194 | if __name__ == "__main__":
195 |
196 | #test_strict()
197 | #test_level()
198 | test_compose_units()
199 |
200 | print("OK\n")
201 |
202 |
203 |
--------------------------------------------------------------------------------
/huygens/doc/images/text.svg:
--------------------------------------------------------------------------------
1 |
2 |
52 |
--------------------------------------------------------------------------------
/huygens/doc/mkdoc.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import os
4 | import collections
5 |
6 | import markdown
7 | from pygments.formatters import HtmlFormatter
8 | from pygments import highlight
9 | from pygments.lexers import Python3Lexer
10 |
11 | from huygens.argv import argv
12 | from huygens.doc import run_tests
13 | from huygens.box import Box
14 |
15 |
16 | def html_head(s):
17 | html = """\
18 |
19 |
20 |
%s
\n"%body 60 | 61 | def html_pre(body): 62 | return "%s\n"%body 63 | 64 | def html_img(name): 65 | return '
", file=output)
140 | traceback.print_exc(file=output)
141 | print("", file=output)
142 | traceback.print_exc()
143 |
144 | print(html_tail(), file=output)
145 | output.close()
146 | Box.DEBUG = False
147 |
148 |
149 | def html_code(lines):
150 | s = '\n'.join(lines)
151 | if not s.strip():
152 | return ""
153 | return highlight(s, Python3Lexer(), HtmlFormatter())
154 |
155 | COMMENT = "# "
156 |
157 | def html_comment(lines):
158 | lines = [line[len(COMMENT):] for line in lines]
159 | s = '\n'.join(lines)
160 | #return html_p(s)
161 |
162 | md = markdown.Markdown()
163 | html = md.convert(s)
164 | return html
165 |
166 |
167 | def html_snip(lines):
168 |
169 | lines = dedent(lines)
170 |
171 | code = []
172 | comment = []
173 | idx = 0
174 | while idx < len(lines):
175 | line = lines[idx]
176 | if line.startswith(COMMENT):
177 | if code:
178 | yield html_code(code)
179 | code = []
180 | comment.append(line)
181 | elif line.startswith("#") and comment:
182 | assert not code
183 | comment.append(line)
184 | elif line.startswith("#"):
185 | pass
186 | else:
187 | if comment:
188 | yield html_comment(comment)
189 | comment = []
190 | code.append(line)
191 | idx += 1
192 |
193 | if code:
194 | yield html_code(code)
195 |
196 | if comment:
197 | yield html_comment(comment)
198 |
199 |
200 | def get_indent(line):
201 | i = 0
202 | while line.startswith(' '*i):
203 | i += 1
204 | space = ' '*(i-1)
205 | return space
206 |
207 |
208 | def find_dedent(lines, idx):
209 |
210 | space = None
211 | while idx < len(lines):
212 | line = lines[idx]
213 | if line.strip():
214 | indent = get_indent(lines[idx])
215 | if space is None:
216 | space = indent
217 | if len(indent) < len(space):
218 | break
219 | idx += 1
220 | return idx
221 |
222 |
223 | def dedent(lines):
224 | indent = None
225 | assert lines
226 | for line in lines:
227 | if not line.strip():
228 | continue
229 | space = get_indent(line)
230 | if indent is None or indent.startswith(space):
231 | indent = space
232 | if indent is None:
233 | return []
234 | lines = [line[len(indent):] for line in lines]
235 | return lines
236 |
237 |
238 | if __name__ == "__main__":
239 | main()
240 | print("mkdoc: OK")
241 |
242 |
243 |
--------------------------------------------------------------------------------
/huygens/wiggle/shapes.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | """
4 |
5 | make some shapes: pair of pants, tube's, etc.
6 |
7 | """
8 |
9 | from huygens.namespace import *
10 | from huygens.argv import argv
11 |
12 | from huygens import pov
13 | from huygens.pov import View, Mat
14 |
15 | from huygens.wiggle import Cell0, Cell1, Cell2
16 |
17 | def make_pants_rev(m=None, i=None, cone=1.0):
18 |
19 | if i is None:
20 | i = Cell0("i", stroke=None)
21 | if m is None:
22 | m = Cell0("m", fill=grey)
23 |
24 | mm = Cell1(m(skip=True), m(skip=True),
25 | stroke=None, pip_color=None,
26 | _width=0.001, skip=True)
27 | m_m = Cell1(m, m, stroke=None, pip_color=None)
28 | i_mm = Cell1(i, m@m, pip_color=None)
29 | mm_i = Cell1(m@m, i, pip_color=None)
30 | saddle = Cell2( mm_i<from huygens.sat import Variable, Solver, System
106 |
107 | x = Variable('x')
108 | y = Variable('y')
109 | z = Variable('z')
110 |
111 | items = [
112 | x+y >= 1.,
113 | x+z == 5,
114 | y >= 3.
115 | ]
116 |
117 | solver = Solver(items)
118 |
119 | result = solver.solve()
120 | print(result)
121 |
122 | system = System()
123 | v = system.get_var()
124 | u = system.get_var()
125 | w = system.get_var()
126 | system.add(v+u+w == 3.)
127 | system.solve()
128 | print(system[v] + system[u] + system[w])
129 |