# Scales

Data-to-pixel conversion and axis rendering for different data types. Scales handle the transformation between data values and pixel coordinates on the chart canvas.

## Capabilities

### Linear Scale

Handles linear numeric data with equal intervals.

```javascript { .api }
class LinearScale extends Scale {
  static id: 'linear';
  static defaults: object;
  
  getPixelForValue(value: number): number;
  getValueForPixel(pixel: number): number;
  getLabelForValue(value: number): string;
  getDecimalForPixel(pixel: number): number;
}

interface LinearScaleOptions {
  type: 'linear';
  position?: 'left' | 'right' | 'top' | 'bottom' | 'center' | { [scaleId: string]: number };
  axis?: 'x' | 'y';
  offset?: boolean;
  beginAtZero?: boolean;
  bounds?: 'data' | 'ticks';
  clip?: boolean;
  grace?: number | string;
  min?: number;
  max?: number;
  suggestedMin?: number;
  suggestedMax?: number;
  ticks?: TickOptions;
  title?: TitleOptions;
  grid?: GridLineOptions;
  border?: BorderOptions;
}
```

**Usage Example:**

```javascript
const config = {
  type: 'line',
  data: { /* data */ },
  options: {
    scales: {
      y: {
        type: 'linear',
        beginAtZero: true,
        min: 0,
        max: 100,
        ticks: {
          stepSize: 10
        }
      }
    }
  }
};
```

### Category Scale

Handles categorical/ordinal data with discrete labels.

```javascript { .api }
class CategoryScale extends Scale {
  static id: 'category';
  static defaults: object;
  
  getPixelForValue(value: string | number): number;
  getValueForPixel(pixel: number): number;
  getLabelForValue(value: string | number): string;
  getLabels(): string[];
}

interface CategoryScaleOptions {
  type: 'category';
  position?: 'left' | 'right' | 'top' | 'bottom' | 'center' | { [scaleId: string]: number };
  axis?: 'x' | 'y';
  offset?: boolean;
  labels?: string[];
  min?: string | number;
  max?: string | number;
  ticks?: TickOptions;
  title?: TitleOptions;
  grid?: GridLineOptions;
  border?: BorderOptions;
}
```

**Usage Example:**

```javascript
const config = {
  type: 'bar',
  data: {
    labels: ['Red', 'Blue', 'Yellow', 'Green'],
    datasets: [{ /* dataset */ }]
  },
  options: {
    scales: {
      x: {
        type: 'category',
        labels: ['Custom Red', 'Custom Blue', 'Custom Yellow', 'Custom Green']
      }
    }
  }
};
```

### Logarithmic Scale

Handles logarithmic numeric data with exponential intervals.

```javascript { .api }
class LogarithmicScale extends Scale {
  static id: 'logarithmic';
  static defaults: object;
  
  getPixelForValue(value: number): number;
  getValueForPixel(pixel: number): number;
  getLabelForValue(value: number): string;
}

interface LogarithmicScaleOptions {
  type: 'logarithmic';
  position?: 'left' | 'right' | 'top' | 'bottom' | 'center' | { [scaleId: string]: number };
  axis?: 'x' | 'y';
  min?: number;
  max?: number;
  suggestedMin?: number;
  suggestedMax?: number;
  ticks?: TickOptions;
  title?: TitleOptions;
  grid?: GridLineOptions;
  border?: BorderOptions;
}
```

### Time Scale

Handles time-based data with temporal intervals.

```javascript { .api }
class TimeScale extends Scale {
  static id: 'time';
  static defaults: object;
  
  getPixelForValue(value: string | number | Date): number;
  getValueForPixel(pixel: number): number;
  getLabelForValue(value: string | number | Date): string;
}

interface TimeScaleOptions {
  type: 'time';
  position?: 'left' | 'right' | 'top' | 'bottom' | 'center' | { [scaleId: string]: number };
  axis?: 'x' | 'y';
  adapters?: DateAdapter;
  bounds?: 'data' | 'ticks';
  offset?: boolean;
  min?: string | number | Date;
  max?: string | number | Date;
  suggestedMin?: string | number | Date;
  suggestedMax?: string | number | Date;
  time?: TimeOptions;
  ticks?: TickOptions;
  title?: TitleOptions;
  grid?: GridLineOptions;
  border?: BorderOptions;
}

interface TimeOptions {
  displayFormats?: {
    millisecond?: string;
    second?: string;
    minute?: string;
    hour?: string;
    day?: string;
    week?: string;
    month?: string;
    quarter?: string;
    year?: string;
  };
  isoWeekday?: boolean | number;
  parser?: string | ((value: unknown) => Date);
  round?: TimeUnit;
  tooltipFormat?: string;
  unit?: TimeUnit;
  stepSize?: number;
  minUnit?: TimeUnit;
}

type TimeUnit = 'millisecond' | 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year';

interface DateAdapter {
  formats(): { [key: string]: string };
  parse(value: unknown, format?: string): Date | null;
  format(time: Date, format: string): string;
  add(time: Date, amount: number, unit: TimeUnit): Date;
  diff(max: Date, min: Date, unit: TimeUnit): number;
  startOf(time: Date, unit: TimeUnit, weekday?: number): Date;
  endOf(time: Date, unit: TimeUnit): Date;
}
```

**Usage Example:**

```javascript
const config = {
  type: 'line',
  data: {
    datasets: [{
      label: 'Time Series',
      data: [
        { x: '2023-01-01', y: 10 },
        { x: '2023-02-01', y: 20 },
        { x: '2023-03-01', y: 15 }
      ]
    }]
  },
  options: {
    scales: {
      x: {
        type: 'time',
        time: {
          unit: 'month',
          displayFormats: {
            month: 'MMM YYYY'
          }
        }
      }
    }
  }
};
```

