diff --git a/README.md b/README.md index 6ca2b7b..eefb45d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@
-SchOS +SchOS — Shift, lit, on graphite + +# SchOS **A 64-bit operating system, written from the metal up.** diff --git a/docs/Website/Assets/wallpaper1.svg b/docs/Website/Assets/wallpaper1.svg deleted file mode 100644 index b69ab0e..0000000 --- a/docs/Website/Assets/wallpaper1.svg +++ /dev/null @@ -1,2725 +0,0 @@ - - - -SchOSPowered by SchIFT Kernel diff --git a/docs/branding/README.md b/docs/branding/README.md index 3132eba..89b5de2 100644 --- a/docs/branding/README.md +++ b/docs/branding/README.md @@ -99,6 +99,12 @@ No JPEG anywhere — a mark is flat colour with hard edges, which is the exact case JPEG is worst at, and the ringing it leaves around an edge is permanent. No screenshots of marks. No re-exports of re-exports: go back to the SVG. +**Wallpapers are the one exception**, and the reason is the same rule read the +other way round. They are soft gradients under a film of grain: no hard edge +for JPEG to ring around, and nothing a PNG can compress -- the grain made each +one nine megabytes as PNG and about a hundred kilobytes as JPEG, with no +difference anyone can see. The SVG is still the source; the JPEG is a render. + **SVG is the source of truth for every mark.** A raster is something generated from it, never edited. @@ -128,9 +134,33 @@ shift.svg the logo, in colour shift-mask.svg the same geometry in greys -- see below shift-mask.png rasterised from it, committed on purpose uberblock.svg SchFS -superseded-*.png the old wordmark, kept for reference only +wallpapers/ 3840x2160, SVG and a JPEG render of each ``` +### Wallpapers + +Generated by `tools/wallpapers.py` from the Shift geometry in `shift.svg`, so a +change to the mark is one command away from every wallpaper rather than four +hand edits. Rendered to JPEG with a browser, because a browser is the renderer +that gets the SVG lighting filters right: + +```sh +python3 tools/wallpapers.py +chromium --headless=new --window-size=3840,2160 --screenshot=graphite.jpg page.html +``` + +where `page.html` is an `` of the SVG at 3840x2160. + +| | | +|---|---| +| `graphite` | Shift on warm graphite, throwing a violet pool of light on the surface. The default. | +| `strata` | The slabs repeated into a cascade receding into indigo, near ones sharp, far ones soft. | +| `porcelain` | The light one: Shift as glazed porcelain on paper, the top slab in the accent. | +| `dial` | The uberblock ring as hardware -- a turned metal face, slots as keys, the newest one and the root lit. | + +The wallpaper they replace used Guru, which is retired (see the top of this +file), and was drawn flat. + The pipeline is three stages: ``` diff --git a/docs/branding/superseded-full-logo.png b/docs/branding/superseded-full-logo.png deleted file mode 100644 index 38f6f74..0000000 Binary files a/docs/branding/superseded-full-logo.png and /dev/null differ diff --git a/docs/branding/superseded-text-logo.png b/docs/branding/superseded-text-logo.png deleted file mode 100644 index de90d66..0000000 Binary files a/docs/branding/superseded-text-logo.png and /dev/null differ diff --git a/docs/branding/wallpaper1.png b/docs/branding/wallpaper1.png deleted file mode 100644 index 94f6467..0000000 Binary files a/docs/branding/wallpaper1.png and /dev/null differ diff --git a/docs/branding/wallpaper1.svg b/docs/branding/wallpaper1.svg deleted file mode 100644 index b69ab0e..0000000 --- a/docs/branding/wallpaper1.svg +++ /dev/null @@ -1,2725 +0,0 @@ - - - -SchOSPowered by SchIFT Kernel diff --git a/docs/branding/wallpapers/dial.jpg b/docs/branding/wallpapers/dial.jpg new file mode 100644 index 0000000..c4b04cb Binary files /dev/null and b/docs/branding/wallpapers/dial.jpg differ diff --git a/docs/branding/wallpapers/dial.svg b/docs/branding/wallpapers/dial.svg new file mode 100644 index 0000000..b0aeee8 --- /dev/null +++ b/docs/branding/wallpapers/dial.svg @@ -0,0 +1,65 @@ + + SchOS — Dial + The SchFS uberblock ring as a piece of hardware: a turned metal face, the slots as keys set around it, the newest one and the root lit. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/branding/wallpapers/graphite.jpg b/docs/branding/wallpapers/graphite.jpg new file mode 100644 index 0000000..5b867f3 Binary files /dev/null and b/docs/branding/wallpapers/graphite.jpg differ diff --git a/docs/branding/wallpapers/graphite.svg b/docs/branding/wallpapers/graphite.svg new file mode 100644 index 0000000..563b478 --- /dev/null +++ b/docs/branding/wallpapers/graphite.svg @@ -0,0 +1,121 @@ + + SchOS — Graphite + Shift on warm graphite, lit from the upper left, casting a violet pool of light on the surface it stands on. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/branding/wallpapers/porcelain.jpg b/docs/branding/wallpapers/porcelain.jpg new file mode 100644 index 0000000..cc8cec5 Binary files /dev/null and b/docs/branding/wallpapers/porcelain.jpg differ diff --git a/docs/branding/wallpapers/porcelain.svg b/docs/branding/wallpapers/porcelain.svg new file mode 100644 index 0000000..d6eda01 --- /dev/null +++ b/docs/branding/wallpapers/porcelain.svg @@ -0,0 +1,46 @@ + + SchOS — Porcelain + The light one: Shift as glazed porcelain on soft paper, with the accent spent on the top slab alone. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/branding/wallpapers/strata.jpg b/docs/branding/wallpapers/strata.jpg new file mode 100644 index 0000000..4961171 Binary files /dev/null and b/docs/branding/wallpapers/strata.jpg differ diff --git a/docs/branding/wallpapers/strata.svg b/docs/branding/wallpapers/strata.svg new file mode 100644 index 0000000..84d05de --- /dev/null +++ b/docs/branding/wallpapers/strata.svg @@ -0,0 +1,94 @@ + + SchOS — Strata + Shift's slabs, repeated into a cascade that recedes into an indigo dark: near ones sharp and bright, far ones soft. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/wallpapers.py b/tools/wallpapers.py new file mode 100644 index 0000000..60d64cf --- /dev/null +++ b/tools/wallpapers.py @@ -0,0 +1,320 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later +# +# The SchOS wallpapers, written out as SVG. +# +# tools/wallpapers.py [OUTDIR] (default: docs/branding/wallpapers) +# +# Generated rather than drawn, for the same reason the include rewrite is a +# script: every one of them is the Shift geometry from docs/branding/shift.svg +# under a different light, and a hand-edited copy of a path is a copy that +# drifts. Change the mark there and run this again. +# +# All of them are 3840x2160 and lit the way Niveus lights a key -- one light +# from the upper left, gloss on what faces it, a soft shadow on what is under +# it -- because they sit behind a desktop built from those keys. +# +# The PNGs beside them are renders of these files (see the README there); this +# script does not write them, since the only renderer that gets the filters +# right is a browser. + +import math, os, re, sys + +ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +OUT = sys.argv[1] if len(sys.argv) > 1 else os.path.join(ROOT, "docs/branding/wallpapers") +W, H = 3840, 2160 + +# --- the mark ----------------------------------------------------------------- + +def shift_parts(): + """Return (defs, body) of shift.svg, ids prefixed so it can be embedded.""" + src = open(os.path.join(ROOT, "docs/branding/shift.svg")).read() + defs = re.search(r"(.*?)", src, re.S).group(1) + body = src[src.index("") + len(""):src.rindex("")] + body = re.sub(r"", "", body, flags=re.S) + fix = lambda s: re.sub(r'id="([^"]+)"', r'id="sh-\1"', s).replace("url(#", "url(#sh-") + return fix(defs), fix(body) + +# The three slabs as plain outlines, for the wallpapers that light the mark +# themselves rather than using its own shading. +SLABS = [ # (top face, front face, end wall), bottom slab first + ("M54 126 L158 126 L128 156 L24 156 Z", "M24 156 L128 156 L128 167 L24 167 Z", + "M158 126 L158 137 L128 167 L128 156 Z"), + ("M72 84 L176 84 L146 114 L42 114 Z", "M42 114 L146 114 L146 125 L42 125 Z", + "M176 84 L176 95 L146 125 L146 114 Z"), + ("M90 42 L194 42 L164 72 L60 72 Z", "M60 72 L164 72 L164 83 L60 83 Z", + "M194 42 L194 53 L164 83 L164 72 Z"), +] + +def svg(title, desc, defs, body): + return f''' + {title} + {desc} + +{defs} + +{body} + +''' + +GRAIN = ''' + + + + ''' + +def blur(id_, sd): + return f' ' + +# --- 1. Graphite --------------------------------------------------------------- + +def graphite(): + d, b = shift_parts() + s = 6.2 + tx, ty = W / 2 - 109 * s, H / 2 - 112 * s + defs = d + f''' + + + + + + + + + + + + + + +''' + GRAIN.format(a="0.07") + "\n" + blur("far", 60) + body = f''' + + + {b} + + ''' + return svg("SchOS — Graphite", + "Shift on warm graphite, lit from the upper left, casting a violet pool of light on the surface it stands on.", + defs, body) + +# --- 2. Strata ----------------------------------------------------------------- + +def slab(x, y, w, d, t, shade, alpha, gid): + """One slab in Shift's oblique: top face w wide and d deep, t thick.""" + k = 30 / 104 * w * (d / (30 / 104 * w)) if w else 0 + # the back edge is displaced right by the depth, as in the mark + top = f"M{x+d:.1f} {y:.1f} L{x+d+w:.1f} {y:.1f} L{x+w:.1f} {y+d:.1f} L{x:.1f} {y+d:.1f} Z" + front = f"M{x:.1f} {y+d:.1f} L{x+w:.1f} {y+d:.1f} L{x+w:.1f} {y+d+t:.1f} L{x:.1f} {y+d+t:.1f} Z" + wall = f"M{x+d+w:.1f} {y:.1f} L{x+d+w:.1f} {y+t:.1f} L{x+w:.1f} {y+d+t:.1f} L{x+w:.1f} {y+d:.1f} Z" + edge = f"M{x+0.6:.1f} {y+d-0.6:.1f} L{x+d+0.4:.1f} {y+0.6:.1f} L{x+d+w-1:.1f} {y+0.6:.1f}" + return (f' ' + f'' + f'' + f'' + f'' + f'' + '\n') + +def mix(a, b, f): + pa = [int(a[i:i+2], 16) for i in (1, 3, 5)] + pb = [int(b[i:i+2], 16) for i in (1, 3, 5)] + return "#%02X%02X%02X" % tuple(round(x + (y - x) * f) for x, y in zip(pa, pb)) + +def strata(): + defs = [''' + + + + + + + + + + + + + ''', GRAIN.format(a="0.05"), blur("far", 7), blur("farther", 18), blur("shadow", 28)] + # Five depths of colour, from near (bright violet) to far (indigo, dim). + near, far = "#7C5CFF", "#2A2266" + for i in range(5): + c = mix(near, far, i / 4) + defs.append(f''' + + ''') + + # A cascade: slabs stepping down and to the left, each displaced from the + # one above exactly as Shift's are, at three distances. The near ones are + # solid and cast a shadow on what is below them; the far ones are softer + # and dimmer, which is all depth of field is. + bands = [ # (count, x0, y0, width, depth, thickness, step x, step y, colour, blur, alpha) + (9, 1500, -60, 380, 95, 26, -150, 150, 4, "farther", 0.55), + (7, 3420, -40, 620, 150, 44, -230, 240, 2, "far", 0.8), + (5, 2650, 260, 980, 230, 74, -330, 340, 0, None, 1.0), + ] + body = [f' \n', + f' \n'] + for n, x0, y0, w, d, t, sx, sy, lvl, filt, alpha in bands: + body.append(f' \n' if filt else " \n") + for i in range(n): + x, y = x0 + sx * i, y0 + sy * i + sh = f"M{x+d+40:.1f} {y+t+50:.1f} L{x+d+w+40:.1f} {y+t+50:.1f} L{x+w+40:.1f} {y+d+t+70:.1f} L{x+40:.1f} {y+d+t+70:.1f} Z" + body.append(f' \n') + body.append(slab(x, y, w, d, t, None, alpha, f"d{min(4, lvl + i // 3)}")) + body.append(" \n") + body.append(f' ') + return svg("SchOS — Strata", + "Shift's slabs, repeated into a cascade that recedes into an indigo dark: near ones sharp and bright, far ones soft.", + "\n".join(defs), "".join(body)) + +# --- 3. Porcelain (light) ------------------------------------------------------ + +def porcelain(): + s = 6.0 + tx, ty = W / 2 - 109 * s, H / 2 - 108 * s + faces = [] + for i, (top, front, wall) in enumerate(SLABS): + g = "pv" if i == 2 else "p" # the top slab carries the accent, as a glaze + faces.append(f'') + defs = ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + +''' + GRAIN.format(a="0.06") + body = f''' + + + {"".join(faces)} + + ''' + return svg("SchOS — Porcelain", + "The light one: Shift as glazed porcelain on soft paper, with the accent spent on the top slab alone.", + defs, body) + +# --- 4. Dial --------------------------------------------------------------------- + +def annulus(cx, cy, r0, r1, a0, a1): + """An annular sector, angles in degrees clockwise from twelve o'clock.""" + def pt(r, a): + t = math.radians(a) + return cx + r * math.sin(t), cy - r * math.cos(t) + (x0, y0), (x1, y1) = pt(r1, a0), pt(r1, a1) + (x2, y2), (x3, y3) = pt(r0, a1), pt(r0, a0) + big = 1 if a1 - a0 > 180 else 0 + return (f"M{x0:.1f} {y0:.1f} A{r1} {r1} 0 {big} 1 {x1:.1f} {y1:.1f} " + f"L{x2:.1f} {y2:.1f} A{r0} {r0} 0 {big} 0 {x3:.1f} {y3:.1f} Z") + +def dial(): + cx, cy = W / 2, H / 2 + R = 520 # the machined face + # Brushed metal, radially: thin wedges whose lightness follows the angle + # to the light, which is what makes a turned surface look turned. + wedges = [] + # Each wedge overlaps the next by more than its own width, so no seam + # between two of them is ever a pixel of background -- which is what drew + # an interference cross through the middle of the first render. + for i in range(360): + a0, a1 = i * 1.0, i * 1.0 + 2.2 + lit = 0.5 + 0.5 * math.cos(math.radians(2 * (a0 - 315))) + jitter = ((i * 7919) % 13) / 13 * 0.08 + v = int(34 + 46 * lit + 30 * jitter) + wedges.append(f'') + slots = [] + for k in range(12): + a0 = k * 30 - 10 + if k == 1: + continue + slots.append(f'') + lit_slot = annulus(cx, cy, R + 120, R + 370, 30 - 10, 30 + 10) + defs = f''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +''' + GRAIN.format(a="0.05") + "\n" + blur("soft", 40) + body = f''' + + {"".join(slots)} + + + + + {"".join(wedges)} + + + + + + + ''' + return svg("SchOS — Dial", + "The SchFS uberblock ring as a piece of hardware: a turned metal face, the slots as keys set around it, the newest one and the root lit.", + defs, body) + +WALLS = {"graphite": graphite, "strata": strata, "porcelain": porcelain, "dial": dial} + +if __name__ == "__main__": + os.makedirs(OUT, exist_ok=True) + for name, fn in WALLS.items(): + with open(os.path.join(OUT, name + ".svg"), "w") as f: + f.write(fn()) + print(os.path.join(OUT, name + ".svg"))