# Panel light

> useRectLight is a lamp shaped like a rectangle that shines from its front only: a strip in the ceiling and a window, beside bulbs to compare.

Example `rect-light-3d`, running at https://nacatamalon.com/en/examples/rect-light-3d. The whole catalogue: https://nacatamalon.com/llms/examples.md.

`useRectLight` is a lamp shaped like a rectangle that shines from its front only. Each corner is lit from the nearest point of the panel, so a long strip in the ceiling lights the corridor under it evenly and a window lights the wall and the floor in a band, where a bulb would make one bright spot.

Press `2` to put bulbs in the same places and compare, and the arrows to make the strip shorter or longer while it runs.

## Code

`index.ts`:

```ts
import { createGame } from 'nacatamalon';
import * as Settings from './settings';
import { WIDTH, HEIGHT } from './settings';
import { Room } from './scenes/demo';

const game = createGame('#app', {
    width: WIDTH,
    height: HEIGHT,
    background: Settings.DARK
});

// Declare Scenes
game({
    Room
});
```

`components/bulb.ts`:

```ts
import { getColor, usePointLight } from 'nacatamalon';

export const Bulb = (x: number, y: number, z: number, hex: string) => usePointLight({
    range: 7,
    intensity: 2.2,
    ambient: 0.05,
    color: getColor(hex),
    transform: {
        x,
        y,
        z
    }
});
```

`components/interior.ts`:

```ts
import { createMesh, useCubeGeometry, usePlaneGeometry } from 'nacatamalon';

export const Interior = () => {
    // Floor and walls cut into many squares: light is worked out at the corners, so a floor of
    // four corners would only ever be as bright as they are.
    const floor = usePlaneGeometry({
        width: 10,
        depth: 10,
        widthSegments: 40,
        depthSegments: 40
    });

    const wall = usePlaneGeometry({
        width: 10,
        depth: 4,
        widthSegments: 40,
        depthSegments: 16
    });

    createMesh({
        geometry: floor,
        tint: '#9a96a8'
    });
    createMesh({
        geometry: wall,
        tint: '#b8b0c4',
        transform: {
            y: 2,
            z: -5,
            rotationX: Math.PI / 2
        }
    });
    createMesh({
        geometry: wall,
        tint: '#b8b0c4',
        transform: {
            x: -5,
            y: 2,
            rotationX: Math.PI / 2,
            rotationY: Math.PI / 2
        }
    });

    const cube = useCubeGeometry();

    for (const [x, z, h] of [[-2.5, -2, 1.2], [0.5, -3, 0.6], [2.5, -1, 1.6], [-1, 1, 0.8]] as const) {
        createMesh({
            geometry: cube,
            tint: '#e0d6c8',
            transform: {
                x,
                y: h / 2,
                z,
                scaleY: h
            }
        });
    }
};
```

`components/strip.ts`:

```ts
import { createMesh, useCubeGeometry, useRectLight } from 'nacatamalon';

export const Strip = (x: number, y: number, z: number) => {
    const light = useRectLight({
        width: 6,
        height: 0.5,
        range: 7,
        intensity: 2.2,
        ambient: 0.05,
        color: '#e8f0ff',
        transform: {
            x,
            y,
            z
        }
    });

    const glow = createMesh({
        geometry: useCubeGeometry(),
        tint: '#ffffff',
        emissive: '#e8f0ff',
        transform: {
            x,
            y: y + 0.03,
            z,
            scaleX: 6,
            scaleY: 0.04,
            scaleZ: 0.5
        }
    });

    return {
        light,
        glow
    };
};
```

`components/window.ts`:

```ts
import { createMesh, useCubeGeometry, useRectLight } from 'nacatamalon';

export const Window = () => {
    const light = useRectLight({
        width: 2.4,
        height: 1.6,
        range: 7,
        intensity: 2.2,
        ambient: 0.05,
        color: '#7fb2ff',
        transform: {
            x: -4.95,
            y: 1.6,
            z: 0,
            rotationX: 0,
            rotationY: -Math.PI / 2
        }
    });

    createMesh({
        geometry: useCubeGeometry(),
        tint: '#ffffff',
        emissive: '#7fb2ff',
        transform: {
            x: -5,
            y: 1.6,
            z: 0,
            scaleX: 0.04,
            scaleY: 1.6,
            scaleZ: 2.4
        }
    });

    return light;
};
```

`scenes/demo.ts`:

```ts
import { createScene, createText, useCamera3d, useKeyboard, useSpawn, useUpdate } from 'nacatamalon';
import * as Settings from '../settings';

import { Strip } from '../components/strip';
import { Window } from '../components/window';
import { Bulb } from '../components/bulb';
import { Interior } from '../components/interior';

export const Room = () => {
    useCamera3d(Settings.CAMERA);

    useSpawn(Interior)();

    const strip = useSpawn(Strip)(0.5, 3.2, -1.5).api;
    const window = useSpawn(Window)().api;

    // The same places with bulbs, to compare. Off until 2 is pressed.
    const bulbs = [useSpawn(Bulb)(0.5, 3.2, -1.5, '#e8f0ff').api, useSpawn(Bulb)(-4.6, 1.6, 0, '#7fb2ff').api];

    const label = createText({
        text: '',
        tint: '#d8d4e8',
        transform: {
            x: 8,
            y: 8
        }
    });

    createText({
        text: '1 PANELS  2 BULBS  LEFT/RIGHT LENGTH',
        tint: '#8a86a0',
        transform: {
            x: 8,
            y: 20
        }
    });

    const keys = useKeyboard();
    let panels = true;

    useUpdate((delta) => {
        if (keys.justPressed('1')) {
            panels = true;
        }

        if (keys.justPressed('2')) {
            panels = false;
        }

        if (keys.isDown('ArrowLeft')) {
            strip.light.width = Math.max(0.2, strip.light.width - delta * 3);
        }

        if (keys.isDown('ArrowRight')) {
            strip.light.width = Math.min(9, strip.light.width + delta * 3);
        }

        strip.glow.transform.scaleX = strip.light.width;
        strip.light.intensity = panels ? 2.2 : 0;
        window.intensity = panels ? 2.2 : 0;

        for (const bulb of bulbs) {
            bulb.intensity = panels ? 0 : 2.2;
        }

        label.text = panels ? 'PANELS' : 'BULBS';
    });

    return createScene();
};
```

`settings.ts`:

```ts
export const WIDTH = 480;
export const HEIGHT = 320;
import type { TCamera3dOptions } from 'nacatamalon';
export const CAMERA = {
    projection: 'perspective',
    fov: 60,
    transform: {
        y: 2.6,
        z: 7,
        rotationX: -0.28
    }
} satisfies TCamera3dOptions;
import { getColor } from 'nacatamalon';

export const DARK = getColor('#1b1a24');
```
