@roobscoob/vibes 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -32,19 +32,41 @@ A sketch says what each light should make, a hundred times a second.
32
32
  | `at({ x, y, t })` | the same light, looking from somewhere else or at another time |
33
33
 
34
34
  Return a color (anything that says one: see below), or `null` to leave the
35
- light as the layers below have it. `sketch("#f80")` makes every light one
36
- color; `sketch(palettes.dusk)` lays a palette across the room.
35
+ light as the layers below have it. `sketch("#f80")` makes every color light
36
+ one color; `sketch(palettes.dusk)` lays a palette across the room.
37
37
 
38
38
  - `light.rail` is `{ id, along, t, index, count }`: how far along its rail
39
39
  (meters, and 0 to 1), its place on it, and how many share it.
40
40
  - `light.random` is a number from 0 to 1 of its own, the same every time.
41
- - `light.below` is what the layers below this sketch make it, as a color.
41
+ - `light.below` is what the layers below this sketch make it, as a color, and
42
+ `light.belowOn` whether that's on at all.
42
43
  - `light.state` is the light's own memory, kept from tick to tick.
43
44
 
44
- Old bulbs only switch: `sketch()` turns them on at 100 lm or more and off
45
- below 70 (`sketch(fn, { switchOn, switchOff })` to change that). Something
46
- that isn't a color leaves its light as it is, with a warning in the console,
47
- so one slip doesn't stop the sketch.
45
+ Something that isn't a color leaves its light as it is, with a warning in
46
+ the console, so one slip doesn't stop the sketch.
47
+
48
+ ## Old bulbs
49
+
50
+ The old bulbs only switch, and look nothing like the color lights, so they
51
+ have a look of their own, apart from color's. Each look is named for the
52
+ kind of light it's for, as `light.kind` names them:
53
+
54
+ ```ts
55
+ export const update = sketch({
56
+ color: osc(3).color(palettes.dusk), // the color bulbs and panels
57
+ switch: osc(1, 0.25).thresh(), // the old bulbs: on or off
58
+ });
59
+ ```
60
+
61
+ `sketch(look)` is `sketch({ color: look })`: a color look is never asked
62
+ about old bulbs, and never switches them. Without a `switch` look they're
63
+ left as they are; `sketch(look, { switch })` gives one beside a color look.
64
+
65
+ A `switch` look gives `true` or `false`, a number (on at 0.5 or more, as
66
+ arclamp's own `on` channel: a field from 0 to 1 works, like
67
+ `noise(4).thresh(0.6)` or `clock(96).every(1).env()`), `"on"` or `"off"`, or
68
+ `null` to leave the bulb as it is. It can be a function of `here`, a field,
69
+ or one answer for every old bulb: `sketch({ switch: true })`.
48
70
 
49
71
  ## Colors
50
72
 
package/dist/color.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { type Vec3 } from "./lemtone.ts";
2
+ import type { Here } from "./here.ts";
2
3
  /** Anything that says a color: see the top of color.ts. */
