-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmod-mult-circle.html
400 lines (352 loc) · 32.9 KB
/
mod-mult-circle.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="generator" content="Pelican" />
<title>Modular Multiplication Circle with Python</title>
<link rel="stylesheet" href="https://yashaslokesh.github.io/theme/css/main.css" />
<link href="https://yashaslokesh.github.io/feeds/all.atom.xml" type="application/atom+xml" rel="alternate" title="Yashas's Blog Atom Feed" />
<meta name="description" content="In this article, we create a modular multiplication circle visualization using pycairo." />
</head>
<body id="index" class="home">
<header id="banner" class="body">
<h1><a href="https://yashaslokesh.github.io/">Yashas's Blog</a></h1>
<nav><ul>
<li><a href="https://yashaslokesh.github.io/pages/about-me.html">About</a></li>
<li><a href="https://yashaslokesh.github.io/category/misc.html">misc</a></li>
<li class="active"><a href="https://yashaslokesh.github.io/category/python.html">Python</a></li>
</ul></nav>
</header><!-- /#banner -->
<section id="content" class="body">
<article>
<header>
<h1 class="entry-title">
<a href="https://yashaslokesh.github.io/mod-mult-circle.html" rel="bookmark"
title="Permalink to Modular Multiplication Circle with Python">Modular Multiplication Circle with Python</a></h1>
</header>
<div class="entry-content">
<footer class="post-info">
<abbr class="published" title="2020-08-26T20:10:00-04:00">
Published: Wed 26 August 2020
</abbr>
<br />
<abbr class="modified" title="2020-08-26T20:10:00-04:00">
Updated: Wed 26 August 2020
</abbr>
<address class="vcard author">
By <a class="url fn" href="https://yashaslokesh.github.io/author/yashas-lokesh.html">Yashas Lokesh</a>
</address>
<p>In <a href="https://yashaslokesh.github.io/category/python.html">Python</a>.</p>
<p>tags: <a href="https://yashaslokesh.github.io/tag/python.html">python</a> <a href="https://yashaslokesh.github.io/tag/pycairo.html">pycairo</a> <a href="https://yashaslokesh.github.io/tag/math.html">math</a> </p>
</footer><!-- /.post-info --> <h2>Intro</h2>
<p>I was browsing <a href="https://www.reddit.com/r/pygame">r/pygame</a>
when I saw <a href="https://www.reddit.com/r/pygame/comments/ekfcor/modular_multiplication_circle/">this post</a>,
and I was instantly entranced by the curious shapes being created by this visualization.
The creator linked to <a href="https://demonstrations.wolfram.com/ModularMultiplicationOnACircle/">this demonstration</a>,
and I decided to recreate the image in Python. I usually use <a href="https://www.pygame.org/news"><code>pygame</code></a>, but the creator
used pygame to generate their images. I wanted to try something different, and I've been meaning to learn
<a href="https://pycairo.readthedocs.io/en/latest/"><code>pycairo</code></a>, so I figured this would be the perfect opportunity: I can learn
this graphics library by make a cool-looking math visual.</p>
<h2>The Circle</h2>
<p>To create a modular multiplication circle, you pick two numbers; let's call them <span class="math">\(mod\)</span> and
<span class="math">\(mult\)</span>. The circle we create will have <span class="math">\(mod\)</span> number of points. To each point, we just assign
each point a number from 0 to <span class="math">\(mod - 1\)</span>. For example, if <span class="math">\(mod = 60\)</span>, then we number
the points from 0 to 59, starting at the top of the circle and going clockwise.</p>
<p>Why did I choose to start numbering at the top of the circle and go clock-wise? There was
no particular reason. You can pick to choose to start at any other position around the
circle and to go clockwise or counter-clockwise.</p>
<p>So then for each point in the circle (call this the starting point),
take the number representing that point, multiply it by <span class="math">\(mult\)</span> and
find the remainder when you divide it by <span class="math">\(mod\)</span>.
Find the point represented by the remainder (the ending point), and connect
the two points.</p>
<p>This is the algorithm for creating the visualization. Choosing a different number
of points and varying the multiplier can make chaotic patterns or more beautiful
patterns. If you vary the color of the lines based on which point
you're on, you can get a nice color palette, too.</p>
<p>I created a prototype with pygame with 1986 points and a multiplier
of 997. I've since forgotten how exactly I picked the colors,
but it looks like red and green show prominently, so
perhaps I fixed blue while moving from red to green clockwise
around the circle.</p>
<p><img alt="1986-point mod circle with mult of 997" src="https://yashaslokesh.github.io/images/1986_997.jpeg" style="max-width: 500px"></p>
<p>Now we'll go over how to do this with pycairo.</p>
<h2>Setup</h2>
<p>I tried using <code>pipenv</code> with PyCharm for this project, but you can use any up-to-date virtual environment library.
For creating multiplication circles with an arbitrary amount of points, I thought the easiest way to determine starting
and ending points for different numbers would be to rotate a vector. One way I thought of doing this was by using <code>numpy</code>.</p>
<p>I installed <code>pycairo</code> and <code>numpy</code> in my environment and opened up a file named <code>cairo_circle.py</code>. At the top of the file,
I made my standard imports:</p>
<p>File: <code>cairo_circle.py</code></p>
<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">cairo</span>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span>
</code></pre></div>
<h2>Starting with PyCairo</h2>
<p>I went through the <code>pycairo</code> introduction and quickly learned that all of the drawing is done with contexts. A context
is created from a surface, and we can use the context object's methods to move around a point and then draw lines or shapes.</p>
<p>To get started and draw a line, I used this code:</p>
<p>File: <code>cairo_circle.py</code></p>
<div class="highlight"><pre><span></span><code><span class="n">size</span> <span class="o">=</span> <span class="p">(</span><span class="mi">500</span><span class="p">,)</span> <span class="o">*</span> <span class="mi">2</span>
<span class="k">with</span> <span class="n">cairo</span><span class="o">.</span><span class="n">ImageSurface</span><span class="p">(</span><span class="n">cairo</span><span class="o">.</span><span class="n">Format</span><span class="o">.</span><span class="n">ARGB32</span><span class="p">,</span> <span class="o">*</span><span class="n">size</span><span class="p">)</span> <span class="k">as</span> <span class="n">surface</span><span class="p">:</span>
<span class="n">context</span> <span class="o">=</span> <span class="n">cairo</span><span class="o">.</span><span class="n">Context</span><span class="p">(</span><span class="n">surface</span><span class="p">)</span>
<span class="n">context</span><span class="o">.</span><span class="n">set_source_rgb</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">context</span><span class="o">.</span><span class="n">set_line_width</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">context</span><span class="o">.</span><span class="n">move_to</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">context</span><span class="o">.</span><span class="n">line_to</span><span class="p">(</span><span class="mi">250</span><span class="p">,</span> <span class="mi">250</span><span class="p">)</span>
<span class="n">context</span><span class="o">.</span><span class="n">stroke</span><span class="p">()</span>
<span class="n">surface</span><span class="o">.</span><span class="n">write_to_png</span><span class="p">(</span><span class="s1">'circle_1.png'</span><span class="p">)</span>
</code></pre></div>
<p>Let's go over this code before showing the final result:</p>
<p>Every surface has to have a size specified. Here I choose a size of 500x500 so
that the image won't look too grainy and won't be too large of an image</p>
<p>The format I chose will create an image with an alpha channel, so any part
of the image that isn't drawn over with lines will be transparent, so the circle we
create can be laid on top of other images.</p>
<p>Next, we create a context object using by instantiating the <code>cairo.Context</code> class with our surface.
Then we set the drawing color to be white; the arguments have to be floats between 0 and 1.
If you have colors with values 0-255, just divide by 255 to get the corresponding float value.
We set the line width to be 1, and then move to the top-left corner of the screen.
<code>pycairo</code>'s origin is in the top-left, and the x- and y- values increase to the right and down.
Then we draw a line to the middle of the screen and call <code>context.stroke()</code>.</p>
<p>Finally, to see the result of our efforts, we save the <code>ImageSurface</code> to <code>circle_1.png</code>, which gives us:</p>
<p><img alt="circle 1" src="https://yashaslokesh.github.io/images/circle_1.png"></p>
<p><strong>NOTE:</strong> I used the <code>RGB24</code> format here instead of <code>ARGB32</code>, so that the colors show properly.</p>
<p>Great! These are all the tools we need to work with Pycairo.</p>
<h2>Calculating Coordinates of Points</h2>
<p>Now, given a point around the circle, we want to be able to find the coordinates
for that point. To do this, we can find the angle of the point around
the circle, rotate a vector by that angle, and then move the base of the
vector to the center of the Cairo context. The vector then gives us the
coordinates.</p>
<p>If we have a variable <span class="math">\(mod\)</span> and a number <span class="math">\(num\)</span>, we can find the angle to the point
representing that number by calculating <code>angle = ((2.0 * np.pi) / mod) * num</code>.
One full rotation around the circle is equivalent
to <span class="math">\(2pi\)</span> radians, so we divide by <span class="math">\(mod\)</span> to get the correct angle
partitions for our circle. Then we multiply by <span class="math">\(num\)</span> to get to the
<span class="math">\(num\)</span> point.</p>
<p>The top of the circle has <span class="math">\(num = 0\)</span>, so the angle will be 0. So we need a vector
that will straight upwards. We can define this
vector as <code>start_vec = np.array([0, -240])</code>. We use a negative
y-value so that the vector will point upwards. Remember
that Cairo's coordinate system has the origin
in the top-left and x- and y-values increase
to the right and down.</p>
<p>Now we need to rotate this starting vector by our angle. According to
the <a href="https://en.wikipedia.org/wiki/Rotation_matrix">Rotation matrix article on Wikipedia</a>,
the matrix
</p>
<div class="math">$$
M = \begin{pmatrix}
\cos\theta & -\sin\theta \\
\sin\theta & \cos\theta \\
\end{pmatrix}
$$</div>
<p>multiplied by our starting vector will give us the desired vector.
So in our code, we can create the matrix using:
<code>m = np.array([[np.cos(angle), -np.sin(angle)], [np.sin(angle), np.cos(angle)]])</code>
and then we can obtain our desired vector by
multiplying this matrix by the starting vector:
<code>rot_vec = np.matmul(m, start_vec)</code>.</p>
<p>The center of our Cairo context is <code>(250, 250)</code>. We can calculate this value
from the size: <code>center = (size[0] // 2,) * 2</code>. Then, we get the
final vector using <code>vec = rot_vec + center</code>.</p>
<p>Let's put this all into a function called <code>get_coords_for_num</code>:</p>
<div class="highlight"><pre><span></span><code><span class="n">mod</span> <span class="o">=</span> <span class="mi">100</span>
<span class="n">center</span> <span class="o">=</span> <span class="p">(</span><span class="n">size</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">//</span> <span class="mi">2</span><span class="p">,)</span> <span class="o">*</span> <span class="mi">2</span>
<span class="n">start_vec</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">([</span><span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="p">(</span><span class="n">center</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">-</span> <span class="mi">10</span><span class="p">)])</span>
<span class="k">def</span> <span class="nf">get_coords_for_num</span><span class="p">(</span><span class="n">num</span><span class="p">):</span>
<span class="n">angle</span> <span class="o">=</span> <span class="p">((</span><span class="mf">2.0</span> <span class="o">*</span> <span class="n">np</span><span class="o">.</span><span class="n">pi</span><span class="p">)</span> <span class="o">/</span> <span class="p">(</span><span class="n">mod</span><span class="p">))</span> <span class="o">*</span> <span class="n">num</span>
<span class="n">sin</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">sin</span><span class="p">(</span><span class="n">angle</span><span class="p">)</span>
<span class="n">cos</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">cos</span><span class="p">(</span><span class="n">angle</span><span class="p">)</span>
<span class="n">m</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">([[</span><span class="n">cos</span><span class="p">,</span> <span class="o">-</span><span class="n">sin</span><span class="p">],</span> <span class="p">[</span><span class="n">sin</span><span class="p">,</span> <span class="n">cos</span><span class="p">]])</span>
<span class="n">vec</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">matmul</span><span class="p">(</span><span class="n">m</span><span class="p">,</span> <span class="n">start_vec</span><span class="p">)</span>
<span class="k">return</span> <span class="n">vec</span> <span class="o">+</span> <span class="n">center</span>
</code></pre></div>
<p>The <code>center</code> and <code>start_vec</code> values were calculated so that the size
of the image can easily be changed and everything will still work.</p>
<h2>Drawing the Circle</h2>
<p>Now that we know how to draw lines and how to get the
coordinates for points around our circle, let's bring
the two parts together.</p>
<p>For each non-negative integer <span class="math">\(num\)</span> less than <span class="math">\(mod\)</span>, we want to
multiply <span class="math">\(num\)</span> by <span class="math">\(mult\)</span>, and calculate the remainder
when we divide this product by <span class="math">\(mod\)</span>. This is more
commonly known as the <em>modulo</em> operation, and we can
find the remainder using: <code>rem = (num * mult) % mod</code>.</p>
<p>This is what the code looks like after adding these changes:</p>
<div class="highlight"><pre><span></span><code><span class="n">mult</span> <span class="o">=</span> <span class="mi">5</span>
<span class="k">with</span> <span class="n">cairo</span><span class="o">.</span><span class="n">ImageSurface</span><span class="p">(</span><span class="n">cairo</span><span class="o">.</span><span class="n">Format</span><span class="o">.</span><span class="n">ARGB32</span><span class="p">,</span> <span class="o">*</span><span class="n">size</span><span class="p">)</span> <span class="k">as</span> <span class="n">surface</span><span class="p">:</span>
<span class="n">context</span> <span class="o">=</span> <span class="n">cairo</span><span class="o">.</span><span class="n">Context</span><span class="p">(</span><span class="n">surface</span><span class="p">)</span>
<span class="n">context</span><span class="o">.</span><span class="n">set_line_width</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="k">for</span> <span class="n">num</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">mod</span><span class="p">):</span>
<span class="n">context</span><span class="o">.</span><span class="n">set_source_rgb</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">rem</span> <span class="o">=</span> <span class="p">(</span><span class="n">num</span> <span class="o">*</span> <span class="n">mult</span><span class="p">)</span> <span class="o">%</span> <span class="n">mod</span>
<span class="n">start_coords</span> <span class="o">=</span> <span class="n">get_coords_for_num</span><span class="p">(</span><span class="n">num</span><span class="p">)</span>
<span class="n">context</span><span class="o">.</span><span class="n">move_to</span><span class="p">(</span><span class="n">start_coords</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">start_coords</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
<span class="n">end_coords</span> <span class="o">=</span> <span class="n">get_coords_for_num</span><span class="p">(</span><span class="n">rem</span><span class="p">)</span>
<span class="n">context</span><span class="o">.</span><span class="n">line_to</span><span class="p">(</span><span class="n">end_coords</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">end_coords</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
<span class="n">context</span><span class="o">.</span><span class="n">stroke</span><span class="p">()</span>
<span class="n">circ_str</span> <span class="o">=</span> <span class="sa">f</span><span class="s1">'mod_</span><span class="si">{</span><span class="n">mod</span><span class="si">}</span><span class="s1">_mult_</span><span class="si">{</span><span class="n">mult</span><span class="si">}</span><span class="s1">'</span>
<span class="n">surface</span><span class="o">.</span><span class="n">write_to_png</span><span class="p">(</span><span class="sa">f</span><span class="s1">'complete_circle_</span><span class="si">{</span><span class="n">circ_str</span><span class="si">}</span><span class="s1">.png'</span><span class="p">)</span>
</code></pre></div>
<p>We use the <code>get_coords_for_num</code> function to get coordinates for points
and then the Cairo drawing functions to connect a start and end point
on the circle, for all points on the circle.</p>
<p>Then we set the name of the circle to be something descriptive;
in this case the generated image name will be
<code>'complete_circle_mod_100_mult_5.png'</code>. </p>
<p>Here's the full code:</p>
<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">cairo</span>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span>
<span class="n">mod</span> <span class="o">=</span> <span class="mi">100</span>
<span class="n">mult</span> <span class="o">=</span> <span class="mi">5</span>
<span class="n">size</span> <span class="o">=</span> <span class="p">(</span><span class="mi">500</span><span class="p">,)</span> <span class="o">*</span> <span class="mi">2</span>
<span class="n">center</span> <span class="o">=</span> <span class="p">(</span><span class="n">size</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">//</span> <span class="mi">2</span><span class="p">,)</span> <span class="o">*</span> <span class="mi">2</span>
<span class="n">start_vec</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">([</span><span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="p">(</span><span class="n">center</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">-</span> <span class="mi">10</span><span class="p">)])</span>
<span class="k">def</span> <span class="nf">get_coords_for_num</span><span class="p">(</span><span class="n">num</span><span class="p">):</span>
<span class="n">angle</span> <span class="o">=</span> <span class="p">((</span><span class="mf">2.0</span> <span class="o">*</span> <span class="n">np</span><span class="o">.</span><span class="n">pi</span><span class="p">)</span> <span class="o">/</span> <span class="p">(</span><span class="n">mod</span><span class="p">))</span> <span class="o">*</span> <span class="n">num</span>
<span class="n">sin</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">sin</span><span class="p">(</span><span class="n">angle</span><span class="p">)</span>
<span class="n">cos</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">cos</span><span class="p">(</span><span class="n">angle</span><span class="p">)</span>
<span class="n">m</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">([[</span><span class="n">cos</span><span class="p">,</span> <span class="o">-</span><span class="n">sin</span><span class="p">],</span> <span class="p">[</span><span class="n">sin</span><span class="p">,</span> <span class="n">cos</span><span class="p">]])</span>
<span class="n">vec</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">matmul</span><span class="p">(</span><span class="n">m</span><span class="p">,</span> <span class="n">start_vec</span><span class="p">)</span>
<span class="k">return</span> <span class="n">vec</span> <span class="o">+</span> <span class="n">center</span>
<span class="k">with</span> <span class="n">cairo</span><span class="o">.</span><span class="n">ImageSurface</span><span class="p">(</span><span class="n">cairo</span><span class="o">.</span><span class="n">Format</span><span class="o">.</span><span class="n">ARGB32</span><span class="p">,</span> <span class="o">*</span><span class="n">size</span><span class="p">)</span> <span class="k">as</span> <span class="n">surface</span><span class="p">:</span>
<span class="n">context</span> <span class="o">=</span> <span class="n">cairo</span><span class="o">.</span><span class="n">Context</span><span class="p">(</span><span class="n">surface</span><span class="p">)</span>
<span class="n">context</span><span class="o">.</span><span class="n">set_line_width</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="k">for</span> <span class="n">num</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">mod</span><span class="p">):</span>
<span class="n">context</span><span class="o">.</span><span class="n">set_source_rgb</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">rem</span> <span class="o">=</span> <span class="p">(</span><span class="n">num</span> <span class="o">*</span> <span class="n">mult</span><span class="p">)</span> <span class="o">%</span> <span class="n">mod</span>
<span class="n">start_coords</span> <span class="o">=</span> <span class="n">get_coords_for_num</span><span class="p">(</span><span class="n">num</span><span class="p">)</span>
<span class="n">context</span><span class="o">.</span><span class="n">move_to</span><span class="p">(</span><span class="n">start_coords</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">start_coords</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
<span class="n">end_coords</span> <span class="o">=</span> <span class="n">get_coords_for_num</span><span class="p">(</span><span class="n">rem</span><span class="p">)</span>
<span class="n">context</span><span class="o">.</span><span class="n">line_to</span><span class="p">(</span><span class="n">end_coords</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">end_coords</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
<span class="n">context</span><span class="o">.</span><span class="n">stroke</span><span class="p">()</span>
<span class="n">circ_str</span> <span class="o">=</span> <span class="sa">f</span><span class="s1">'mod_</span><span class="si">{</span><span class="n">mod</span><span class="si">}</span><span class="s1">_mult_</span><span class="si">{</span><span class="n">mult</span><span class="si">}</span><span class="s1">'</span>
<span class="n">surface</span><span class="o">.</span><span class="n">write_to_png</span><span class="p">(</span><span class="sa">f</span><span class="s1">'complete_circle_</span><span class="si">{</span><span class="n">circ_str</span><span class="si">}</span><span class="s1">.png'</span><span class="p">)</span>
</code></pre></div>
<p>which I used to generate:</p>
<p><img alt="complete_circle_mod_100_mult_5.png" src="https://yashaslokesh.github.io/images/complete_circle_mod_100_mult_5.png"></p>
<p><strong>NOTE:</strong> I used the <code>RGB24</code> format here instead of <code>ARGB32</code>, so that the colors show properly.</p>
<p>You can vary the <code>mod</code>, <code>mult</code>, and colors to get some cooler-looking circles.
Here are some other circles I've created:</p>
<p><code>mod = 287</code>, <code>mult = 7</code>, Fixed green to 1 and randomized red and blue values:</p>
<p><img alt="circle 287, mult 7" src="https://yashaslokesh.github.io/images/circle_mod_287_with_mult_7.png"></p>
<p><code>mod = 12</code>, <code>mult = 4</code>, Fixed blue to 1 and randomized red and green values:</p>
<p><img alt="circle 12, mult 4" src="https://yashaslokesh.github.io/images/circle_mod_12_with_mult_4.png"></p>
<p><code>mod = 200</code>, <code>mult = 45</code>, Fixed green to 1 and randomized red and blue values:</p>
<p><img alt="circle 200, mult 45" src="https://yashaslokesh.github.io/images/circle_mod_200_with_mult_45.png"></p>
<h2>Next Steps</h2>
<p>If you use any of the code here and make your own visual,
tag me on Twitter @yashaslokesh_ so I can see it! </p>
<p>There will be a follow-up article to this one where I go over
how to create a video showing the circle being generated. That
article will use <code>ffmpeg</code> and write intermediate
images to the <code>ffmpeg</code> process.
Check back in a few weeks, for that article.</p>
<h2>Conclusion</h2>
<p>Thanks for reading, and I hope you learned more about
pycairo, numpy, and modular arithmetic, if you were inexperienced
in any of these topics before. </p>
<p>If you see any errors or suggestions for this article, feel free
to file an issue on <a href="https://github.com/yashaslokesh">the GitHub repo</a>
or contact me <a href="https://twitter.com/yashaslokesh_">on Twitter</a>.
If you want to contact me about anything else, feel free
to contact me <a href="https://twitter.com/yashaslokesh_">on Twitter</a>.</p>
<script type="text/javascript">if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
var align = "center",
indent = "0em",
linebreak = "false";
if (false) {
align = (screen.width < 768) ? "left" : align;
indent = (screen.width < 768) ? "0em" : indent;
linebreak = (screen.width < 768) ? 'true' : linebreak;
}
var mathjaxscript = document.createElement('script');
mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
mathjaxscript.type = 'text/javascript';
mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';
var configscript = document.createElement('script');
configscript.type = 'text/x-mathjax-config';
configscript[(window.opera ? "innerHTML" : "text")] =
"MathJax.Hub.Config({" +
" config: ['MMLorHTML.js']," +
" TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'none' } }," +
" jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
" extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
" displayAlign: '"+ align +"'," +
" displayIndent: '"+ indent +"'," +
" showMathMenu: true," +
" messageStyle: 'normal'," +
" tex2jax: { " +
" inlineMath: [ ['\\\\(','\\\\)'] ], " +
" displayMath: [ ['$$','$$'] ]," +
" processEscapes: true," +
" preview: 'TeX'," +
" }, " +
" 'HTML-CSS': { " +
" availableFonts: ['STIX', 'TeX']," +
" preferredFont: 'STIX'," +
" styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
" linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
" }, " +
"}); " +
"if ('default' !== 'default') {" +
"MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"}";
(document.body || document.getElementsByTagName('head')[0]).appendChild(configscript);
(document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
</script>
</div><!-- /.entry-content -->
</article>
</section>
<section id="extras" class="body">
<div class="blogroll">
<h2>links</h2>
<ul>
<li><a href="http://getpelican.com/">Pelican</a></li>
<li><a href="http://python.org/">Python.org</a></li>
<li><a href="http://jinja.pocoo.org/">Jinja2</a></li>
<li><a href="http://tutorial.math.lamar.edu/">Paul's Calc Notes</a></li>
<li><a href="https://www.pygame.org/news">Pygame</a></li>
</ul>
</div><!-- /.blogroll -->
<div class="social">
<h2>social</h2>
<ul>
<li><a href="https://yashaslokesh.github.io/feeds/all.atom.xml" type="application/atom+xml" rel="alternate">atom feed</a></li>
<li><a href="https://twitter.com/yashaslokesh_">My Twitter</a></li>
<li><a href="https://www.linkedin.com/in/yashaslokesh/">My LinkedIn</a></li>
<li><a href="https://github.com/yashaslokesh">My GitHub</a></li>
</ul>
</div><!-- /.social -->
</section><!-- /#extras -->
<footer id="contentinfo" class="body">
<address id="about" class="vcard body">
Proudly powered by <a href="https://getpelican.com/">Pelican</a>, which takes great advantage of <a href="https://www.python.org/">Python</a>.
</address><!-- /#about -->
<p>The theme is by <a href="https://www.smashingmagazine.com/2009/08/designing-a-html-5-layout-from-scratch/">Smashing Magazine</a>, thanks!</p>
</footer><!-- /#contentinfo -->
<script type="text/javascript">
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-123139556-3', 'auto');
ga('send', 'pageview');
</script>
</body>
</html>