# Squhorls

a guest
Nov 19th, 2023
49
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
1. from math import cos, sin, dist, hypot
2. from random import random
3.
4. # given a point, calculates how far it is from a square with corners (+/-1, +/-1).
5. def distance_from_square(x, y):
6.     if y >= x and y >= -x:
7.         return abs(1 - y)
8.     elif y >= x and y < -x:
9.         return abs(1 + x)
10.     elif y < x and y < -x:
11.         return abs(1 + y)
12.     elif y < x and y >= -x:
13.         return abs(1 - x)
14.
15. # divide square into 16 smaller squares, place center of a whorl randomly in each one.
16. charge_coords = [((i%4) / 4 + random() / 4, (i//4) / 4 + random() / 4) for i in range(16)]
17.
18. # given a point and a list of whorl centers, calculate direction of vector at that point given by sum of whorl vectors
19. def force_direction(x, y, charges):
20.     force_x = 0
21.     force_y = 0
22.     for c in charges:
23.         distance_cubed = dist((x, y), c)**3
24.         force_x = force_x + (c[0] - x) / distance_cubed
25.         force_y = force_y + (c[1] - y) / distance_cubed
26.     norm = hypot(force_x, force_y)
27.     return (-force_y / norm, force_x / norm)
28.
29. # given a minimum value a, a maximum value b, and a phase, outputs the data needed to animate an SVG using a triangle wave with that phase
30. def phase_offset(a, b, phase):
31.     if phase % 1 <= 0.5:
32.         value_list = [(1 - 2 * phase) * a + 2 * phase * b, a, b, (1 - 2 * phase) * a + 2 * phase * b]
33.         time_list = [0, phase, 0.5 + phase, 1]
34.     elif phase % 1 > 0.5:
35.         phase_adj = phase - 0.5
36.         value_list = [(1 - 2 * phase_adj) * b + 2 * phase_adj * a, b, a, (1 - 2 * phase_adj) * b + 2 * phase_adj * a]
37.         time_list = [0, phase_adj, phase, 1]
38.     return (value_list, time_list)
39.
40. # generate SVG
41. with open('squhorls.svg', 'w') as file:
42.     file.write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n")
43.     file.write("<svg width=\"600\" height=\"600\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n")
44.     for i in range(60):
45.         for j in range(60):
46.             dir_vec = force_direction(i / 60, j / 60, charge_coords)
47.             start_thickness = 4 / (1 + 11 * distance_from_square((i - 29.5) / 15, (j - 29.5) / 15))
48.             end_thickness = 1/3 * (1 + 11 * distance_from_square((i - 29.5) / 15, (j - 29.5) / 15))
49.             phase = (i + j) / 180
50.             envelope = phase_offset(start_thickness, end_thickness, phase)
51.             file.write("<line x1=\"{0}\" x2=\"{1}\" y1=\"{2}\" y2=\"{3}\" stroke=\"black\" stroke-width=\"{4}\">\n".format(10 * (i + 1), 10 * (i + 1) + 7 * dir_vec[0], 10 * (j + 1), 10 * (j+1) + 7 * dir_vec[1], start_thickness))
52.             file.write("<animate attributeName=\"stroke-width\" values=\"{0};{1};{2};{3}\" keyTimes=\"{4};{5};{6};{7}\" dur=\"6s\" repeatCount=\"indefinite\" /> \n </line>\n".format(envelope[0][0], envelope[0][1], envelope[0][2], envelope[0][3], envelope[1][0], envelope[1][1], envelope[1][2], envelope[1][3]))
53.     file.write("</svg>")