|
28 | 28 | } |
29 | 29 |
|
30 | 30 |
|
31 | | -def trace_trefoil(t: float) -> Vector: |
32 | | - return ( |
33 | | - math.sin(t) + 2.0 * math.sin(2.0 * t), |
34 | | - math.cos(t) - 2.0 * math.cos(2.0 * t), |
35 | | - -math.sin(3.0 * t) * 1.2, |
36 | | - ) |
37 | | - |
38 | | - |
39 | | -def subtract(a: Vector, b: Vector) -> Vector: |
40 | | - return (a[0] - b[0], a[1] - b[1], a[2] - b[2]) |
41 | | - |
42 | | - |
43 | | -def cross(a: Vector, b: Vector) -> Vector: |
44 | | - return ( |
45 | | - a[1] * b[2] - a[2] * b[1], |
46 | | - a[2] * b[0] - a[0] * b[2], |
47 | | - a[0] * b[1] - a[1] * b[0], |
48 | | - ) |
| 31 | +def main() -> None: |
| 32 | + ASSETS_DIR.mkdir(parents=True, exist_ok=True) |
| 33 | + write_mesh(ASSETS_DIR / "pretzel.mdl") |
| 34 | + for name, (top, bottom, glow) in WALLPAPERS.items(): |
| 35 | + write_wallpaper(ASSETS_DIR / name, top, bottom, glow) |
49 | 36 |
|
50 | 37 |
|
51 | | -def dot(a: Vector, b: Vector) -> float: |
52 | | - return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] |
| 38 | +def write_mesh(path: pathlib.Path) -> None: |
| 39 | + vertices, faces = sweep_tube() |
| 40 | + with path.open("w", encoding="utf-8") as file: |
| 41 | + file.write(f"# Trefoil-knot pretzel: {len(vertices)} vertices,") |
| 42 | + file.write(f" {len(faces)} faces\n") |
| 43 | + for x, y, z in vertices: |
| 44 | + file.write(f"v {x:.6f} {y:.6f} {z:.6f}\n") |
| 45 | + for a, b, c in faces: |
| 46 | + file.write(f"f {a + 1} {b + 1} {c + 1}\n") |
| 47 | + print(f"Wrote {path} ({len(vertices)} vertices, {len(faces)} faces)") |
53 | 48 |
|
54 | 49 |
|
55 | | -def normalize(vector: Vector) -> Vector: |
56 | | - length = math.sqrt(dot(vector, vector)) or 1.0 |
57 | | - return (vector[0] / length, vector[1] / length, vector[2] / length) |
| 50 | +def write_wallpaper( |
| 51 | + path: pathlib.Path, top: Color, bottom: Color, glow: Color |
| 52 | +) -> None: |
| 53 | + size = WALLPAPER_SIZE |
| 54 | + lights = [ |
| 55 | + (size * 0.25, size * 0.3, size * 0.22), |
| 56 | + (size * 0.7, size * 0.55, size * 0.3), |
| 57 | + (size * 0.45, size * 0.8, size * 0.18), |
| 58 | + ] |
| 59 | + rows = bytearray() |
| 60 | + for y in range(size): |
| 61 | + vertical = y / (size - 1) |
| 62 | + base = blend(top, bottom, vertical) |
| 63 | + for x in range(size): |
| 64 | + halo = 0.0 |
| 65 | + for cx, cy, radius in lights: |
| 66 | + distance = math.hypot(x - cx, y - cy) |
| 67 | + halo += max(0.0, 1.0 - distance / radius) ** 2 |
| 68 | + color = blend(base, glow, min(1.0, halo)) |
| 69 | + rows.extend(color) |
| 70 | + with path.open("wb") as file: |
| 71 | + file.write(b"P6\n%d %d\n255\n" % (size, size)) |
| 72 | + file.write(rows) |
| 73 | + print(f"Wrote {path} ({size}x{size})") |
58 | 74 |
|
59 | 75 |
|
60 | 76 | def sweep_tube() -> tuple[list[Vector], list[Face]]: |
@@ -101,56 +117,40 @@ def sweep_tube() -> tuple[list[Vector], list[Face]]: |
101 | 117 | return vertices, faces |
102 | 118 |
|
103 | 119 |
|
104 | | -def write_mesh(path: pathlib.Path) -> None: |
105 | | - vertices, faces = sweep_tube() |
106 | | - with path.open("w", encoding="utf-8") as file: |
107 | | - file.write(f"# Trefoil-knot pretzel: {len(vertices)} vertices,") |
108 | | - file.write(f" {len(faces)} faces\n") |
109 | | - for x, y, z in vertices: |
110 | | - file.write(f"v {x:.6f} {y:.6f} {z:.6f}\n") |
111 | | - for a, b, c in faces: |
112 | | - file.write(f"f {a + 1} {b + 1} {c + 1}\n") |
113 | | - print(f"Wrote {path} ({len(vertices)} vertices, {len(faces)} faces)") |
114 | | - |
115 | | - |
116 | 120 | def blend(low: Color, high: Color, amount: float) -> Color: |
117 | 121 | return tuple( |
118 | 122 | round(low[channel] + (high[channel] - low[channel]) * amount) |
119 | 123 | for channel in range(3) |
120 | 124 | ) |
121 | 125 |
|
122 | 126 |
|
123 | | -def write_wallpaper( |
124 | | - path: pathlib.Path, top: Color, bottom: Color, glow: Color |
125 | | -) -> None: |
126 | | - size = WALLPAPER_SIZE |
127 | | - lights = [ |
128 | | - (size * 0.25, size * 0.3, size * 0.22), |
129 | | - (size * 0.7, size * 0.55, size * 0.3), |
130 | | - (size * 0.45, size * 0.8, size * 0.18), |
131 | | - ] |
132 | | - rows = bytearray() |
133 | | - for y in range(size): |
134 | | - vertical = y / (size - 1) |
135 | | - base = blend(top, bottom, vertical) |
136 | | - for x in range(size): |
137 | | - halo = 0.0 |
138 | | - for cx, cy, radius in lights: |
139 | | - distance = math.hypot(x - cx, y - cy) |
140 | | - halo += max(0.0, 1.0 - distance / radius) ** 2 |
141 | | - color = blend(base, glow, min(1.0, halo)) |
142 | | - rows.extend(color) |
143 | | - with path.open("wb") as file: |
144 | | - file.write(b"P6\n%d %d\n255\n" % (size, size)) |
145 | | - file.write(rows) |
146 | | - print(f"Wrote {path} ({size}x{size})") |
| 127 | +def trace_trefoil(t: float) -> Vector: |
| 128 | + return ( |
| 129 | + math.sin(t) + 2.0 * math.sin(2.0 * t), |
| 130 | + math.cos(t) - 2.0 * math.cos(2.0 * t), |
| 131 | + -math.sin(3.0 * t) * 1.2, |
| 132 | + ) |
147 | 133 |
|
148 | 134 |
|
149 | | -def main() -> None: |
150 | | - ASSETS_DIR.mkdir(parents=True, exist_ok=True) |
151 | | - write_mesh(ASSETS_DIR / "pretzel.mdl") |
152 | | - for name, (top, bottom, glow) in WALLPAPERS.items(): |
153 | | - write_wallpaper(ASSETS_DIR / name, top, bottom, glow) |
| 135 | +def subtract(a: Vector, b: Vector) -> Vector: |
| 136 | + return (a[0] - b[0], a[1] - b[1], a[2] - b[2]) |
| 137 | + |
| 138 | + |
| 139 | +def cross(a: Vector, b: Vector) -> Vector: |
| 140 | + return ( |
| 141 | + a[1] * b[2] - a[2] * b[1], |
| 142 | + a[2] * b[0] - a[0] * b[2], |
| 143 | + a[0] * b[1] - a[1] * b[0], |
| 144 | + ) |
| 145 | + |
| 146 | + |
| 147 | +def dot(a: Vector, b: Vector) -> float: |
| 148 | + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] |
| 149 | + |
| 150 | + |
| 151 | +def normalize(vector: Vector) -> Vector: |
| 152 | + length = math.sqrt(dot(vector, vector)) or 1.0 |
| 153 | + return (vector[0] / length, vector[1] / length, vector[2] / length) |
154 | 154 |
|
155 | 155 |
|
156 | 156 | if __name__ == "__main__": |
|
0 commit comments