3
4
  export type ColorInput = Color | string | number | readonly number[] | {
4
5
  readonly [key: string]: unknown;
@@ -151,6 +152,8 @@ export declare function xy(x: number, y: number, lm?: number): Color;
151
152
  /** Colors along a line from 0 to 1: palette(x) is the color at x. */
152
153
  export interface Palette {
153
154
  (x: number): Color;
155
+ /** Given a here instead, the palette laid across the room, left to right: its color at here.u. */
156
+ (here: Here): Color;
154
157
  readonly stops: readonly Color[];
155
158
  /** Where each stop is, 0 to 1. */
156
159
  readonly positions: readonly number[];
@@ -1 +1 @@
1
- {"version":3,"file":"color.d.ts","sourceRoot":"","sources":["../src/color.ts"],"names":[],"mappings":"AAiBA,OAAO,EAWL,KAAK,IAAI,EACV,MAAM,cAAc,CAAC;AAiBtB,2DAA2D;AAC3D,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG;IAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CAAC;AAE3G,uGAAuG;AACvG,MAAM,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;AAEpD,8DAA8D;AAC9D,MAAM,WAAW,YAAY;IAC3B,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,KAAK;IAChB,yDAAyD;IACzD,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,iCAAiC;IACjC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,gFAAgF;IAChF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,SAAI,EAKzD;IAED,yDAAyD;IACzD,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,KAAK,CAEjD;IAID,uGAAuG;IACvG,IAAI,GAAG,IAAI,MAAM,CAEhB;IAED,uDAAuD;IACvD,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,gEAAgE;IAChE,IAAI,MAAM,IAAI,MAAM,GAAG,IAAI,CAE1B;IAED,mEAAmE;IACnE,IAAI,aAAa,IAAI,MAAM,CAE1B;IAED,kFAAkF;IAClF,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,yDAAyD;IACzD,IAAI,UAAU,IAAI,MAAM,CAEvB;IAED,+CAA+C;IAC/C,IAAI,QAAQ,IAAI;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAE7E;IAED,gDAAgD;IAChD,IAAI,GAAG,IAAI,IAAI,CAId;IAED,iCAAiC;IACjC,IAAI,EAAE,IAAI;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAGjC;IAED;;;OAGG;IACH,IAAI,GAAG,IAAI;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAG7C;IAED,oEAAoE;IACpE,IAAI,GAAG,IAAI,MAAM,CAGhB;IAED,6FAA6F;IAC7F,IAAI,GAAG,IAAI;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAG7C;IAED,oDAAoD;IACpD,IAAI,KAAK,IAAI;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAG/C;IAED,2EAA2E;IAC3E,IAAI,KAAK,IAAI;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAG/C;IAID,qCAAqC;IACrC,EAAE,CAAC,EAAE,EAAE,MAAM,GAAG,KAAK,CAEpB;IAED,oEAAoE;IACpE,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,KAAK,CAEpB;IAED,yFAAyF;IACzF,IAAI,CAAC,CAAC,EAAE,MAAM,GAAG,KAAK,CAErB;IAED,kGAAkG;IAClG,IAAI,CAAC,OAAO,EAAE,YAAY,GAAG,KAAK,CAWjC;IAED;;;;OAIG;IACH,GAAG,CAAC,KAAK,EAAE,UAAU,EAAE,CAAC,SAAM,GAAG,KAAK,CASrC;IAED,wFAAwF;IACxF,GAAG,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,GAAG,KAAK,CAalC;IAED,mCAAmC;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,CAE/B;IAED,yEAAyE;IACzE,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,KAAK,CAEzB;IAED,2GAA2G;IAC3G,KAAK,CAAC,IAAI,GAAE,IAAW,GAAG,KAAK,CAE9B;IAED,8EAA8E;IAC9E,QAAQ,IAAI,MAAM,CAMjB;IAED,MAAM,IAAI;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAE/D;CACF;AAED,gBAAgB;AAChB,eAAO,MAAM,GAAG,EAAE,KAAyC,CAAC;AAU5D;;;GAGG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,KAAK,CAG3D;AAED;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,SAAkB,GAAG,KAAK,CAI3E;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,SAAkB,EAAE,QAAQ,SAAI,GAAG,KAAK,CAG9F;AAED,8GAA8G;AAC9G,wBAAgB,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,KAAK,CAGvE;AAED,4FAA4F;AAC5F,wBAAgB,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,KAAK,CAEvE;AAED,wEAAwE;AACxE,wBAAgB,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,KAAK,CAGrE;AAED,iGAAiG;AACjG,wBAAgB,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,KAAK,CAErE;AAED,sFAAsF;AACtF,eAAO,MAAM,GAAG,SAAU,MAAM,KAAG,KAAoB,CAAC;AAExD,mEAAmE;AACnE,wBAAgB,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,SAAkB,GAAG,KAAK,CAGpE;AAqJD,qEAAqE;AACrE,MAAM,WAAW,OAAO;IACtB,CAAC,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IACnB,QAAQ,CAAC,KAAK,EAAE,SAAS,KAAK,EAAE,CAAC;IACjC,kCAAkC;IAClC,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC;AAED,uEAAuE;AACvE,MAAM,MAAM,IAAI,GAAG,UAAU,GAAG,SAAS,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AAK9D,8BAA8B;AAC9B,eAAO,MAAM,SAAS,MAAO,OAAO,KAAG,CAAC,IAAI,OAAkF,CAAC;AAE/H;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,GAAG,KAAK,EAAE,IAAI,EAAE,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC,GAAG,OAAO,CAqBrE;AAED,mHAAmH;AACnH,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAI7C;AAED,qFAAqF;AACrF,eAAO,MAAM,QAAQ;IACnB,gDAAgD;IAChD,IAAI;IACJ,8CAA8C;IAC9C,GAAG;IACH,gDAAgD;IAChD,KAAK;IACL,4CAA4C;IAC5C,MAAM;CACP,CAAC;AAIF,mDAAmD;AACnD,MAAM,WAAW,IAAI;IACnB,6BAA6B;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,qGAAqG;IACrG,KAAK,CAAC,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;IAC7B,4CAA4C;IAC5C,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,iJAAiJ;IACjJ,KAAK,CAAC,CAAC,EAAE,UAAU,GAAG,KAAK,CAAC;CAC7B;AAKD;;;;;GAKG;AACH,eAAO,MAAM,IAAI,EAAE,IAelB,CAAC"}
1
+ {"version":3,"file":"color.d.ts","sourceRoot":"","sources":["../src/color.ts"],"names":[],"mappings":"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"}
package/dist/color.js CHANGED
@@ -433,7 +433,8 @@ export function palette(...given) {
433
433
  const stops = Object.freeze(placed.map((p) => Object.freeze(p.c)));
434
434
  const positions = Object.freeze(placed.map((p) => p.at));
435
435
  const at = (x) => {
436
- const v = Number.isFinite(x) ? x : 0;
436
+ const along = typeof x === "number" ? x : x?.u;
437
+ const v = typeof along === "number" && Number.isFinite(along) ? along : 0;
437
438
  if (v <= positions[0])
438
439
  return stops[0];
439
440
  for (let i = 1; i < stops.length; i++) {
package/dist/here.d.ts CHANGED
@@ -38,6 +38,8 @@ export declare class Light {
38
38
  readonly random: number;
39
39
  /** What the layers below this sketch are making it do. */
40
40
  below: Color;
41
+ /** Whether the layers below have it on (for an old bulb), or lit at all (for a color light). */
42
+ belowOn: boolean;
41
43
  /** Its own memory, kept from one tick to the next. */
42
44
  state: Record<string, any>;
43
45
  constructor(p: PrevLight, x: number, y: number);
@@ -1 +1 @@
1
- {"version":3,"file":"here.d.ts","sourceRoot":"","sources":["../src/here.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"here.d.ts","sourceRoot":"","sources":["../src/here.ts"],"names":[],"mappings":"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"}
package/dist/here.js CHANGED
@@ -30,6 +30,8 @@ export class Light {
30
30
  random;
31
31
  /** What the layers below this sketch are making it do. */
32
32
  below = off;
33
+ /** Whether the layers below have it on (for an old bulb), or lit at all (for a color light). */
34
+ belowOn = false;
33
35
  /** Its own memory, kept from one tick to the next. */
34
36
  state = {};
35
37
  constructor(p, x, y) {
package/dist/index.d.ts CHANGED
@@ -4,5 +4,5 @@ export { Here, Light, Room, type Rail, type RailSpot } from "./here.ts";
4
4
  export { delay, field, gradient, isField, kaleid, move, noise, osc, rings, rotate, scale, solid, speed, voronoi, warp, type ColorParam, type Field, type Param, } from "./field.ts";
5
5
  export { clock, type Clock, type Every } from "./signal.ts";
6
6
  export { chance, clamp, decay, ease, fract, hash, lerp, noiseAt, remap, saw, smoothstep, square, tri, wave } from "./shape.ts";
7
- export { sketch, type Look, type LookResult, type SketchOptions } from "./sketch.ts";
7
+ export { sketch, type Look, type LookResult, type Looks, type SketchOptions, type SwitchLook, type SwitchResult } from "./sketch.ts";
8
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,YAAY,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACzE,OAAO,EACL,IAAI,EACJ,KAAK,EACL,KAAK,EACL,GAAG,EACH,GAAG,EACH,SAAS,EACT,OAAO,EACP,GAAG,EACH,KAAK,EACL,KAAK,EACL,OAAO,EACP,QAAQ,EACR,GAAG,EACH,SAAS,EACT,KAAK,EACL,EAAE,EACF,KAAK,IAAI,EACT,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,OAAO,EACZ,KAAK,IAAI,GACV,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE,KAAK,QAAQ,EAAE,MAAM,WAAW,CAAC;AACxE,OAAO,EACL,KAAK,EACL,KAAK,EACL,QAAQ,EACR,OAAO,EACP,MAAM,EACN,IAAI,EACJ,KAAK,EACL,GAAG,EACH,KAAK,EACL,MAAM,EACN,KAAK,EACL,KAAK,EACL,KAAK,EACL,OAAO,EACP,IAAI,EACJ,KAAK,UAAU,EACf,KAAK,KAAK,EACV,KAAK,KAAK,GACX,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,KAAK,EAAE,KAAK,KAAK,EAAE,KAAK,KAAK,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAC/H,OAAO,EAAE,MAAM,EAAE,KAAK,IAAI,EAAE,KAAK,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,YAAY,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACzE,OAAO,EACL,IAAI,EACJ,KAAK,EACL,KAAK,EACL,GAAG,EACH,GAAG,EACH,SAAS,EACT,OAAO,EACP,GAAG,EACH,KAAK,EACL,KAAK,EACL,OAAO,EACP,QAAQ,EACR,GAAG,EACH,SAAS,EACT,KAAK,EACL,EAAE,EACF,KAAK,IAAI,EACT,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,OAAO,EACZ,KAAK,IAAI,GACV,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE,KAAK,QAAQ,EAAE,MAAM,WAAW,CAAC;AACxE,OAAO,EACL,KAAK,EACL,KAAK,EACL,QAAQ,EACR,OAAO,EACP,MAAM,EACN,IAAI,EACJ,KAAK,EACL,GAAG,EACH,KAAK,EACL,MAAM,EACN,KAAK,EACL,KAAK,EACL,KAAK,EACL,OAAO,EACP,IAAI,EACJ,KAAK,UAAU,EACf,KAAK,KAAK,EACV,KAAK,KAAK,GACX,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,KAAK,EAAE,KAAK,KAAK,EAAE,KAAK,KAAK,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAC/H,OAAO,EAAE,MAAM,EAAE,KAAK,IAAI,EAAE,KAAK,UAAU,EAAE,KAAK,KAAK,EAAE,KAAK,aAAa,EAAE,KAAK,UAAU,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC"}
package/dist/sketch.d.ts CHANGED
@@ -1,21 +1,29 @@
1
- import { type ColorInput, type Palette } from "./color.ts";
1
+ import { type ColorInput } from "./color.ts";
2
2
  import type { Update } from "./arclamp.ts";
3
3
  import { Here } from "./here.ts";
4
- /** What a light should make, here and now; null, undefined or false leaves it as it is below. */
4
+ /** What a color light should make, here and now; null, undefined or false leaves it as it is below. */
5
5
  export type LookResult = ColorInput | null | undefined | false | void;
6
- /**
7
- * What a light should make, here and now: a function of here giving a color,
8
- * one color for every light, or a palette, laid across the room.
9
- */
10
- export type Look = ((here: Here) => LookResult) | ColorInput | Palette;
6
+ /** The color lights' look: a function of here giving a color, one color for every light, or a palette, laid across the room. */
7
+ export type Look = ((here: Here) => LookResult) | ColorInput;
8
+ /** Whether an old bulb should be on: true or false, a number (on at 0.5 or more), "on" or "off"; null or undefined leaves it as it is. */
9
+ export type SwitchResult = boolean | number | string | null | undefined | void;
10
+ /** The old bulbs' look: a function of here (a field too) saying whether each is on, or one answer for them all. */
11
+ export type SwitchLook = ((here: Here) => SwitchResult) | boolean | number | string;
12
+ /** A look for each kind of light (light.kind); either may be left out, and its lights are left as they are. */
13
+ export interface Looks {
14
+ /** For the color bulbs and panels. */
15
+ color?: Look;
16
+ /** For the old bulbs, which only switch. */
17
+ switch?: SwitchLook;
18
+ }
11
19
  export interface SketchOptions {
12
- /** Old bulbs switch on when asked for this many lumens or more (default 100: about halfway, to the eye, to one that's on). */
13
- switchOn?: number;
14
- /** And off when asked for fewer than this (default 70). */
15
- switchOff?: number;
20
+ /** The old bulbs' look, beside a color look given on its own. */
21
+ switch?: SwitchLook;
16
22
  }
17
- /** Your look (a function of here, or a field) as arclamp's update(). */
23
+ /** Your color look (a function of here, or a field) as arclamp's update(); old bulbs are left alone, unless given `switch`. */
18
24
  export declare function sketch(look: (here: Here) => LookResult, options?: SketchOptions): Update;
19
- /** One color for every light, or a palette laid across the room, as arclamp's update(). */
20
- export declare function sketch(look: ColorInput | Palette, options?: SketchOptions): Update;
25
+ /** A look for each kind of light: { color, switch }. */
26
+ export declare function sketch(looks: Looks): Update;
27
+ /** One color for every color light, or a palette laid across the room, as arclamp's update(). */
28
+ export declare function sketch(look: ColorInput, options?: SketchOptions): Update;
21
29
  //# sourceMappingURL=sketch.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sketch.d.ts","sourceRoot":"","sources":["../src/sketch.ts"],"names":[],"mappings":"AAeA,OAAO,EAA2B,KAAK,UAAU,EAAE,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AACpF,OAAO,KAAK,EAAa,MAAM,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAW,IAAI,EAAiC,MAAM,WAAW,CAAC;AAKzE,iGAAiG;AACjG,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,IAAI,GAAG,SAAS,GAAG,KAAK,GAAG,IAAI,CAAC;AAEtE;;;GAGG;AACH,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,IAAI,KAAK,UAAU,CAAC,GAAG,UAAU,GAAG,OAAO,CAAC;AAEvE,MAAM,WAAW,aAAa;IAC5B,8HAA8H;IAC9H,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAKD,wEAAwE;AACxE,wBAAgB,MAAM,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,UAAU,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC;AAC1F,2FAA2F;AAC3F,wBAAgB,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC"}
1
+ {"version":3,"file":"sketch.d.ts","sourceRoot":"","sources":["../src/sketch.ts"],"names":[],"mappings":"AAuBA,OAAO,EAA2B,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AACtE,OAAO,KAAK,EAAa,MAAM,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAW,IAAI,EAAiC,MAAM,WAAW,CAAC;AAKzE,uGAAuG;AACvG,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,IAAI,GAAG,SAAS,GAAG,KAAK,GAAG,IAAI,CAAC;AAEtE,gIAAgI;AAChI,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,IAAI,KAAK,UAAU,CAAC,GAAG,UAAU,CAAC;AAE7D,0IAA0I;AAC1I,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CAAC;AAE/E,mHAAmH;AACnH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,IAAI,EAAE,IAAI,KAAK,YAAY,CAAC,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;AAEpF,+GAA+G;AAC/G,MAAM,WAAW,KAAK;IACpB,sCAAsC;IACtC,KAAK,CAAC,EAAE,IAAI,CAAC;IACb,4CAA4C;IAC5C,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,iEAAiE;IACjE,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB;AAKD,+HAA+H;AAC/H,wBAAgB,MAAM,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,UAAU,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC;AAC1F,wDAAwD;AACxD,wBAAgB,MAAM,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAAC;AAC7C,iGAAiG;AACjG,wBAAgB,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC"}
package/dist/sketch.js CHANGED
@@ -1,28 +1,37 @@
1
- // sketch(): your look as arclamp's update().
1
+ // sketch(): your looks as arclamp's update().
2
2
  //
3
- // A look is a function of here (your own, or a field), or one color for
4
- // every light (a palette is laid across the room, left to right). Each tick
5
- // sketch() asks it what every light should make now, and turns that into
6
- // channels: lemtone channels for color lights, and on or off for old bulbs
7
- // (on at `switchOn` lumens or more, off below `switchOff`, and left as they
8
- // were between, so a pattern hovering near the line doesn't chatter).
3
+ // Color lights and old bulbs are controlled apart, each by a look of its own,
4
+ // named for the kind of light it's for (light.kind):
9
5
  //
10
- // Anything a look gives that says a color will do: a Color, "#f80",
11
- // "2700K", { r, g, b }, … Null, undefined or false leaves the light as it is
12
- // below. Something that isn't a color, or a color that isn't all numbers,
13
- // leaves it as it is too, with a warning (once for each kind of mistake), so
14
- // one slip doesn't stop the sketch.
6
+ // sketch({ color: osc(3).color(palettes.dusk), switch: osc(1, 0.25).thresh() })
7
+ //
8
+ // The color look is asked about the color bulbs and panels only: a function
9
+ // of here (your own, or a field) giving a color, one color for every light,
10
+ // or a palette laid across the room. Anything that says a color will do (a
11
+ // Color, "#f80", "2700K", { r, g, b }, …). sketch(look) is
12
+ // sketch({ color: look }).
13
+ //
14
+ // The switch look is asked about the old bulbs only, which just switch: true or
15
+ // false, a number (on at 0.5 or more, as arclamp's own `on` channel), "on" or
16
+ // "off", or a function of here (a field too) giving one. Without one, the old
17
+ // bulbs are left as they are; a color never switches them.
18
+ //
19
+ // Null or undefined from either leaves the light as it is below (so does
20
+ // false from a color look). Something that can't be read leaves it as it is
21
+ // too, with a warning (once for each kind of mistake), so one slip doesn't
22
+ // stop the sketch.
15
23
  import { Color, color, isPalette } from "./color.js";
16
24
  import { colorOf, Here, roomOf, sameLayout } from "./here.js";
17
25
  // A tick after a long gap (the sketch paused, or held up) counts as at most this long.
18
26
  const MAX_DT = 0.25;
19
- export function sketch(look, options = {}) {
20
- const ask = lookOf(look);
21
- const switchOn = options.switchOn ?? 100;
22
- const switchOff = Math.min(options.switchOff ?? 70, switchOn);
27
+ export function sketch(given, options = {}) {
28
+ if (options && typeof options === "object" && "on" in options)
29
+ throw new Error(ON_KEY);
30
+ const looks = isLooks(given) ? { switch: options.switch, ...given } : { color: given, switch: options.switch };
31
+ const askColor = looks.color === undefined ? null : lookOf(looks.color);
32
+ const askSwitch = looks.switch === undefined ? null : switchLookOf(looks.switch);
23
33
  let room = null;
24
34
  let last = null;
25
- const on = new Map();
26
35
  const warned = new Set();
27
36
  const warn = (kind, text) => {
28
37
  if (warned.has(kind))
@@ -41,7 +50,20 @@ export function sketch(look, options = {}) {
41
50
  for (let i = 0; i < list.length; i++) {
42
51
  const light = room.lights[i];
43
52
  light.below = colorOf(list[i], light.kind);
53
+ light.belowOn = light.below.lm > 0;
54
+ const ask = light.kind === "switch" ? askSwitch : askColor;
55
+ if (!ask)
56
+ continue;
44
57
  const v = ask(new Here(light.x, light.y, t, dt, nowMs, light, room));
58
+ if (light.kind === "switch") {
59
+ const on = readOn(v);
60
+ if (on === "unreadable") {
61
+ warn("on", `light ${light.id} is an old bulb, which only switches: its look gives true or false, a number (on at 0.5 or more), or "on" or "off", not ${describe(v)}; give old bulbs a look of their own, sketch({ color, switch })`);
62
+ }
63
+ else if (on !== null)
64
+ lights.push({ id: light.id, channels: { on: on ? 1 : 0 } });
65
+ continue;
66
+ }
45
67
  if (v === null || v === undefined || v === false)
46
68
  continue;
47
69
  let c;
@@ -57,17 +79,6 @@ export function sketch(look, options = {}) {
57
79
  }
58
80
  continue;
59
81
  }
60
- if (light.kind === "switch") {
61
- if (!Number.isFinite(c.lm)) {
62
- warn("nan", `light ${light.id} was given a color that isn't all numbers, ${JSON.stringify(c)}, so it's left as it is`);
63
- continue;
64
- }
65
- const was = on.get(light.id) ?? c.lm >= switchOn;
66
- const now = c.lm >= switchOn ? true : c.lm < switchOff ? false : was;
67
- on.set(light.id, now);
68
- lights.push({ id: light.id, channels: { on: now ? 1 : 0 } });
69
- continue;
70
- }
71
82
  const channels = c.channels;
72
83
  if (!Number.isFinite(channels.a) || !Number.isFinite(channels.b) || !Number.isFinite(channels.brightness) || !Number.isFinite(channels.fidelity)) {
73
84
  warn("nan", `light ${light.id} was given a color that isn't all numbers, ${JSON.stringify(c)}, so it's left as it is`);
@@ -78,7 +89,18 @@ export function sketch(look, options = {}) {
78
89
  return { lights };
79
90
  };
80
91
  }
81
- /** The look as a function of here. One color is the same everywhere (and said wrong, an error now, not each tick). */
92
+ const ON_KEY = 'old bulbs\' look is `switch`, for the kind of light it\'s for: sketch({ color, switch }), or sketch(look, { switch })';
93
+ /** Whether a sketch was given a look for each kind of light ({ color, switch }) rather than one look. */
94
+ function isLooks(v) {
95
+ if (!v || typeof v !== "object" || v instanceof Color || Array.isArray(v))
96
+ return false;
97
+ const keys = Object.keys(v);
98
+ // { on } would read as an old bulb's channels, a color: say what's meant instead.
99
+ if (keys.includes("on"))
100
+ throw new Error(ON_KEY);
101
+ return keys.length > 0 && keys.every((k) => k === "color" || k === "switch");
102
+ }
103
+ /** The color look as a function of here. One color is the same everywhere (and said wrong, an error now, not each tick). */
82
104
  function lookOf(look) {
83
105
  if (isPalette(look))
84
106
  return (here) => look(here.u);
@@ -87,3 +109,33 @@ function lookOf(look) {
87
109
  const c = color(look);
88
110
  return () => c;
89
111
  }
112
+ /** The switch look as a function of here. One answer is the same everywhere (and said wrong, an error now). */
113
+ function switchLookOf(look) {
114
+ if (typeof look === "function")
115
+ return look;
116
+ const on = readOn(look);
117
+ if (on === "unreadable")
118
+ throw new Error(`${describe(look)} isn't on or off: give true or false, a number (on at 0.5 or more), or "on" or "off"`);
119
+ return () => on;
120
+ }
121
+ /** On (true), off (false), leave it (null), or can't tell. */
122
+ function readOn(v) {
123
+ if (v === null || v === undefined)
124
+ return null;
125
+ if (typeof v === "boolean")
126
+ return v;
127
+ if (typeof v === "number")
128
+ return Number.isFinite(v) ? v >= 0.5 : "unreadable";
129
+ if (typeof v === "string") {
130
+ const s = v.trim().toLowerCase();
131
+ if (s === "on" || s === "true" || s === "yes")
132
+ return true;
133
+ if (s === "off" || s === "false" || s === "no")
134
+ return false;
135
+ return s !== "" && Number.isFinite(Number(s)) ? Number(s) >= 0.5 : "unreadable";
136
+ }
137
+ if (typeof v === "object" && !(v instanceof Color) && "on" in v)
138
+ return readOn(v.on);
139
+ return "unreadable";
140
+ }
141
+ const describe = (v) => (v instanceof Color ? `a color (${v})` : typeof v === "string" ? `"${v}"` : JSON.stringify(v) ?? String(v));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@roobscoob/vibes",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Color, light and time for sketches on the lights at the Recurse Center",
5
5
  "keywords": ["lighting", "lemtone", "oklab", "arclamp", "recurse-center"],
6
6
  "license": "MIT",
package/src/color.ts CHANGED
@@ -28,6 +28,7 @@ import {
28
28
  xyzToOklab,
29
29
  type Vec3,
30
30
  } from "./lemtone.ts";
31
+ import type { Here } from "./here.ts";
31
32
  import { parseColor, type Spec } from "./parse.ts";
32
33
  import { clamp, lerp } from "./shape.ts";
33
34
  import {
@@ -484,6 +485,8 @@ function fromObject(o: Record<string, unknown>): Color {
484
485
  /** Colors along a line from 0 to 1: palette(x) is the color at x. */
485
486
  export interface Palette {
486
487
  (x: number): Color;
488
+ /** Given a here instead, the palette laid across the room, left to right: its color at here.u. */
489
+ (here: Here): Color;
487
490
  readonly stops: readonly Color[];
488
491
  /** Where each stop is, 0 to 1. */
489
492
  readonly positions: readonly number[];
@@ -512,8 +515,9 @@ export function palette(...given: Stop[] | [readonly Stop[]]): Palette {
512
515
  .sort((x, y) => x.at - y.at);
513
516
  const stops = Object.freeze(placed.map((p) => Object.freeze(p.c)));
514
517
  const positions = Object.freeze(placed.map((p) => p.at));
515
- const at = (x: number): Color => {
516
- const v = Number.isFinite(x) ? x : 0;
518
+ const at = (x: number | Here): Color => {
519
+ const along = typeof x === "number" ? x : (x as Partial<Here> | null)?.u;
520
+ const v = typeof along === "number" && Number.isFinite(along) ? along : 0;
517
521
  if (v <= positions[0]!) return stops[0]!;
518
522
  for (let i = 1; i < stops.length; i++) {
519
523
  if (v <= positions[i]!) {
@@ -523,7 +527,7 @@ export function palette(...given: Stop[] | [readonly Stop[]]): Palette {
523
527
  }
524
528
  return stops[stops.length - 1]!;
525
529
  };
526
- return Object.assign(at, { stops, positions });
530
+ return Object.assign(at, { stops, positions }) as Palette;
527
531
  }
528
532
 
529
533
  /** A palette from anything that says one: a palette, a list of stops, or one color (from dark at 0 to it at 1). */
package/src/here.ts CHANGED
@@ -55,6 +55,8 @@ export class Light {
55
55
  readonly random: number;
56
56
  /** What the layers below this sketch are making it do. */
57
57
  below: Color = off;
58
+ /** Whether the layers below have it on (for an old bulb), or lit at all (for a color light). */
59
+ belowOn = false;
58
60
  /** Its own memory, kept from one tick to the next. */
59
61
  state: Record<string, any> = {};
60
62
 
package/src/index.ts CHANGED
@@ -49,4 +49,4 @@ export {
49
49
  } from "./field.ts";
50
50
  export { clock, type Clock, type Every } from "./signal.ts";
51
51
  export { chance, clamp, decay, ease, fract, hash, lerp, noiseAt, remap, saw, smoothstep, square, tri, wave } from "./shape.ts";
52
- export { sketch, type Look, type LookResult, type SketchOptions } from "./sketch.ts";
52
+ export { sketch, type Look, type LookResult, type Looks, type SketchOptions, type SwitchLook, type SwitchResult } from "./sketch.ts";
package/src/sketch.ts CHANGED
@@ -1,55 +1,74 @@
1
- // sketch(): your look as arclamp's update().
1
+ // sketch(): your looks as arclamp's update().
2
2
  //
3
- // A look is a function of here (your own, or a field), or one color for
4
- // every light (a palette is laid across the room, left to right). Each tick
5
- // sketch() asks it what every light should make now, and turns that into
6
- // channels: lemtone channels for color lights, and on or off for old bulbs
7
- // (on at `switchOn` lumens or more, off below `switchOff`, and left as they
8
- // were between, so a pattern hovering near the line doesn't chatter).
3
+ // Color lights and old bulbs are controlled apart, each by a look of its own,
4
+ // named for the kind of light it's for (light.kind):
9
5
  //
10
- // Anything a look gives that says a color will do: a Color, "#f80",
11
- // "2700K", { r, g, b }, … Null, undefined or false leaves the light as it is
12
- // below. Something that isn't a color, or a color that isn't all numbers,
13
- // leaves it as it is too, with a warning (once for each kind of mistake), so
14
- // one slip doesn't stop the sketch.
6
+ // sketch({ color: osc(3).color(palettes.dusk), switch: osc(1, 0.25).thresh() })
7
+ //
8
+ // The color look is asked about the color bulbs and panels only: a function
9
+ // of here (your own, or a field) giving a color, one color for every light,
10
+ // or a palette laid across the room. Anything that says a color will do (a
11
+ // Color, "#f80", "2700K", { r, g, b }, …). sketch(look) is
12
+ // sketch({ color: look }).
13
+ //
14
+ // The switch look is asked about the old bulbs only, which just switch: true or
15
+ // false, a number (on at 0.5 or more, as arclamp's own `on` channel), "on" or
16
+ // "off", or a function of here (a field too) giving one. Without one, the old
17
+ // bulbs are left as they are; a color never switches them.
18
+ //
19
+ // Null or undefined from either leaves the light as it is below (so does
20
+ // false from a color look). Something that can't be read leaves it as it is
21
+ // too, with a warning (once for each kind of mistake), so one slip doesn't
22
+ // stop the sketch.
15
23
 
16
- import { Color, color, isPalette, type ColorInput, type Palette } from "./color.ts";
24
+ import { Color, color, isPalette, type ColorInput } from "./color.ts";
17
25
  import type { Out, Prev, Update } from "./arclamp.ts";
18
26
  import { colorOf, Here, roomOf, sameLayout, type Room } from "./here.ts";
19
27
 
20
28
  // Plain V8 (arclamp's sandbox) has only what arclamp's prelude gives it.
21
29
  declare const console: { warn(...data: unknown[]): void };
22
30
 
23
- /** What a light should make, here and now; null, undefined or false leaves it as it is below. */
31
+ /** What a color light should make, here and now; null, undefined or false leaves it as it is below. */
24
32
  export type LookResult = ColorInput | null | undefined | false | void;
25
33
 
26
- /**
27
- * What a light should make, here and now: a function of here giving a color,
28
- * one color for every light, or a palette, laid across the room.
29
- */
30
- export type Look = ((here: Here) => LookResult) | ColorInput | Palette;
34
+ /** The color lights' look: a function of here giving a color, one color for every light, or a palette, laid across the room. */
35
+ export type Look = ((here: Here) => LookResult) | ColorInput;
36
+
37
+ /** Whether an old bulb should be on: true or false, a number (on at 0.5 or more), "on" or "off"; null or undefined leaves it as it is. */
38
+ export type SwitchResult = boolean | number | string | null | undefined | void;
39
+
40
+ /** The old bulbs' look: a function of here (a field too) saying whether each is on, or one answer for them all. */
41
+ export type SwitchLook = ((here: Here) => SwitchResult) | boolean | number | string;
42
+
43
+ /** A look for each kind of light (light.kind); either may be left out, and its lights are left as they are. */
44
+ export interface Looks {
45
+ /** For the color bulbs and panels. */
46
+ color?: Look;
47
+ /** For the old bulbs, which only switch. */
48
+ switch?: SwitchLook;
49
+ }
31
50
 
32
51
  export interface SketchOptions {
33
- /** Old bulbs switch on when asked for this many lumens or more (default 100: about halfway, to the eye, to one that's on). */
34
- switchOn?: number;
35
- /** And off when asked for fewer than this (default 70). */
36
- switchOff?: number;
52
+ /** The old bulbs' look, beside a color look given on its own. */
53
+ switch?: SwitchLook;
37
54
  }
38
55
 
39
56
  // A tick after a long gap (the sketch paused, or held up) counts as at most this long.
40
57
  const MAX_DT = 0.25;
41
58
 
42
- /** Your look (a function of here, or a field) as arclamp's update(). */
59
+ /** Your color look (a function of here, or a field) as arclamp's update(); old bulbs are left alone, unless given `switch`. */
43
60
  export function sketch(look: (here: Here) => LookResult, options?: SketchOptions): Update;
44
- /** One color for every light, or a palette laid across the room, as arclamp's update(). */
45
- export function sketch(look: ColorInput | Palette, options?: SketchOptions): Update;
46
- export function sketch(look: Look, options: SketchOptions = {}): Update {
47
- const ask = lookOf(look);
48
- const switchOn = options.switchOn ?? 100;
49
- const switchOff = Math.min(options.switchOff ?? 70, switchOn);
61
+ /** A look for each kind of light: { color, switch }. */
62
+ export function sketch(looks: Looks): Update;
63
+ /** One color for every color light, or a palette laid across the room, as arclamp's update(). */
64
+ export function sketch(look: ColorInput, options?: SketchOptions): Update;
65
+ export function sketch(given: Looks | Look, options: SketchOptions = {}): Update {
66
+ if (options && typeof options === "object" && "on" in options) throw new Error(ON_KEY);
67
+ const looks: Looks = isLooks(given) ? { switch: options.switch, ...given } : { color: given as Look, switch: options.switch };
68
+ const askColor = looks.color === undefined ? null : lookOf(looks.color);
69
+ const askSwitch = looks.switch === undefined ? null : switchLookOf(looks.switch);
50
70
  let room: Room | null = null;
51
71
  let last: number | null = null;
52
- const on = new Map<number, boolean>();
53
72
  const warned = new Set<string>();
54
73
 
55
74
  const warn = (kind: string, text: string) => {
@@ -69,9 +88,23 @@ export function sketch(look: Look, options: SketchOptions = {}): Update {
69
88
  for (let i = 0; i < list.length; i++) {
70
89
  const light = room.lights[i]!;
71
90
  light.below = colorOf(list[i]!, light.kind);
91
+ light.belowOn = light.below.lm > 0;
92
+ const ask = light.kind === "switch" ? askSwitch : askColor;
93
+ if (!ask) continue;
72
94
  const v = ask(new Here(light.x, light.y, t, dt, nowMs, light, room));
73
- if (v === null || v === undefined || v === false) continue;
74
95
 
96
+ if (light.kind === "switch") {
97
+ const on = readOn(v);
98
+ if (on === "unreadable") {
99
+ warn(
100
+ "on",
101
+ `light ${light.id} is an old bulb, which only switches: its look gives true or false, a number (on at 0.5 or more), or "on" or "off", not ${describe(v)}; give old bulbs a look of their own, sketch({ color, switch })`,
102
+ );
103
+ } else if (on !== null) lights.push({ id: light.id, channels: { on: on ? 1 : 0 } });
104
+ continue;
105
+ }
106
+
107
+ if (v === null || v === undefined || v === false) continue;
75
108
  let c: Color;
76
109
  try {
77
110
  c = v instanceof Color ? v : color(v as ColorInput);
@@ -83,19 +116,6 @@ export function sketch(look: Look, options: SketchOptions = {}): Update {
83
116
  }
84
117
  continue;
85
118
  }
86
-
87
- if (light.kind === "switch") {
88
- if (!Number.isFinite(c.lm)) {
89
- warn("nan", `light ${light.id} was given a color that isn't all numbers, ${JSON.stringify(c)}, so it's left as it is`);
90
- continue;
91
- }
92
- const was = on.get(light.id) ?? c.lm >= switchOn;
93
- const now = c.lm >= switchOn ? true : c.lm < switchOff ? false : was;
94
- on.set(light.id, now);
95
- lights.push({ id: light.id, channels: { on: now ? 1 : 0 } });
96
- continue;
97
- }
98
-
99
119
  const channels = c.channels;
100
120
  if (!Number.isFinite(channels.a) || !Number.isFinite(channels.b) || !Number.isFinite(channels.brightness) || !Number.isFinite(channels.fidelity)) {
101
121
  warn("nan", `light ${light.id} was given a color that isn't all numbers, ${JSON.stringify(c)}, so it's left as it is`);
@@ -107,10 +127,46 @@ export function sketch(look: Look, options: SketchOptions = {}): Update {
107
127
  };
108
128
  }
109
129
 
110
- /** The look as a function of here. One color is the same everywhere (and said wrong, an error now, not each tick). */
130
+ const ON_KEY = 'old bulbs\' look is `switch`, for the kind of light it\'s for: sketch({ color, switch }), or sketch(look, { switch })';
131
+
132
+ /** Whether a sketch was given a look for each kind of light ({ color, switch }) rather than one look. */
133
+ function isLooks(v: unknown): v is Looks {
134
+ if (!v || typeof v !== "object" || v instanceof Color || Array.isArray(v)) return false;
135
+ const keys = Object.keys(v);
136
+ // { on } would read as an old bulb's channels, a color: say what's meant instead.
137
+ if (keys.includes("on")) throw new Error(ON_KEY);
138
+ return keys.length > 0 && keys.every((k) => k === "color" || k === "switch");
139
+ }
140
+
141
+ /** The color look as a function of here. One color is the same everywhere (and said wrong, an error now, not each tick). */
111
142
  function lookOf(look: Look): (here: Here) => unknown {
112
143
  if (isPalette(look)) return (here) => look(here.u);
113
144
  if (typeof look === "function") return look as (here: Here) => unknown;
114
145
  const c = color(look);
115
146
  return () => c;
116
147
  }
148
+
149
+ /** The switch look as a function of here. One answer is the same everywhere (and said wrong, an error now). */
150
+ function switchLookOf(look: SwitchLook): (here: Here) => unknown {
151
+ if (typeof look === "function") return look as (here: Here) => unknown;
152
+ const on = readOn(look);
153
+ if (on === "unreadable") throw new Error(`${describe(look)} isn't on or off: give true or false, a number (on at 0.5 or more), or "on" or "off"`);
154
+ return () => on;
155
+ }
156
+
157
+ /** On (true), off (false), leave it (null), or can't tell. */
158
+ function readOn(v: unknown): boolean | null | "unreadable" {
159
+ if (v === null || v === undefined) return null;
160
+ if (typeof v === "boolean") return v;
161
+ if (typeof v === "number") return Number.isFinite(v) ? v >= 0.5 : "unreadable";
162
+ if (typeof v === "string") {
163
+ const s = v.trim().toLowerCase();
164
+ if (s === "on" || s === "true" || s === "yes") return true;
165
+ if (s === "off" || s === "false" || s === "no") return false;
166
+ return s !== "" && Number.isFinite(Number(s)) ? Number(s) >= 0.5 : "unreadable";
167
+ }
168
+ if (typeof v === "object" && !(v instanceof Color) && "on" in v) return readOn((v as { on: unknown }).on);
169
+ return "unreadable";
170
+ }
171
+
172
+ const describe = (v: unknown) => (v instanceof Color ? `a color (${v})` : typeof v === "string" ? `"${v}"` : JSON.stringify(v) ?? String(v));