# Pixcen JavaScript Library System

**Version 1.0.0**

Comprehensive utility libraries for Pixcen brush and shader scripts. Consolidates 40+ code duplications across 32+ scripts, reducing script size by 40-60% while improving maintainability.

---

## 📚 Library Overview

| Library | Lines | Auto-Included | Purpose |
|---------|-------|---------------|---------|
| `core.js` | 491 | ✅ Yes | Essential utilities (RNG, math, color, graphics) |
| `noise.js` | 379 | ❌ Optional | Noise algorithms (Perlin, value, cellular) |
| `graphics.js` | 438 | ❌ Optional | Drawing primitives (line, circle, shapes) |

**Total:** 1,308 lines of reusable code

---

## 🚀 Quick Start

### Automatic Inclusion

`core.js` is automatically included in all brush and shader scripts. No action required!

```javascript
// core.js is already loaded - just use it!
const { pixels, mask } = PixcenLib.graphics.createArrays(width, height);
PixcenLib.rng.seed(frame * 12345);
const randomValue = PixcenLib.rng.next();
```

### Optional Libraries

Include additional libraries as needed:

```javascript
// Include noise algorithms
pixcen.include('lib/noise.js');
const noiseValue = PixcenLib.noise.perlin.get(x, y);

// Include drawing primitives
pixcen.include('lib/graphics.js');
PixcenLib.draw.line(pixels, mask, x0, y0, x1, y1, color, width, height);
```

---

## 📖 API Reference

## Core Library (`core.js`)

### PixcenLib.rng - Random Number Generation

Consolidates **9 identical LCG implementations** across scripts.

#### `seed(value)`
Initialize RNG with seed value.
```javascript
PixcenLib.rng.seed(frame * 12345);
```

#### `next()`
Get next random number in range [0, 1).
```javascript
const random = PixcenLib.rng.next(); // 0.0 to 0.999...
```

#### `range(min, max)`
Get random number in specified range.
```javascript
const value = PixcenLib.rng.range(10, 50); // 10.0 to 49.999...
```

#### `int(min, max)`
Get random integer in specified range.
```javascript
const index = PixcenLib.rng.int(0, 16); // 0 to 15
```

**Replaces:**
```javascript
// OLD (10 lines, repeated 9 times):
let rng = frame * 12345;
function lcgRandom() {
    rng = (rng * 1103515245 + 12345) % 2147483648;
    return rng / 2147483648;
}

// NEW (1 line):
PixcenLib.rng.seed(frame * 12345);
```

---

### PixcenLib.math - Mathematical Utilities

#### `lerp(a, b, t)`
Linear interpolation. **Consolidates 4 implementations.**
```javascript
const value = PixcenLib.math.lerp(0, 255, 0.5); // 127.5
```

#### `smoothstep(t)`
Smooth Hermite interpolation (3t² - 2t³). **Consolidates 2 implementations.**
```javascript
const smooth = PixcenLib.math.smoothstep(0.5); // 0.5
```

#### `fade(t)`
Enhanced smoothstep (6t⁵ - 15t⁴ + 10t³). **Consolidates 2 implementations.**
```javascript
const faded = PixcenLib.math.fade(0.5); // 0.5
```

#### `normalize(value, min, max)`
Normalize value to [0, 1] range. **Consolidates 8+ implementations.**
```javascript
const normalized = PixcenLib.math.normalize(50, 0, 100); // 0.5
```

#### `clamp(value, min, max)`
Clamp value to specified range. **Consolidates 6+ implementations.**
```javascript
const clamped = PixcenLib.math.clamp(150, 0, 100); // 100
```

#### `distance(x1, y1, x2, y2, aspectRatio = 1.0)`
Calculate Euclidean distance with optional aspect ratio. **Consolidates 15+ implementations.**
```javascript
const dist = PixcenLib.math.distance(0, 0, 3, 4); // 5.0
const aspectDist = PixcenLib.math.distance(0, 0, 10, 10, 2.0); // 22.36...
```

#### `distanceSquared(x1, y1, x2, y2, aspectRatio = 1.0)`
Calculate squared distance (faster, avoids sqrt).
```javascript
const distSq = PixcenLib.math.distanceSquared(0, 0, 3, 4); // 25
```

#### `toPolar(x, y)`
Convert Cartesian to polar coordinates. **Consolidates 5 implementations.**
```javascript
const { angle, radius } = PixcenLib.math.toPolar(3, 4);
// angle: 0.927... (radians), radius: 5.0
```

