Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 5 additions & 13 deletions src/common/model/light-sources/ArcLightSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,19 @@
* π/2 = down (positive y-axis), consistent with Math.atan2(dy, dx).
*/

import { BaseElement } from "../optics/BaseElement.js";
import type { Point } from "../optics/Geometry.js";
import { normalize, point } from "../optics/Geometry.js";
import {
BRIGHTNESS_CONTINUOUS_THRESHOLD,
BRIGHTNESS_NORMALIZE,
POLARIZATION_SPLIT,
RAY_DENSITY_SCALE,
} from "../optics/OpticsConstants.js";
import type { ElementCategory, SimulationRay, ViewMode } from "../optics/OpticsTypes.js";
import type { SimulationRay, ViewMode } from "../optics/OpticsTypes.js";
import { BaseLightSource } from "./BaseLightSource.js";
import { GREEN_WAVELENGTH } from "./LightSourceConstants.js";

export class ArcLightSource extends BaseElement {
export class ArcLightSource extends BaseLightSource {
public readonly type = "ArcSource";
public readonly category: ElementCategory = "lightSource";

public position: Point;

Expand All @@ -38,22 +36,17 @@ export class ArcLightSource extends BaseElement {
*/
public emissionAngle: number;

public brightness: number;
public wavelength: number;

public constructor(
position: Point,
direction = 0,
emissionAngle = Math.PI / 6, // 30 degrees
brightness = 0.5,
wavelength = GREEN_WAVELENGTH,
) {
super();
super(brightness, wavelength);
this.position = position;
this.direction = direction;
this.emissionAngle = emissionAngle;
this.brightness = brightness;
this.wavelength = wavelength;
}

public override emitRays(rayDensity: number, _mode: ViewMode): SimulationRay[] {
Expand All @@ -69,8 +62,7 @@ export class ArcLightSource extends BaseElement {
? maxRays
: Math.max(1, Math.round((this.brightness / BRIGHTNESS_CONTINUOUS_THRESHOLD) * maxRays));
const angularStep = beta / numRays;
const bBase = isContinuous ? this.brightness : BRIGHTNESS_CONTINUOUS_THRESHOLD;
const b = bBase / BRIGHTNESS_NORMALIZE;
const b = this.normalizeBrightness();

const halfAngle = beta / 2;
const startAngle = this.direction - halfAngle;
Expand Down
45 changes: 45 additions & 0 deletions src/common/model/light-sources/BaseLightSource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* BaseLightSource.ts
*
* Abstract base class for all single-wavelength light source elements.
* Centralises the three properties shared by every concrete light source
* (brightness, wavelength, category) and provides a protected
* brightness-normalization helper used during ray emission.
*/

import { BaseElement } from "../optics/BaseElement.js";
import {
BRIGHTNESS_CONTINUOUS_THRESHOLD,
BRIGHTNESS_NORMALIZE,
} from "../optics/OpticsConstants.js";
import type { ElementCategory } from "../optics/OpticsTypes.js";
import { GREEN_WAVELENGTH } from "./LightSourceConstants.js";

export abstract class BaseLightSource extends BaseElement {
public readonly category: ElementCategory = "lightSource";

public brightness: number;
public wavelength: number;

protected constructor(brightness: number, wavelength: number = GREEN_WAVELENGTH) {
super();
this.brightness = brightness;
this.wavelength = wavelength;
}

/**
* Returns the per-ray brightness factor (before polarization split).
*
* Two emission modes:
* - Discrete (brightness < BRIGHTNESS_CONTINUOUS_THRESHOLD): every ray
* carries a fixed intensity of BRIGHTNESS_CONTINUOUS_THRESHOLD so that
* visual appearance is consistent regardless of how many rays are drawn.
* - Continuous (brightness >= BRIGHTNESS_CONTINUOUS_THRESHOLD): intensity
* scales linearly with brightness up to BRIGHTNESS_NORMALIZE.
*/
protected normalizeBrightness(): number {
const isContinuous = this.brightness >= BRIGHTNESS_CONTINUOUS_THRESHOLD;
const bBase = isContinuous ? this.brightness : BRIGHTNESS_CONTINUOUS_THRESHOLD;
return bBase / BRIGHTNESS_NORMALIZE;
}
}
17 changes: 5 additions & 12 deletions src/common/model/light-sources/BeamSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,38 +6,32 @@
* evenly distributed along the segment.
*/

import { BaseElement } from "../optics/BaseElement.js";
import type { Point } from "../optics/Geometry.js";
import { distance, normalize, point, subtract } from "../optics/Geometry.js";
import {
BEAM_RAY_DENSITY_SCALE,
BRIGHTNESS_CONTINUOUS_THRESHOLD,
BRIGHTNESS_NORMALIZE,
POLARIZATION_SPLIT,
RAY_DENSITY_SCALE,
} from "../optics/OpticsConstants.js";
import type { ElementCategory, SimulationRay, ViewMode } from "../optics/OpticsTypes.js";
import type { SimulationRay, ViewMode } from "../optics/OpticsTypes.js";
import { BaseLightSource } from "./BaseLightSource.js";
import { GREEN_WAVELENGTH } from "./LightSourceConstants.js";

export class BeamSource extends BaseElement {
export class BeamSource extends BaseLightSource {
public readonly type = "Beam";
public readonly category: ElementCategory = "lightSource";

/** First endpoint of the segment perpendicular to beam direction. */
public p1: Point;
/** Second endpoint of the segment perpendicular to beam direction. */
public p2: Point;
public brightness: number;
public wavelength: number;
/** Divergence half-angle in degrees (0 = perfectly parallel). */
public emisAngle: number;

public constructor(p1: Point, p2: Point, brightness = 0.5, wavelength = GREEN_WAVELENGTH, emisAngle = 0) {
super();
super(brightness, wavelength);
this.p1 = p1;
this.p2 = p2;
this.brightness = brightness;
this.wavelength = wavelength;
this.emisAngle = emisAngle;
}

Expand All @@ -57,8 +51,7 @@ export class BeamSource extends BaseElement {
const angularStep = (Math.PI * 2) / Math.max(1, Math.floor(rayDensity * RAY_DENSITY_SCALE));
const numAngledRays = 1 + Math.max(0, Math.ceil(halfAngle / angularStep) - 1) * 2;
const brightnessFactor = 1.0 / numAngledRays;
const bBase = isContinuous ? this.brightness : BRIGHTNESS_CONTINUOUS_THRESHOLD;
const b = Math.min((bBase / BRIGHTNESS_NORMALIZE) * brightnessFactor, 1);
const b = Math.min(this.normalizeBrightness() * brightnessFactor, 1);

const rays: SimulationRay[] = [];

Expand Down
17 changes: 5 additions & 12 deletions src/common/model/light-sources/PointSourceElement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,25 @@
* directions. The angular spacing of rays depends on rayDensity.
*/

import { BaseElement } from "../optics/BaseElement.js";
import type { Point } from "../optics/Geometry.js";
import { point } from "../optics/Geometry.js";
import {
BRIGHTNESS_CONTINUOUS_THRESHOLD,
BRIGHTNESS_NORMALIZE,
POLARIZATION_SPLIT,
RAY_DENSITY_SCALE,
} from "../optics/OpticsConstants.js";
import type { ElementCategory, SimulationRay, ViewMode } from "../optics/OpticsTypes.js";
import type { SimulationRay, ViewMode } from "../optics/OpticsTypes.js";
import { BaseLightSource } from "./BaseLightSource.js";
import { GREEN_WAVELENGTH } from "./LightSourceConstants.js";

export class PointSourceElement extends BaseElement {
export class PointSourceElement extends BaseLightSource {
public readonly type = "PointSource";
public readonly category: ElementCategory = "lightSource";

public position: Point;
public brightness: number;
public wavelength: number;

public constructor(position: Point, brightness = 0.5, wavelength = GREEN_WAVELENGTH) {
super();
super(brightness, wavelength);
this.position = position;
this.brightness = brightness;
this.wavelength = wavelength;
}

public override emitRays(rayDensity: number, mode: ViewMode): SimulationRay[] {
Expand All @@ -42,8 +36,7 @@ export class PointSourceElement extends BaseElement {
const startAngle = mode === "observer" ? -angularStep * 2 + 1e-6 : 0;
// In discrete mode every ray has fixed intensity (BRIGHTNESS_CONTINUOUS_THRESHOLD).
// In continuous mode intensity scales with brightness up to BRIGHTNESS_NORMALIZE.
const bBase = isContinuous ? this.brightness : BRIGHTNESS_CONTINUOUS_THRESHOLD;
const b = bBase / BRIGHTNESS_NORMALIZE;
const b = this.normalizeBrightness();

const rays: SimulationRay[] = [];
let first = true;
Expand Down
13 changes: 4 additions & 9 deletions src/common/model/light-sources/SingleRaySource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,23 @@
* a second point that indicates direction. Emits exactly one ray.
*/

import { BaseElement } from "../optics/BaseElement.js";
import type { Point } from "../optics/Geometry.js";
import { normalize, point, subtract } from "../optics/Geometry.js";
import { POLARIZATION_SPLIT } from "../optics/OpticsConstants.js";
import type { ElementCategory, SimulationRay, ViewMode } from "../optics/OpticsTypes.js";
import type { SimulationRay, ViewMode } from "../optics/OpticsTypes.js";
import { BaseLightSource } from "./BaseLightSource.js";
import { GREEN_WAVELENGTH } from "./LightSourceConstants.js";

export class SingleRaySource extends BaseElement {
export class SingleRaySource extends BaseLightSource {
public readonly type = "SingleRay";
public readonly category: ElementCategory = "lightSource";

public p1: Point;
public p2: Point;
public brightness: number;
public wavelength: number;

public constructor(p1: Point, p2: Point, brightness = 1, wavelength = GREEN_WAVELENGTH) {
super();
super(brightness, wavelength);
this.p1 = p1;
this.p2 = p2;
this.brightness = brightness;
this.wavelength = wavelength;
}

public override emitRays(_rayDensity: number, _mode: ViewMode): SimulationRay[] {
Expand Down
Loading