### Time Series Scale

Optimized time scale for large time series datasets.

```javascript { .api }
class TimeSeriesScale extends TimeScale {
  static id: 'timeseries';
  static defaults: object;
}

interface TimeSeriesScaleOptions extends TimeScaleOptions {
  type: 'timeseries';
  bounds: 'data'; // Always 'data' for timeseries
  offset: false;   // Always false for timeseries
}
```

### Radial Linear Scale

Handles radial numeric data for radar charts.

```javascript { .api }
class RadialLinearScale extends Scale {
  static id: 'radialLinear';
  static defaults: object;
  
  getPixelForValue(value: number, index?: number): number;
  getValueForPixel(pixel: number): number;
  getLabelForValue(value: number): string;
  getIndexAngle(index: number): number;
  getDistanceFromCenterForValue(value: number): number;
  getValueForDistanceFromCenter(distance: number): number;
}

interface RadialLinearScaleOptions {
  type: 'radialLinear';
  animate?: boolean;
  angleLines?: AngleLineOptions;
  beginAtZero?: boolean;
  grid?: GridLineOptions;
  min?: number;
  max?: number;
  pointLabels?: PointLabelOptions;
  startAngle?: number;
  ticks?: TickOptions;
  title?: TitleOptions;
}

interface AngleLineOptions {
  display?: boolean;
  color?: Color;
  lineWidth?: number;
  borderDash?: number[];
  borderDashOffset?: number;
}

interface PointLabelOptions {
  display?: boolean;
  callback?: (label: string, index: number) => string;
  color?: Color;
  font?: FontSpec;
  padding?: number;
  centerPointLabels?: boolean;
}
```

### Base Scale Interface

Common interface implemented by all scales.

```javascript { .api }
interface Scale {
  readonly id: string;
  readonly type: string;
  readonly ctx: CanvasRenderingContext2D;
  readonly chart: Chart;
  
  // Core conversion methods
  getPixelForValue(value: unknown, index?: number): number;
  getValueForPixel(pixel: number): unknown;
  getPixelForTick(index: number): number;
  getPixelForDecimal(decimal: number): number;
  getDecimalForPixel(pixel: number): number;
  getBaseValue(): number;
  getBasePixel(): number;
  
  // Label methods
  getLabelForValue(value: unknown): string;
  
  // Lifecycle methods
  beforeLayout(): void;
  afterLayout(): void;
  beforeUpdate(mode: UpdateMode): void;
  update(width: number, height: number, margins: ChartArea): void;
  afterUpdate(mode: UpdateMode): void;
  
  // Drawing methods
  beforeDraw(): void;
  draw(chartArea: ChartArea): void;
  afterDraw(): void;
  
  // Utility methods
  isHorizontal(): boolean;
  buildTicks(): Tick[];
  configure(): void;
  fit(): void;
}
```

## Scale Registration

Scales must be registered before use in charts.

```javascript { .api }
import {
  Chart,
  LinearScale,
  CategoryScale,
  TimeScale,
  LogarithmicScale,
  RadialLinearScale,
  TimeSeriesScale
} from 'chart.js';

// Register scales
Chart.register(
  LinearScale,
  CategoryScale,
  TimeScale,
  LogarithmicScale,
  RadialLinearScale,
  TimeSeriesScale
);
```

## Types

```javascript { .api }
type ScaleType = 'linear' | 'logarithmic' | 'category' | 'time' | 'timeseries' | 'radialLinear';

interface TickOptions {
  display?: boolean;
  align?: 'start' | 'center' | 'end' | 'inner';
  callback?: (tickValue: number | string, index: number, ticks: Tick[]) => string | string[] | number | number[];
  color?: Color | Color[];
  count?: number;
  crossAlign?: 'near' | 'center' | 'far';
  font?: FontSpec | FontSpec[];
  includeBounds?: boolean;
  labelOffset?: number;
  maxRotation?: number;
  maxTicksLimit?: number;
  minRotation?: number;
  mirror?: boolean;
  padding?: number;
  precision?: number;
  sampleSize?: number;
  showLabelBackdrop?: boolean;
  source?: 'auto' | 'data' | 'labels';
  stepSize?: number;
  textStrokeColor?: Color | Color[];
  textStrokeWidth?: number | number[];
  z?: number;
}

interface TitleOptions {
  display?: boolean;
  align?: 'start' | 'center' | 'end';
  color?: Color;
  font?: FontSpec;
  padding?: Padding;
  text?: string | string[];
}

interface GridLineOptions {
  display?: boolean;
  circular?: boolean;
  color?: Color | Color[];
  lineWidth?: number | number[];
  drawBorder?: boolean;
  drawOnChartArea?: boolean;
  drawTicks?: boolean;
  tickBorderDash?: number[];
  tickBorderDashOffset?: number;
  tickColor?: Color | Color[];
  tickLength?: number;
  tickWidth?: number;
  offset?: boolean;
  z?: number;
}

interface BorderOptions {
  display?: boolean;
  color?: Color;
  width?: number;
  dash?: number[];
  dashOffset?: number;
  z?: number;
}

interface Tick {
  value: number;
  label?: string;
  major?: boolean;
}

interface ChartArea {
  left: number;
  top: number;
  right: number;
  bottom: number;
  width: number;
  height: number;
}

interface FontSpec {
  family?: string;
  size?: number;
  style?: 'normal' | 'italic' | 'oblique';
  weight?: string | number;
  lineHeight?: number | string;
}

interface Padding {
  top?: number;
  right?: number;
  bottom?: number;
  left?: number;
}
```