#### `toCartesian(angle, radius)`
Convert polar to Cartesian coordinates. **Consolidates 6 implementations.**
```javascript
const { x, y } = PixcenLib.math.toCartesian(Math.PI / 4, 10);
// x: 7.07..., y: 7.07...
```

#### `hash2D(x, y, seed = 0)`
2D hash function for procedural generation. **Consolidates 6+ variations.**
```javascript
const hash = PixcenLib.math.hash2D(10, 20); // 0.0 to 1.0
```

#### `map(value, inMin, inMax, outMin, outMax)`
Map value from one range to another.
```javascript
const mapped = PixcenLib.math.map(50, 0, 100, 0, 255); // 127.5
```

---

### PixcenLib.graphics - Graphics Utilities

#### `createArrays(width, height, defaultValue = 0, defaultMask = false)`
Initialize pixel and mask arrays for brushes. **Consolidates 12 identical implementations (ALL brushes!).**
```javascript
// OLD (12 lines in every brush):
const pixels = [];
const mask = [];
for (let py = 0; py < height; py++) {
    const pixelRow = [];
    const maskRow = [];
    for (let px = 0; px < width; px++) {
        pixelRow.push(0);
        maskRow.push(false);
    }
    pixels.push(pixelRow);
    mask.push(maskRow);
}

// NEW (1 line):
const { pixels, mask } = PixcenLib.graphics.createArrays(width, height);
```
(Typically called with 2 arguments; optional parameters rarely needed)

#### `inBounds(x, y, width, height)`
Check if coordinates are within bounds. **Consolidates 10+ inline checks.**
```javascript
if (PixcenLib.graphics.inBounds(x, y, width, height)) {
    // Safe to access pixels[y][x]
}
```

#### `setPixel(pixels, mask, x, y, color, width, height)`
Safely set pixel with automatic bounds checking.
```javascript
PixcenLib.graphics.setPixel(pixels, mask, 10, 20, 5, width, height);
```

#### `getPixel(pixels, x, y, width, height, defaultValue = 0)`
Get pixel value with bounds checking.
```javascript
const color = PixcenLib.graphics.getPixel(pixels, 10, 20, width, height, 0);
```

---

### PixcenLib.color - Color Utilities

#### `isValid(color)`
Validate C64 color index. **Consolidates 3 explicit validations.**
```javascript
if (PixcenLib.color.isValid(color)) {
    pixels[y][x] = color;
}
```

#### `getRange(index, fallbackIndex = 0)`
Get color range with automatic validation. **Consolidates 9 patterns.**
```javascript
// OLD (5 lines):
const colors = pixcen.colors.getColorRangeColors(index);
if (!colors || colors.length === 0) {
    return { width, height, pixels: [], mask: [] };
}

// NEW (1 line):
const colors = PixcenLib.color.getRange(index);
```

#### `mapToRange(value, min, max, rangeIndex)`
Map value to color from range. **Consolidates 12+ implementations.**
```javascript
// OLD (7 lines):
const normalized = (value - min) / (max - min);
const clamped = Math.max(0, Math.min(1, normalized));
const index = Math.floor(clamped * (colors.length - 1));
const safeIndex = Math.max(0, Math.min(colors.length - 1, index));
const color = colors[safeIndex];

// NEW (1 line):
const color = PixcenLib.color.mapToRange(value, min, max, rangeIndex);
```

#### `gradient(colors, t)`
Map gradient position (0-1) to color from array.
```javascript
const color = PixcenLib.color.gradient(colors, 0.5);
```

#### `blend(color1, color2, t)`
Blend two colors (simple average for C64).
```javascript
const blended = PixcenLib.color.blend(1, 15, 0.5); // 8
```

---

### PixcenLib.canvas - Canvas Utilities

#### `getAspectRatio()`
Get pixel aspect ratio for current graphics mode. **Consolidates 7 IDENTICAL implementations!**
```javascript
// OLD (7 lines, duplicated 7 times):
function getPixelAspectRatio() {
    const mode = pixcen.canvas.getMode();
    const multicolorModes = ["MCBitmap", "MCFLI", "MCSprite", "MCFont", "WUnrestricted"];
    return multicolorModes.includes(mode) ? 2.0 : 1.0;
}
const aspectRatio = getPixelAspectRatio();

// NEW (1 line):
const aspectRatio = PixcenLib.canvas.getAspectRatio();
```

