Classes

Classes

A class is a struct with functions around it. Dispatch is static, so inheritance, a mixin and a generic are settled while the file is compiled.

  • Derived value in a base position

    The compiler builds the base representation once when it proves read-only access and equivalent method dispatch. Overrides, receiver writes and observable alias mutation remain outside that proof.

    TypeScript
    class Material {
    color: f32 = 0.5;
    response(): f32 { return this.color; }
    }
    class LeafMaterial extends Material {
    thickness: f32 = 1.;
    }
    function response(material: Material): f32 {
    return material.response();
    }
    export function leafResponse(): f32 {
    const material = new LeafMaterial();
    return response(material);
    }
    Emitted WGSL
    struct Material {
    color: f32,
    }
    struct LeafMaterial {
    color: f32,
    thickness: f32,
    }
    fn response(material: Material) -> f32 {
    return Material_response(material);
    }
    fn leafResponse() -> f32 {
    let material = LeafMaterial_new();
    return response(LeafMaterial_as_Material(material));
    }
  • Class without fields

    Construction and method calls work with no instance fields. The host value is {}; the GPU receives an internal storage member that is absent from the source value.

    TypeScript
    class Counter {
    next(value: f32): f32 { return value + 1.; }
    }
    export function next(value: f32): f32 {
    return new Counter().next(value);
    }
    Emitted WGSL
    struct Counter {
    _empty: u32,
    }
    fn Counter_new() -> Counter {
    var self_: Counter = Counter(0u);
    return self_;
    }
    fn Counter_next(self_: Counter, value: f32) -> f32 {
    return (value + 1.0);
    }
    fn next(value: f32) -> f32 {
    return Counter_next(Counter_new(), value);
    }
  • constructor and new

    new is a call of Ray_new, which builds the struct and hands it back.

    TypeScript
    class Ray {
    origin: vec3
    dir: vec3
    constructor(origin: vec3, dir: vec3) {
    this.origin = origin
    this.dir = dir
    }
    }
    Emitted WGSL
    fn Ray_new(origin: vec3<f32>, dir: vec3<f32>) -> Ray {
    let _cse0 = vec3<f32>(0.0, 0.0, 0.0);
    var self_: Ray = Ray(_cse0, _cse0);
    self_.origin = origin;
    self_.dir = dir;
    return self_;
    }
  • Method

    A function whose first parameter is the struct, and this reads as that parameter.

    TypeScript
    class Ray {
    origin: vec3
    dir: vec3
    at(t: f32): vec3 {
    return this.origin + this.dir * t
    }
    }
    Emitted WGSL
    fn Ray_at(self_: Ray, t: f32) -> vec3<f32> {
    return (self_.origin + (self_.dir * t));
    }
    fn hit(r: Ray) -> vec3<f32> {
    return Ray_at(r, 1.0);
    }
  • static method

    A function with no receiver, under the class name.

    TypeScript
    class Ray {
    origin: vec3
    dir: vec3
    static up(): vec3 {
    return vec3(0., 1., 0.)
    }
    }
    Emitted WGSL
    fn Ray_up() -> vec3<f32> {
    return vec3<f32>(0.0, 1.0, 0.0);
    }
  • Method that writes this

    A method that writes its object takes it by pointer on WGSL, and by inout on GLSL ES 3.00. orbit-inout

    TypeScript
    class Body {
    pos: vec3
    vel: vec3
    step(dt: f32): void {
    this.pos = this.pos + this.vel * dt
    }
    }
    Emitted WGSL
    fn Body_step(self_: ptr<function, Body>, dt: f32) {
    (*self_).pos = ((*self_).pos + ((*self_).vel * dt));
    }
  • extends and super

    The base's fields come first, an inherited method is lowered again, and super is a function of its own. shape-inheritance

    TypeScript
    class Ring extends Circle {
    width: f32
    sdf(p: vec2): f32 {
    return abs(super.sdf(p)) - this.width
    }
    }
    Emitted WGSL
    struct Ring {
    center: vec2<f32>,
    radius: f32,
    width: f32,
    }
    fn Ring_sdf(self_: Ring, p: vec2<f32>) -> f32 {
    return (abs(Ring_super_Circle_sdf(self_, p)) - self_.width);
    }
  • abstract class

    No struct for the abstract class. Each subclass carries the fields and its own copy of the method.

    TypeScript
    abstract class Shape {
    center: vec2
    abstract sdf(p: vec2): f32
    offset(p: vec2): vec2 {
    return p - this.center
    }
    }
    Emitted WGSL
    struct Disc {
    center: vec2<f32>,
    radius: f32,
    }
    fn Disc_offset(self_: Disc, p: vec2<f32>) -> vec2<f32> {
    return (p - self_.center);
    }
  • implements

    Checked by TypeScript alone. The struct is the class's own fields.

    TypeScript
    class Disc implements Placed {
    center: vec2
    radius: f32
    }
    Emitted WGSL
    struct Disc {
    center: vec2<f32>,
    radius: f32,
    }
  • Access modifiers

    Accepted, and they mean nothing to the shader. TypeScript is what enforces them.

    TypeScript
    class Sphere {
    private center: vec3
    readonly radius: f32
    public hit(p: vec3): f32 {
    return length(p - this.center) - this.radius
    }
    }
    Emitted WGSL
    struct Sphere {
    center: vec3<f32>,
    radius: f32,
    }
    fn Sphere_hit(self_: Sphere, p: vec3<f32>) -> f32 {
    return (length((p - self_.center)) - self_.radius);
    }
  • Getter and setter

    Each half is a function of the module, Disc_get_area and Disc_set_area, and a read or a write calls it. class-syntax

    TypeScript
    class Disc {
    radius: f32
    get area(): f32 {
    return this.radius * this.radius
    }
    }
    Emitted WGSL
    struct Disc {
    radius: f32,
    }
    fn Disc_get_area(self_: Disc) -> f32 {
    return (self_.radius * self_.radius);
    }
  • Mixin function

    The function runs while the file is compiled. Its members are spliced in, and Tinted is emitted nowhere. mixin-surface

    TypeScript
    function Tinted<T extends AnyClass>(Base: T) {
    return class extends Base {
    tint: vec3
    lit(cover: f32): vec3 {
    return this.tint * cover
    }
    }
    }
    class TintedDisc extends Tinted(Disc) {
    }
    Emitted WGSL
    struct TintedDisc {
    center: vec2<f32>,
    radius: f32,
    tint: vec3<f32>,
    }
    fn TintedDisc_lit(self_: TintedDisc, cover: f32) -> vec3<f32> {
    return (self_.tint * cover);
    }
  • Generic function

    One function per set of type arguments the program uses, and nothing called pick. generic-helpers

    TypeScript
    function pick<T>(a: T, b: T, c: bool): T {
    return c ? a : b
    }
    Emitted WGSL
    fn pick_f32(a: f32, b: f32, c: bool) -> f32 {
    return select(b, a, c);
    }
    fn pick_vec3(a: vec3<f32>, b: vec3<f32>, c: bool) -> vec3<f32> {
    return select(b, a, c);
    }
  • Generic class

    One struct per set of type arguments, each with its own copy of every method. generic-class

    TypeScript
    class Slot<T> {
    a: T
    b: T
    first(): T {
    return this.a
    }
    }
    Emitted WGSL
    struct Slot_f32 {
    a: f32,
    b: f32,
    }
    struct Slot_vec3 {
    a: vec3<f32>,
    b: vec3<f32>,
    }

Edit this page Report a problem