Account for remaining site changes
This commit is contained in:
parent
0717383be9
commit
8ce7b655bc
8 changed files with 123 additions and 36 deletions
|
|
@ -11,9 +11,9 @@ pub fn ocean_palette(root_index: u8, iterations: u32, max_iterations: u32) -> (u
|
|||
|
||||
// Ocean theme colors for each root basin
|
||||
let (r, g, b) = match root_index {
|
||||
0 => (0.05, 0.25, 0.55), // Deep ocean blue
|
||||
1 => (0.0, 0.45, 0.50), // Teal
|
||||
2 => (0.15, 0.55, 0.65), // Cyan/turquoise
|
||||
0 => (0.05, 0.25, 0.55), // Deep ocean blue
|
||||
1 => (0.0, 0.45, 0.50), // Teal
|
||||
2 => (0.15, 0.55, 0.65), // Cyan/turquoise
|
||||
_ => (0.05, 0.25, 0.55),
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -16,9 +16,9 @@ pub fn compute(z_re: f64, z_im: f64, time: f64) -> (u8, u32) {
|
|||
|
||||
// The three roots of z^3 - 1
|
||||
const ROOTS: [(f64, f64); 3] = [
|
||||
(1.0, 0.0), // 1
|
||||
(-0.5, 0.866025403784439), // e^(2πi/3)
|
||||
(-0.5, -0.866025403784439), // e^(4πi/3)
|
||||
(1.0, 0.0), // 1
|
||||
(-0.5, 0.866025403784439), // e^(2πi/3)
|
||||
(-0.5, -0.866025403784439), // e^(4πi/3)
|
||||
];
|
||||
|
||||
for iter in 0..MAX_ITERATIONS {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,9 @@ use crate::newton::compute;
|
|||
const MAX_ITERATIONS: u32 = 32;
|
||||
|
||||
/// Get the canvas element and 2D context
|
||||
fn get_canvas_and_context(canvas_id: &str) -> Result<(HtmlCanvasElement, CanvasRenderingContext2d), JsValue> {
|
||||
fn get_canvas_and_context(
|
||||
canvas_id: &str,
|
||||
) -> Result<(HtmlCanvasElement, CanvasRenderingContext2d), JsValue> {
|
||||
let window = web_sys::window().ok_or("no window")?;
|
||||
let document = window.document().ok_or("no document")?;
|
||||
let canvas = document
|
||||
|
|
@ -66,7 +68,8 @@ pub fn render_fractal_animated(canvas_id: &str, time: f64, scale: u32) -> Result
|
|||
|
||||
// Create ImageData at reduced size
|
||||
let clamped = wasm_bindgen::Clamped(&pixels[..]);
|
||||
let image_data = ImageData::new_with_u8_clamped_array_and_sh(clamped, width as u32, height as u32)?;
|
||||
let image_data =
|
||||
ImageData::new_with_u8_clamped_array_and_sh(clamped, width as u32, height as u32)?;
|
||||
|
||||
// Scale up when drawing to canvas
|
||||
if scale > 1 {
|
||||
|
|
@ -89,8 +92,10 @@ pub fn render_fractal_animated(canvas_id: &str, time: f64, scale: u32) -> Result
|
|||
)?;
|
||||
context.draw_image_with_html_canvas_element_and_dw_and_dh(
|
||||
&temp_canvas,
|
||||
0.0, 0.0,
|
||||
canvas_width as f64, canvas_height as f64
|
||||
0.0,
|
||||
0.0,
|
||||
canvas_width as f64,
|
||||
canvas_height as f64,
|
||||
)?;
|
||||
} else {
|
||||
context.put_image_data(&image_data, 0.0, 0.0)?;
|
||||
|
|
|
|||
|
|
@ -2,17 +2,18 @@
|
|||
|
||||
WEB_UI_PORT=3001
|
||||
SSH_PORT=2223
|
||||
DATA_DIR=/home/deploy/forgejo/data
|
||||
|
||||
docker run -d \
|
||||
--name forgejo \
|
||||
-p ${WEB_UI_PORT}:3000 \
|
||||
-p 127.0.0.1:${WEB_UI_PORT}:3000 \
|
||||
-p ${SSH_PORT}:22 \
|
||||
-v ~/forgejo/data:/data \
|
||||
-v ${DATA_DIR}:/data \
|
||||
-v /etc/timezone:/etc/timezone:ro \
|
||||
-v /etc/localtime:/etc/localtime:ro \
|
||||
--restart always \
|
||||
--restart always \
|
||||
codeberg.org/forgejo/forgejo:9
|
||||
|
||||
echo "Forgejo started on:"
|
||||
echo " Web UI: http://zachery.lol:${WEB_UI_PORT}"
|
||||
echo " Web UI: http://127.0.0.1:${WEB_UI_PORT} (proxied at https://zachery.lol/code/)"
|
||||
echo " ssh port: ${SSH_PORT}"
|
||||
|
|
|
|||
|
|
@ -33,8 +33,8 @@ server {
|
|||
|
||||
listen 443 ssl; # managed by Certbot
|
||||
listen [::]:443 ssl ipv6only=on; # managed by Certbot
|
||||
ssl_certificate /etc/letsencrypt/live/zachery.lol/fullchain.pem; # managed by Certbot
|
||||
ssl_certificate_key /etc/letsencrypt/live/zachery.lol/privkey.pem; # managed by Certbot
|
||||
ssl_certificate /etc/letsencrypt/live/zachery.lol-0001/fullchain.pem; # managed by Certbot
|
||||
ssl_certificate_key /etc/letsencrypt/live/zachery.lol-0001/privkey.pem; # managed by Certbot
|
||||
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
|
||||
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,6 +117,8 @@
|
|||
}
|
||||
}
|
||||
</style>
|
||||
<link rel="preload" href="/routes/root/dist-strips.bin?v=1" as="fetch" crossorigin />
|
||||
<script defer src="/routes/root/fractal-gl.js?v=12"></script>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="fractal-canvas"></canvas>
|
||||
|
|
@ -135,15 +137,7 @@
|
|||
</nav>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
requestAnimationFrame(function () {
|
||||
requestAnimationFrame(function () {
|
||||
var s = document.createElement('script');
|
||||
s.src = '/routes/root/fractal-gl.js?v=9';
|
||||
s.async = true;
|
||||
document.body.appendChild(s);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<!-- fractal background loaded via deferred <script> in <head> -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -279,6 +279,8 @@
|
|||
}
|
||||
}
|
||||
</script>
|
||||
<link rel="preload" href="/routes/root/dist-strips.bin?v=1" as="fetch" crossorigin />
|
||||
<script defer src="/routes/root/fractal-gl.js?v=12"></script>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="fractal-canvas"></canvas>
|
||||
|
|
@ -323,15 +325,7 @@
|
|||
<a href="/contact">back to contact</a>
|
||||
</nav>
|
||||
|
||||
<script>
|
||||
requestAnimationFrame(function () {
|
||||
requestAnimationFrame(function () {
|
||||
var s = document.createElement('script');
|
||||
s.src = '/routes/root/fractal-gl.js?v=9';
|
||||
s.async = true;
|
||||
document.body.appendChild(s);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<!-- fractal background loaded via deferred <script> in <head> -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
93
tools/gen-strips.py
Normal file
93
tools/gen-strips.py
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
# /// script
|
||||
# requires-python = ">=3.9"
|
||||
# dependencies = ["pillow"]
|
||||
# ///
|
||||
"""
|
||||
Generate routes/root/dist-strips.bin from routes/root/dist.png.
|
||||
|
||||
Splits the 512x512 grayscale Julia distance field into N horizontal bands,
|
||||
re-encodes each band as its own WebP, and concatenates them in CENTER-OUT order
|
||||
(the band the pan/zoom animation shows first) so a streaming client can upload
|
||||
the visible region before the outer bands have finished downloading. WebP q90 is
|
||||
~half the size of PNG with no visible loss after the color-band mapping.
|
||||
|
||||
File layout (little-endian):
|
||||
u32 headerLen
|
||||
bytes headerLen -- UTF-8 JSON: {fullW, fullH, bands:[{y,h,off,len}, ...]}
|
||||
bytes payload -- concatenated band WebPs in the order listed in `bands`
|
||||
(off/len are byte ranges within payload, stream order)
|
||||
|
||||
Re-run only when dist.png changes; the output is committed.
|
||||
Usage: uv run tools/gen-strips.py
|
||||
"""
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import struct
|
||||
from PIL import Image
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
ROOT = os.path.dirname(HERE)
|
||||
SRC = os.path.join(ROOT, "routes", "root", "dist.png")
|
||||
OUT = os.path.join(ROOT, "routes", "root", "dist-strips.bin")
|
||||
NBANDS = 8
|
||||
WEBP_QUALITY = 90 # lossy; ~half of PNG, visually identical. Bump to 95 / use lossless if needed.
|
||||
|
||||
|
||||
def center_out(n):
|
||||
"""Band indices ordered from the middle outward: e.g. n=8 -> [4,3,5,2,6,1,7,0]."""
|
||||
order = []
|
||||
lo, hi = n // 2 - 1, n // 2
|
||||
while lo >= 0 or hi < n:
|
||||
if hi < n:
|
||||
order.append(hi)
|
||||
hi += 1
|
||||
if lo >= 0:
|
||||
order.append(lo)
|
||||
lo -= 1
|
||||
return order
|
||||
|
||||
|
||||
def main():
|
||||
img = Image.open(SRC).convert("L")
|
||||
w, h = img.size
|
||||
|
||||
# Row boundaries (robust if h is not divisible by NBANDS).
|
||||
bounds = [round(i * h / NBANDS) for i in range(NBANDS + 1)]
|
||||
bands_by_index = []
|
||||
for i in range(NBANDS):
|
||||
y0, y1 = bounds[i], bounds[i + 1]
|
||||
crop = img.crop((0, y0, w, y1))
|
||||
buf = io.BytesIO()
|
||||
crop.save(buf, format="WEBP", quality=WEBP_QUALITY, method=6)
|
||||
bands_by_index.append((y0, y1 - y0, buf.getvalue()))
|
||||
|
||||
payload = bytearray()
|
||||
meta = []
|
||||
order = center_out(NBANDS)
|
||||
for idx in order:
|
||||
y, bh, png = bands_by_index[idx]
|
||||
meta.append({"y": y, "h": bh, "off": len(payload), "len": len(png)})
|
||||
payload += png
|
||||
|
||||
header = json.dumps(
|
||||
{"fullW": w, "fullH": h, "bands": meta}, separators=(",", ":")
|
||||
).encode("utf-8")
|
||||
|
||||
with open(OUT, "wb") as f:
|
||||
f.write(struct.pack("<I", len(header)))
|
||||
f.write(header)
|
||||
f.write(payload)
|
||||
|
||||
total = 4 + len(header) + len(payload)
|
||||
src_size = os.path.getsize(SRC)
|
||||
print(f"{w}x{h} -> {NBANDS} bands, center-out order {order}")
|
||||
print(
|
||||
f"header {len(header)}B, payload {len(payload)}B, total {total}B "
|
||||
f"({total / 1024:.1f} KB) vs dist.png {src_size}B ({src_size / 1024:.1f} KB)"
|
||||
)
|
||||
print(f"wrote {OUT}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Reference in a new issue