#### `getDimensions()`
Get canvas dimensions.
```javascript
const { width, height } = PixcenLib.canvas.getDimensions();
```

---

### PixcenLib.params - Parameter Utilities

#### `get(brush, paramName, defaultValue)`
Get parameter value with fallback.
```javascript
const speed = PixcenLib.params.get(brush, 'speed', 5);
```

#### `getInt(brush, paramName, defaultValue)`
Get integer parameter value.
```javascript
const count = PixcenLib.params.getInt(brush, 'particleCount', 50);
```

#### `getFloat(brush, paramName, defaultValue)`
Get float parameter value.
```javascript
const scale = PixcenLib.params.getFloat(brush, 'scale', 1.0);
```

---

## Noise Library (`noise.js`)

**Include with:** `pixcen.include('lib/noise.js');`

### PixcenLib.noise.perlin - Perlin Noise

**Consolidates complete 196-line implementation from perlin-noise.js!**

#### `init(seed)`
Initialize Perlin noise with seed.
```javascript
PixcenLib.noise.perlin.init(frame);
```

#### `get(x, y)`
Get 2D Perlin noise value [-1, 1].
```javascript
const noise = PixcenLib.noise.perlin.get(x / 50, y / 50);
```

#### `octave(x, y, octaves = 4, persistence = 0.5, lacunarity = 2.0)`
Get multi-octave Perlin noise [0, 1] (fractal Brownian motion).
```javascript
const noise = PixcenLib.noise.perlin.octave(x / 50, y / 50, 4, 0.5, 2.0);
```

#### `turbulence(x, y, octaves = 4)`
Get turbulent noise [0, 1] (absolute value of noise).
```javascript
const turb = PixcenLib.noise.perlin.turbulence(x / 50, y / 50, 4);
```

#### `ridged(x, y, octaves = 4)`
Get ridged multi-fractal noise [0, 1] (creates sharp ridges).
```javascript
const ridge = PixcenLib.noise.perlin.ridged(x / 50, y / 50, 4);
```

**Complete Example:**
```javascript
// OLD perlin-noise.js (201 lines):
// ... 196 lines of Perlin noise implementation ...

// NEW (using library, ~50 lines total):
pixcen.include('lib/noise.js');
PixcenLib.noise.perlin.init(frame);
const noiseValue = PixcenLib.noise.perlin.octave(worldX, worldY, octaves);
```

---

### PixcenLib.noise.value - Value Noise

#### `get(x, y, seed = 0)`
Get 2D value noise [0, 1] (simpler than Perlin, blockier patterns).
```javascript
const noise = PixcenLib.noise.value.get(x / 20, y / 20);
```

#### `octave(x, y, octaves = 4, seed = 0)`
Get multi-octave value noise [0, 1].
```javascript
const noise = PixcenLib.noise.value.octave(x / 20, y / 20, 4);
```

---

### PixcenLib.noise.cellular - Cellular/Worley Noise

#### `get(x, y, seed = 0, scale = 1.0)`
Get cellular noise [0, 1] (distance to nearest feature point).

The `scale` parameter is a spatial scale (a multiplier on input coordinates, or inverse frequency) that controls cell size. Smaller `scale` values produce larger cells (lower frequency), while larger `scale` values produce smaller cells (higher frequency). The default value of `1.0` is a neutral scale. The example below uses `0.1` to illustrate larger cells.

```javascript
const cell = PixcenLib.noise.cellular.get(x, y, 0, 0.1); // scale controls cell size; smaller values = larger cells
```

#### `getF2(x, y, seed = 0, scale = 1.0)`
Get distances to first and second nearest points (for cell borders).

The `scale` parameter is a spatial scale (a multiplier on input coordinates, or inverse frequency) that controls cell size. Smaller `scale` values produce larger cells (lower frequency), while larger `scale` values produce smaller cells (higher frequency). The default value of `1.0` is a neutral scale. The example below uses `0.1` to illustrate larger cells.

```javascript
const { first, second } = PixcenLib.noise.cellular.getF2(x, y, 0, 0.1); // scale controls cell size; smaller values = larger cells
const border = second - first; // Cell border width
```

---

## Graphics Library (`graphics.js`)

**Include with:** `pixcen.include('lib/graphics.js');`

### PixcenLib.draw - Drawing Primitives

