mirror of
				https://github.com/bspeice/speice.io
				synced 2025-11-03 18:10:32 -05:00 
			
		
		
		
	Refactor, fix julia bug, implement binary/linear/logarithmic membership
This commit is contained in:
		@ -31,6 +31,40 @@ export const Canvas: React.FC<{ f: renderFn }> = ({ f }) => {
 | 
				
			|||||||
  return <canvas ref={canvasRef} width={400} height={400} />;
 | 
					  return <canvas ref={canvasRef} width={400} height={400} />;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function randomBiUnit() {
 | 
				
			||||||
 | 
					  // Math.random() produces a number in the range [0, 1),
 | 
				
			||||||
 | 
					  // scale to (-1, 1)
 | 
				
			||||||
 | 
					  return Math.random() * 2 - 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function randomInteger(min: number, max: number) {
 | 
					export function randomInteger(min: number, max: number) {
 | 
				
			||||||
  return Math.floor(Math.random() * (max - min)) + min;
 | 
					  return Math.floor(Math.random() * (max - min)) + min;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function weightedChoice<T>(choices: [number, T][]) {
 | 
				
			||||||
 | 
					  const weightSum = choices.reduce(
 | 
				
			||||||
 | 
					    (current, [weight, _t]) => current + weight,
 | 
				
			||||||
 | 
					    0
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					  var choice = Math.random() * weightSum;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (var i = 0; i < choices.length; i++) {
 | 
				
			||||||
 | 
					    const [weight, t] = choices[i];
 | 
				
			||||||
 | 
					    if (choice < weight) {
 | 
				
			||||||
 | 
					      return t;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    choice -= weight;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  throw "unreachable";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function imageIndex(x: number, y: number, width: number) {
 | 
				
			||||||
 | 
					  // Taken from: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Pixel_manipulation_with_canvas
 | 
				
			||||||
 | 
					  return y * (width * 4) + x * 4;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function histIndex(x: number, y: number, width: number) {
 | 
				
			||||||
 | 
					  return y * width + x;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,4 @@
 | 
				
			|||||||
import { randomInteger, renderFn } from "./0-utility";
 | 
					import { randomBiUnit, randomInteger, renderFn, imageIndex } from "./0-utility";
 | 
				
			||||||
 | 
					 | 
				
			||||||
const ITER = 100_000;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
function plot(x: number, y: number, image: ImageData) {
 | 
					function plot(x: number, y: number, image: ImageData) {
 | 
				
			||||||
  // A trivial `plot` implementation would take the range [-1, 1],
 | 
					  // A trivial `plot` implementation would take the range [-1, 1],
 | 
				
			||||||
@ -19,10 +17,8 @@ function plot(x: number, y: number, image: ImageData) {
 | 
				
			|||||||
  var pixelX = Math.floor((-x + 1) * image.width);
 | 
					  var pixelX = Math.floor((-x + 1) * image.width);
 | 
				
			||||||
  var pixelY = Math.floor((-y + 1) * image.height);
 | 
					  var pixelY = Math.floor((-y + 1) * image.height);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Now translate the (x, y) pixel coordinates to a buffer index
 | 
					  // Set the pixel black:
 | 
				
			||||||
  // and paint it black:
 | 
					  const index = imageIndex(pixelX, pixelY, image.width);
 | 
				
			||||||
  const index = pixelY * (image.width * 4) + pixelX * 4;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  image.data[index + 0] = 0;
 | 
					  image.data[index + 0] = 0;
 | 
				
			||||||
  image.data[index + 1] = 0;
 | 
					  image.data[index + 1] = 0;
 | 
				
			||||||
  image.data[index + 2] = 0;
 | 
					  image.data[index + 2] = 0;
 | 
				
			||||||
@ -44,13 +40,13 @@ export const gasket: renderFn = (image) => {
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
  ];
 | 
					  ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let x = Math.random() * 2 - 1;
 | 
					  let x = randomBiUnit();
 | 
				
			||||||
  let y = Math.random() * 2 - 1;
 | 
					  let y = randomBiUnit();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Heuristic for iteration count
 | 
					  // Plot with quality 1
 | 
				
			||||||
  const iter = image.height * image.width;
 | 
					  const iterations = image.height * image.width;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  for (var i = 0; i < iter; i++) {
 | 
					  for (var i = 0; i < iterations; i++) {
 | 
				
			||||||
    const Fi = randomInteger(0, F.length);
 | 
					    const Fi = randomInteger(0, F.length);
 | 
				
			||||||
    [x, y] = F[Fi](x, y);
 | 
					    [x, y] = F[Fi](x, y);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,3 +1,5 @@
 | 
				
			|||||||
 | 
					import { randomBiUnit, weightedChoice } from "./0-utility";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type Variation = (
 | 
					export type Variation = (
 | 
				
			||||||
  x: number,
 | 
					  x: number,
 | 
				
			||||||
  y: number,
 | 
					  y: number,
 | 
				
			||||||
@ -17,7 +19,7 @@ function r(x: number, y: number) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function theta(x: number, y: number) {
 | 
					function theta(x: number, y: number) {
 | 
				
			||||||
  return Math.atan2(y, x);
 | 
					  return Math.atan2(x, y);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function omega(): number {
 | 
					function omega(): number {
 | 
				
			||||||
@ -28,10 +30,8 @@ export const linear: Variation = (x, y) => [x, y];
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export const julia: Variation = (x, y) => {
 | 
					export const julia: Variation = (x, y) => {
 | 
				
			||||||
  const sqrtR = Math.sqrt(r(x, y));
 | 
					  const sqrtR = Math.sqrt(r(x, y));
 | 
				
			||||||
  return [
 | 
					  const thetaVal = theta(x, y) / 2 + omega();
 | 
				
			||||||
    sqrtR * Math.cos(theta(x, y) / 2 + omega()),
 | 
					  return [sqrtR * Math.cos(thetaVal), sqrtR * Math.sin(thetaVal)];
 | 
				
			||||||
    sqrtR * Math.sin(theta(x, y) / 2 + omega()),
 | 
					 | 
				
			||||||
  ];
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const popcorn: Variation = (x, y, transformCoefs) => {
 | 
					export const popcorn: Variation = (x, y, transformCoefs) => {
 | 
				
			||||||
@ -74,28 +74,9 @@ export class Transform {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function weightedChoice<T>(choices: [number, T][]) {
 | 
					 | 
				
			||||||
  const weightSum = choices.reduce(
 | 
					 | 
				
			||||||
    (current, [weight, _t]) => current + weight,
 | 
					 | 
				
			||||||
    0
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
  var choice = Math.random() * weightSum;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  for (var i = 0; i < choices.length; i++) {
 | 
					 | 
				
			||||||
    const [weight, t] = choices[i];
 | 
					 | 
				
			||||||
    if (choice < weight) {
 | 
					 | 
				
			||||||
      return t;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    choice -= weight;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  throw "unreachable";
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export class Flame {
 | 
					export class Flame {
 | 
				
			||||||
  x: number = Math.random() * 2 - 1;
 | 
					  protected x: number = randomBiUnit();
 | 
				
			||||||
  y: number = Math.random() * 2 - 1;
 | 
					  protected y: number = randomBiUnit();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(public readonly transforms: [number, Transform][]) {}
 | 
					  constructor(public readonly transforms: [number, Transform][]) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -103,17 +84,35 @@ export class Flame {
 | 
				
			|||||||
    const transform = weightedChoice(this.transforms);
 | 
					    const transform = weightedChoice(this.transforms);
 | 
				
			||||||
    [this.x, this.y] = transform.apply(this.x, this.y);
 | 
					    [this.x, this.y] = transform.apply(this.x, this.y);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  current() {
 | 
				
			||||||
 | 
					    return [this.x, this.y];
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function camera(x: number, y: number, size: number): [number, number] {
 | 
				
			||||||
 | 
					  // Assuming both:
 | 
				
			||||||
 | 
					  //  - The origin is the intended center of the output image
 | 
				
			||||||
 | 
					  //  - The output image is square
 | 
				
			||||||
 | 
					  // ...then map points in the range (-scale, scale) to pixel coordinates.
 | 
				
			||||||
 | 
					  //
 | 
				
			||||||
 | 
					  // The way `flam3` actually calculates the "camera" for taking a point
 | 
				
			||||||
 | 
					  // and determining which pixel to update is fairly involved. The example
 | 
				
			||||||
 | 
					  // fractal was designed in Apophysis (which shows points in the range
 | 
				
			||||||
 | 
					  // [-2, 2] by default) so we use that assumption to simplify the math here.
 | 
				
			||||||
 | 
					  return [Math.floor(((x + 2) * size) / 4), Math.floor(((y + 2) * size) / 4)];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function plot(x: number, y: number, image: ImageData) {
 | 
					export function plot(x: number, y: number, image: ImageData) {
 | 
				
			||||||
  const pixelX = Math.floor(((x + 2) * image.width) / 4);
 | 
					  // "Zoom out" the camera by a factor of 2 to match the default Apophysis scaling
 | 
				
			||||||
  const pixelY = Math.floor(((y + 2) * image.height) / 4);
 | 
					  // (plot all points in the range [-2, 2])
 | 
				
			||||||
 | 
					  const [pixelX, pixelY] = camera(x, y, image.width);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (
 | 
					  if (
 | 
				
			||||||
    pixelX < 0 ||
 | 
					    pixelX < 0 ||
 | 
				
			||||||
    pixelX > image.width ||
 | 
					    pixelX >= image.width ||
 | 
				
			||||||
    pixelY < 0 ||
 | 
					    pixelY < 0 ||
 | 
				
			||||||
    pixelY > image.height
 | 
					    pixelY >= image.height
 | 
				
			||||||
  ) {
 | 
					  ) {
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -132,7 +131,8 @@ export function render(flame: Flame, quality: number, image: ImageData) {
 | 
				
			|||||||
  for (var i = 0; i < iterations; i++) {
 | 
					  for (var i = 0; i < iterations; i++) {
 | 
				
			||||||
    flame.step();
 | 
					    flame.step();
 | 
				
			||||||
    if (i > 20) {
 | 
					    if (i > 20) {
 | 
				
			||||||
      plot(flame.x, flame.y, image);
 | 
					      const [flameX, flameY] = flame.current();
 | 
				
			||||||
 | 
					      plot(flameX, flameY, image);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -167,7 +167,6 @@ export const transform2 = new Transform(
 | 
				
			|||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const transform3Weight = 0.42233;
 | 
					export const transform3Weight = 0.42233;
 | 
				
			||||||
export const transform3Pdj = [1.09358, 2.13048, 2.54127, 2.37267] as const;
 | 
					 | 
				
			||||||
export const transform3 = new Transform(
 | 
					export const transform3 = new Transform(
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    a: 1.51523,
 | 
					    a: 1.51523,
 | 
				
			||||||
@ -177,7 +176,7 @@ export const transform3 = new Transform(
 | 
				
			|||||||
    e: -1.455964,
 | 
					    e: -1.455964,
 | 
				
			||||||
    f: -0.362059,
 | 
					    f: -0.362059,
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  [[1, pdj(...transform3Pdj)]]
 | 
					  [[1, pdj(1.09358, 2.13048, 2.54127, 2.37267)]]
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function renderBaseline(image: ImageData) {
 | 
					export function renderBaseline(image: ImageData) {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,4 @@
 | 
				
			|||||||
import {
 | 
					import {
 | 
				
			||||||
  Coefs,
 | 
					 | 
				
			||||||
  Flame,
 | 
					  Flame,
 | 
				
			||||||
  Transform,
 | 
					  Transform,
 | 
				
			||||||
  julia,
 | 
					  julia,
 | 
				
			||||||
@ -13,6 +12,8 @@ import {
 | 
				
			|||||||
import { transform2Post } from "./2b-post";
 | 
					import { transform2Post } from "./2b-post";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class FlameFinal extends Flame {
 | 
					export class FlameFinal extends Flame {
 | 
				
			||||||
 | 
					  didLog: boolean = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(
 | 
					  constructor(
 | 
				
			||||||
    transforms: [number, Transform][],
 | 
					    transforms: [number, Transform][],
 | 
				
			||||||
    public readonly final: Transform
 | 
					    public readonly final: Transform
 | 
				
			||||||
@ -20,10 +21,20 @@ export class FlameFinal extends Flame {
 | 
				
			|||||||
    super(transforms);
 | 
					    super(transforms);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  step() {
 | 
					  override step(): void {
 | 
				
			||||||
    super.step();
 | 
					    super.step();
 | 
				
			||||||
    [this.x, this.y] = this.final.apply(this.x, this.y);
 | 
					    [this.x, this.y] = this.final.apply(this.x, this.y);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  override current() {
 | 
				
			||||||
 | 
					    if (!this.didLog) {
 | 
				
			||||||
 | 
					      this.didLog = true;
 | 
				
			||||||
 | 
					      console.trace(`Getting final xform to plot`);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // NOTE: The final transform does not modify the iterator point
 | 
				
			||||||
 | 
					    // return this.final.apply(this.x, this.y);
 | 
				
			||||||
 | 
					    return [this.x, this.y];
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const transformFinal = new Transform(
 | 
					export const transformFinal = new Transform(
 | 
				
			||||||
@ -38,15 +49,15 @@ export const transformFinal = new Transform(
 | 
				
			|||||||
  [[1, julia]]
 | 
					  [[1, julia]]
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function renderFinal(image: ImageData) {
 | 
					export const flameFinal = new FlameFinal(
 | 
				
			||||||
  const flame = new FlameFinal(
 | 
					  [
 | 
				
			||||||
    [
 | 
					    [transform1Weight, transform1],
 | 
				
			||||||
      [transform1Weight, transform1],
 | 
					    [transform2Weight, transform2Post],
 | 
				
			||||||
      [transform2Weight, transform2Post],
 | 
					    [transform3Weight, transform3],
 | 
				
			||||||
      [transform3Weight, transform3],
 | 
					  ],
 | 
				
			||||||
    ],
 | 
					  transformFinal
 | 
				
			||||||
    transformFinal
 | 
					);
 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  render(flame, 1, image);
 | 
					export function renderFinal(image: ImageData) {
 | 
				
			||||||
 | 
					  render(flameFinal, 1, image);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										78
									
								
								posts/2023/06/flam3/3a-binary.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								posts/2023/06/flam3/3a-binary.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,78 @@
 | 
				
			|||||||
 | 
					import { histIndex, imageIndex } from "./0-utility";
 | 
				
			||||||
 | 
					import { camera } from "./2a-variations";
 | 
				
			||||||
 | 
					import { flameFinal, FlameFinal } from "./2c-final";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export abstract class Accumulator {
 | 
				
			||||||
 | 
					  histogram: number[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor(
 | 
				
			||||||
 | 
					    protected readonly width: number,
 | 
				
			||||||
 | 
					    protected readonly height: number
 | 
				
			||||||
 | 
					  ) {
 | 
				
			||||||
 | 
					    for (var i = 0; i < width * height; i++) {
 | 
				
			||||||
 | 
					      this.histogram.push(0);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  accumulate(x: number, y: number) {
 | 
				
			||||||
 | 
					    const [pixelX, pixelY] = camera(x, y, this.width);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (
 | 
				
			||||||
 | 
					      pixelX < 0 ||
 | 
				
			||||||
 | 
					      pixelX >= this.width ||
 | 
				
			||||||
 | 
					      pixelY < 0 ||
 | 
				
			||||||
 | 
					      pixelY >= this.height
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const index = histIndex(pixelX, pixelY, this.width);
 | 
				
			||||||
 | 
					    this.histogram[index] += 1;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  abstract render(image: ImageData): void;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AccumulateBinary extends Accumulator {
 | 
				
			||||||
 | 
					  render(image: ImageData) {
 | 
				
			||||||
 | 
					    for (var x = 0; x < image.width; x++) {
 | 
				
			||||||
 | 
					      for (var y = 0; y < image.height; y++) {
 | 
				
			||||||
 | 
					        const index = histIndex(x, y, image.width);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Color black if this pixel is part of the solution set, white otherwise
 | 
				
			||||||
 | 
					        const value = this.histogram[index] > 0 ? 0 : 0xff;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const iIdx = imageIndex(x, y, image.width);
 | 
				
			||||||
 | 
					        image.data[iIdx + 0] = value;
 | 
				
			||||||
 | 
					        image.data[iIdx + 1] = value;
 | 
				
			||||||
 | 
					        image.data[iIdx + 2] = value;
 | 
				
			||||||
 | 
					        image.data[iIdx + 3] = 0xff;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function render(
 | 
				
			||||||
 | 
					  flame: FlameFinal,
 | 
				
			||||||
 | 
					  quality: number,
 | 
				
			||||||
 | 
					  accumulator: Accumulator,
 | 
				
			||||||
 | 
					  image: ImageData
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					  const iterations = quality * image.width * image.height;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (var i = 0; i < iterations; i++) {
 | 
				
			||||||
 | 
					    flame.step();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (i > 20) {
 | 
				
			||||||
 | 
					      const [flameX, flameY] = flame.current();
 | 
				
			||||||
 | 
					      accumulator.accumulate(flameX, flameY);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  accumulator.render(image);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function renderBinary(image: ImageData) {
 | 
				
			||||||
 | 
					  const accumulator = new AccumulateBinary(image.width, image.height);
 | 
				
			||||||
 | 
					  render(flameFinal, 10, accumulator, image);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										30
									
								
								posts/2023/06/flam3/3b-linear.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								posts/2023/06/flam3/3b-linear.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					import { histIndex, imageIndex } from "./0-utility";
 | 
				
			||||||
 | 
					import { flameFinal } from "./2c-final";
 | 
				
			||||||
 | 
					import { Accumulator, render } from "./3a-binary";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class AccumulateLinear extends Accumulator {
 | 
				
			||||||
 | 
					  render(image: ImageData): void {
 | 
				
			||||||
 | 
					    const maxValue = Math.max(...this.histogram);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (var x = 0; x < image.width; x++) {
 | 
				
			||||||
 | 
					      for (var y = 0; y < image.height; y++) {
 | 
				
			||||||
 | 
					        const index = histIndex(x, y, image.width);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Color full black if this pixel is maxValue, white if not part
 | 
				
			||||||
 | 
					        // of the solution set
 | 
				
			||||||
 | 
					        const value = (1 - this.histogram[index] / maxValue) * 0xff;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const iIdx = imageIndex(x, y, image.width);
 | 
				
			||||||
 | 
					        image.data[iIdx + 0] = value;
 | 
				
			||||||
 | 
					        image.data[iIdx + 1] = value;
 | 
				
			||||||
 | 
					        image.data[iIdx + 2] = value;
 | 
				
			||||||
 | 
					        image.data[iIdx + 3] = 0xff;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function renderLinear(image: ImageData) {
 | 
				
			||||||
 | 
					  const accumulator = new AccumulateLinear(image.width, image.height);
 | 
				
			||||||
 | 
					  render(flameFinal, 10, accumulator, image);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										36
									
								
								posts/2023/06/flam3/3c-logarithmic.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								posts/2023/06/flam3/3c-logarithmic.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					import { histIndex, imageIndex } from "./0-utility";
 | 
				
			||||||
 | 
					import { flameFinal } from "./2c-final";
 | 
				
			||||||
 | 
					import { Accumulator, render } from "./3a-binary";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class AccumulateLogarithmic extends Accumulator {
 | 
				
			||||||
 | 
					  render(image: ImageData): void {
 | 
				
			||||||
 | 
					    // Re-scale vibrancy to be log scale...
 | 
				
			||||||
 | 
					    for (var i = 0; i < this.histogram.length; i++) {
 | 
				
			||||||
 | 
					      this.histogram[i] = Math.log(this.histogram[i]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // ...but otherwise render the same way as linear
 | 
				
			||||||
 | 
					    const maxValue = Math.max(...this.histogram);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (var x = 0; x < image.width; x++) {
 | 
				
			||||||
 | 
					      for (var y = 0; y < image.height; y++) {
 | 
				
			||||||
 | 
					        const index = histIndex(x, y, image.width);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Color full black if this pixel is maxValue, white if not part
 | 
				
			||||||
 | 
					        // of the solution set
 | 
				
			||||||
 | 
					        const value = (1 - this.histogram[index] / maxValue) * 0xff;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const iIdx = imageIndex(x, y, image.width);
 | 
				
			||||||
 | 
					        image.data[iIdx + 0] = value;
 | 
				
			||||||
 | 
					        image.data[iIdx + 1] = value;
 | 
				
			||||||
 | 
					        image.data[iIdx + 2] = value;
 | 
				
			||||||
 | 
					        image.data[iIdx + 3] = 0xff;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function renderLogarithmic(image: ImageData) {
 | 
				
			||||||
 | 
					  const accumulator = new AccumulateLogarithmic(image.width, image.height);
 | 
				
			||||||
 | 
					  render(flameFinal, 10, accumulator, image);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -5,6 +5,9 @@ import { gasket } from "./1-gasket";
 | 
				
			|||||||
import { renderBaseline } from "./2a-variations";
 | 
					import { renderBaseline } from "./2a-variations";
 | 
				
			||||||
import { renderPost } from "./2b-post";
 | 
					import { renderPost } from "./2b-post";
 | 
				
			||||||
import { renderFinal } from "./2c-final";
 | 
					import { renderFinal } from "./2c-final";
 | 
				
			||||||
 | 
					import { renderBinary } from "./3a-binary";
 | 
				
			||||||
 | 
					import { renderLinear } from "./3b-linear";
 | 
				
			||||||
 | 
					import { renderLogarithmic } from "./3c-logarithmic";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function () {
 | 
					export default function () {
 | 
				
			||||||
  const Layout = Blog({
 | 
					  const Layout = Blog({
 | 
				
			||||||
@ -14,14 +17,17 @@ export default function () {
 | 
				
			|||||||
  });
 | 
					  });
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <Layout>
 | 
					    <Layout>
 | 
				
			||||||
      <div>
 | 
					      {/* <div>
 | 
				
			||||||
        <Canvas f={gasket} />
 | 
					        <Canvas f={gasket} />
 | 
				
			||||||
        <Canvas f={renderBaseline} />
 | 
					        <Canvas f={renderBaseline} />
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
      <div>
 | 
					      <div>
 | 
				
			||||||
        <Canvas f={renderPost} />
 | 
					        <Canvas f={renderPost} />
 | 
				
			||||||
        <Canvas f={renderFinal} />
 | 
					        <Canvas f={renderFinal} />
 | 
				
			||||||
      </div>
 | 
					      </div> */}
 | 
				
			||||||
 | 
					      <Canvas f={renderBinary} />
 | 
				
			||||||
 | 
					      <Canvas f={renderLinear} />
 | 
				
			||||||
 | 
					      <Canvas f={renderLogarithmic} />
 | 
				
			||||||
    </Layout>
 | 
					    </Layout>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user