#### `line(pixels, mask, x0, y0, x1, y1, color, width, height, thickness = 1)`
Draw line using Bresenham's algorithm. **Consolidates 2 complete implementations (~30 lines each).**
```javascript
// OLD lightning.js (40 lines of Bresenham):
function drawThickLine(x0, y0, x1, y1, intensity) {
    // ... 40 lines of line drawing code ...
}

// NEW (1 line):
PixcenLib.draw.line(pixels, mask, x0, y0, x1, y1, color, width, height, thickness);
```

#### `lineAA(pixels, mask, x0, y0, x1, y1, color, width, height, thickness = 1)`
Draw line with intensity-based anti-aliasing.
```javascript
PixcenLib.draw.lineAA(pixels, mask, x0, y0, x1, y1, 15, width, height, 2);
```

#### `circle(pixels, mask, cx, cy, radius, color, width, height)`
Draw circle outline using Midpoint algorithm.
```javascript
PixcenLib.draw.circle(pixels, mask, centerX, centerY, 20, color, width, height);
```

#### `circleFilled(pixels, mask, cx, cy, radius, color, width, height)`
Draw filled circle.
```javascript
PixcenLib.draw.circleFilled(pixels, mask, centerX, centerY, 20, color, width, height);
```

#### `rect(pixels, mask, x, y, w, h, color, width, height)`
Draw rectangle outline.
```javascript
PixcenLib.draw.rect(pixels, mask, 10, 10, 50, 30, color, width, height);
```

#### `rectFilled(pixels, mask, x, y, w, h, color, width, height)`
Draw filled rectangle.
```javascript
PixcenLib.draw.rectFilled(pixels, mask, 10, 10, 50, 30, color, width, height);
```

#### `ellipse(pixels, mask, cx, cy, rx, ry, color, width, height)`
Draw ellipse outline.
```javascript
PixcenLib.draw.ellipse(pixels, mask, centerX, centerY, 30, 20, color, width, height);
```

#### `polygon(pixels, mask, points, color, width, height, closed = true)`
Draw polygon from array of points.
```javascript
const points = [{x: 10, y: 10}, {x: 50, y: 20}, {x: 30, y: 60}];
PixcenLib.draw.polygon(pixels, mask, points, color, width, height);
```

#### `floodFill(pixels, mask, x, y, newColor, width, height)`
Flood fill starting from point.
```javascript
PixcenLib.draw.floodFill(pixels, mask, startX, startY, newColor, width, height);
```

---

## 💡 Usage Examples

### Example 1: Simple Brush with Core Library

```javascript
const brush = {
    name: "Radial Gradient",
    params: {
        colorRange: { type: "colorRange", default: 0, label: "Colors" }
    },

    generate: function(width, height, frame, x, y, color) {
        // Get aspect ratio (1 line instead of 7)
        const aspectRatio = PixcenLib.canvas.getAspectRatio();

        // Initialize arrays (1 line instead of 12)
        const { pixels, mask } = PixcenLib.graphics.createArrays(width, height);

        // Get color range (1 line instead of 5)
        const colors = PixcenLib.color.getRange(
            PixcenLib.params.getInt(brush, 'colorRange', 0)
        );

        const centerX = Math.floor(width / 2);
        const centerY = Math.floor(height / 2);
        const maxRadius = Math.min(centerX, centerY);

        for (let py = 0; py < height; py++) {
            for (let px = 0; px < width; px++) {
                // Distance with aspect correction (1 line instead of 4)
                const dist = PixcenLib.math.distance(px, py, centerX, centerY, aspectRatio);

                // Normalize and map to color (1 line instead of 7)
                const color = PixcenLib.color.mapToRange(dist, 0, maxRadius, 0);

                pixels[py][px] = color;
                mask[py][px] = true;
            }
        }

        return { width, height, pixels, mask };
    }
};

pixcen.registerBrush(brush);
```

**Code Reduction: 65 lines → 35 lines (46% reduction)**

---

### Example 2: Perlin Noise Terrain

```javascript
pixcen.include('lib/noise.js'); // Load noise library

const brush = {
    name: "Terrain",
    params: {
        scale: { type: "number", default: 20, min: 5, max: 50 },
        colorRange: { type: "colorRange", default: 0 }
    },

    generate: function(width, height, frame, x, y, color) {
        const { pixels, mask } = PixcenLib.graphics.createArrays(width, height);
        const colors = PixcenLib.color.getRange(0);
        const scale = PixcenLib.params.get(brush, 'scale', 20);

        // Initialize noise (1 line instead of 40)
        PixcenLib.noise.perlin.init(frame);

        for (let py = 0; py < height; py++) {
            for (let px = 0; px < width; px++) {
                const worldX = (x + px - width / 2) / scale;
                const worldY = (y + py - height / 2) / scale;

                // Get multi-octave noise (1 line instead of 20)
                const noiseValue = PixcenLib.noise.perlin.octave(worldX, worldY, 4);

                // Map to color (1 line)
                const color = PixcenLib.color.gradient(colors, noiseValue);

                if (color > 0) {
                    pixels[py][px] = color;
                    mask[py][px] = true;
                }
            }
        }

        return { width, height, pixels, mask };
    }
};

pixcen.registerBrush(brush);
```

**Code Reduction: 201 lines → 50 lines (75% reduction!)**

---

### Example 3: Drawing Shapes

```javascript
pixcen.include('lib/graphics.js'); // Load graphics library

const brush = {
    name: "Geometric Shapes",

    generate: function(width, height, frame, x, y, color) {
        const { pixels, mask } = PixcenLib.graphics.createArrays(width, height);
        const centerX = Math.floor(width / 2);
        const centerY = Math.floor(height / 2);

        // Draw circle (1 line instead of 30)
        PixcenLib.draw.circle(pixels, mask, centerX, centerY, 20, 1, width, height);

        // Draw line (1 line instead of 40)
        PixcenLib.draw.line(pixels, mask, 0, 0, width - 1, height - 1, 14, width, height, 2);

        // Draw rectangle
        PixcenLib.draw.rect(pixels, mask, 10, 10, 30, 20, 2, width, height);

        // Draw polygon
        const star = [
            {x: centerX, y: 5},
            {x: centerX + 10, y: centerY},
            {x: width - 5, y: centerY},
            {x: centerX + 5, y: centerY + 10},
            {x: centerX, y: height - 5},
            {x: centerX - 5, y: centerY + 10},
            {x: 5, y: centerY},
            {x: centerX - 10, y: centerY}
        ];
        PixcenLib.draw.polygon(pixels, mask, star, 10, width, height);

        return { width, height, pixels, mask };
    }
};

pixcen.registerBrush(brush);
```

**Code Reduction: 120 lines → 40 lines (67% reduction)**

---

## 📊 Code Reduction Statistics

| Pattern | Before | After | Reduction |
|---------|--------|-------|-----------|
| Array initialization | 12 lines | 1 line | **92%** |
| Aspect ratio | 7 lines | 1 line | **86%** |
| RNG setup | 10 lines | 1 line | **90%** |
| Color range access | 5 lines | 1 line | **80%** |
| Distance calculation | 4 lines | 1 line | **75%** |
| Color mapping | 7 lines | 1 line | **86%** |
| Perlin noise | 196 lines | 1 include + 3 lines | **98%** |
| Bresenham line | 40 lines | 1 line | **98%** |

**Average reduction across all patterns: 85%**

---

## 🎯 Best Practices

1. **Always use PixcenLib.graphics.createArrays()** instead of manual initialization
2. **Use PixcenLib.canvas.getAspectRatio()** for multicolor mode correction
3. **Seed RNG with frame** for consistent animated patterns: `PixcenLib.rng.seed(frame * 12345)`
4. **Use mapToRange()** instead of manual gradient mapping
5. **Include optional libraries** only when needed to minimize load time
6. **Validate colors** with `PixcenLib.color.isValid()` before assignment
7. **Use bounds checking** with `PixcenLib.graphics.inBounds()` to prevent crashes

---

## 🔧 Future Additions

Potential libraries for future development:

- **math.js** - 3D transformations, matrix operations, Bezier curves
- **procedural.js** - Maze generation, L-systems, cellular automata
- **color.js** - Color space conversions, palette generation
- **filters.js** - Convolution kernels, image processing

---

## 📝 Version History

- **1.0.0** (Initial Release)
  - Core library with RNG, math, color, graphics, canvas, params utilities
  - Noise library with Perlin, value, and cellular noise
  - Graphics library with Bresenham line, circles, ellipses, polygons, flood fill
  - Consolidates 40+ code duplications across 32+ scripts
  - Achieves 40-60% code reduction in refactored scripts

---

## 🤝 Contributing

When adding new utilities to the library:

1. Check for existing duplications across scripts
2. Create generalized, reusable function
3. Add comprehensive JSDoc comments
4. Update this README with examples
5. Test with multiple scripts to verify compatibility

---

## 📄 License

Part of the Pixcen project. See main project license.
