Compare commits
29 Commits
42039b0bac
...
flam3.js
Author | SHA1 | Date | |
---|---|---|---|
8edad84796 | |||
b74cf41713 | |||
342e4ee6cf | |||
04d5099338 | |||
7f44243cd0 | |||
782b00320b | |||
01af290363 | |||
458920b1ae | |||
90ac3559ae | |||
ea946b2ae9 | |||
ce4fdd154a | |||
f3c463360e | |||
de903e1617 | |||
671b97b7a0 | |||
e8ec0e0521 | |||
79394519c7 | |||
4bcc321c4b | |||
68c0b26015 | |||
0a7323a8c9 | |||
8e1c7725c1 | |||
65d58c7f7d | |||
7894cf8c81 | |||
dff82d018b | |||
11b70a6da8 | |||
c32052e9e1 | |||
b2ff9535c9 | |||
b7a6971174 | |||
2d3b9c0c3a | |||
3c80fbab21 |
1277
package-lock.json
generated
18
package.json
@ -12,21 +12,31 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource/jetbrains-mono": "^4.5.12",
|
"@fontsource/jetbrains-mono": "^4.5.12",
|
||||||
"@fontsource/lato": "^4.5.10",
|
"@fontsource/lato": "^4.5.10",
|
||||||
|
"@fortawesome/fontawesome-svg-core": "^6.4.0",
|
||||||
|
"@fortawesome/free-brands-svg-icons": "^6.4.0",
|
||||||
|
"@fortawesome/free-regular-svg-icons": "^6.4.0",
|
||||||
|
"@fortawesome/free-solid-svg-icons": "^6.4.0",
|
||||||
|
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||||
|
"@mdx-js/react": "^2.3.0",
|
||||||
|
"highlight.js": "^11.7.0",
|
||||||
|
"katex": "^0.16.6",
|
||||||
"normalize.css": "^8.0.1",
|
"normalize.css": "^8.0.1",
|
||||||
"prism-themes": "^1.9.0",
|
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0"
|
"react-dom": "^18.2.0",
|
||||||
|
"remark-frontmatter": "^4.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@bspeice/vite-plugin-blog": "^1.1.0",
|
"@bspeice/vite-plugin-blog": "^1.1.0",
|
||||||
"@mdx-js/rollup": "^2.3.0",
|
"@mdx-js/rollup": "^2.3.0",
|
||||||
"@types/react": "^18.0.28",
|
"@types/react": "^18.0.28",
|
||||||
"@types/react-dom": "^18.0.11",
|
"@types/react-dom": "^18.0.11",
|
||||||
"@types/remark-prism": "^1.3.4",
|
|
||||||
"@vitejs/plugin-react-swc": "^3.0.0",
|
"@vitejs/plugin-react-swc": "^3.0.0",
|
||||||
"husky": "^8.0.0",
|
"husky": "^8.0.0",
|
||||||
"pretty-quick": "^3.1.3",
|
"pretty-quick": "^3.1.3",
|
||||||
"remark-prism": "^1.3.6",
|
"rehype-highlight": "^6.0.0",
|
||||||
|
"rehype-katex": "^6.0.3",
|
||||||
|
"remark-math": "^5.1.1",
|
||||||
|
"remark-mdx-frontmatter": "^3.0.0",
|
||||||
"typescript": "^4.9.3",
|
"typescript": "^4.9.3",
|
||||||
"vite": "^4.2.0"
|
"vite": "^4.2.0"
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,16 @@
|
|||||||
import { PropsWithChildren } from "react";
|
import { PropsWithChildren, StrictMode } from "react";
|
||||||
|
|
||||||
import "./style.css";
|
import "./style.css";
|
||||||
|
import Navbar from "./Navbar";
|
||||||
const Sidebar: React.FC = () => (
|
|
||||||
<span className={"navbar"}>
|
|
||||||
<a href="/">Home</a>/<a href="/about">About</a>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
|
|
||||||
const Layout: React.FC<PropsWithChildren> = ({ children }) => (
|
const Layout: React.FC<PropsWithChildren> = ({ children }) => (
|
||||||
<div className="gridOffset">
|
<StrictMode>
|
||||||
<div className="gridOffsetSide">
|
<div className="gridOffset">
|
||||||
<Sidebar />
|
<Navbar />
|
||||||
|
<hr style={{ marginTop: "0" }} />
|
||||||
|
{children}
|
||||||
</div>
|
</div>
|
||||||
{children}
|
</StrictMode>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
export default Layout;
|
export default Layout;
|
||||||
|
18
pages/Navbar.tsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { faHome, faUser } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
|
||||||
|
const Navbar: React.FC = () => (
|
||||||
|
<span className="navbar">
|
||||||
|
<a href="/">
|
||||||
|
<FontAwesomeIcon icon={faHome} className="icon" />
|
||||||
|
Home
|
||||||
|
</a>
|
||||||
|
<span>/</span>
|
||||||
|
<a href="/about">
|
||||||
|
<FontAwesomeIcon icon={faUser} className="icon" />
|
||||||
|
About
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Navbar;
|
@ -1,8 +1,23 @@
|
|||||||
import Layout from "./Page";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { faGithub, faLinkedin } from "@fortawesome/free-brands-svg-icons";
|
||||||
|
import { faEnvelope } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
|
||||||
|
import Layout from "./LayoutPage";
|
||||||
export default Layout;
|
export default Layout;
|
||||||
|
|
||||||
Developer currently living in New York City
|
Developer living in New York City
|
||||||
|
|
||||||
Email: [bradlee@speice.io](mailto:bradlee@speice.io)
|
> <FontAwesomeIcon
|
||||||
|
> icon={faEnvelope}
|
||||||
LinkedIn: [bradleespeice](https://www.linkedin.com/in/bradleespeice/)
|
> style={{ color: "var(--color-secondary)" }}
|
||||||
|
> /> [bradlee@speice.io](mailto:bradlee@speice.io)
|
||||||
|
>
|
||||||
|
> <FontAwesomeIcon
|
||||||
|
> icon={faGithub}
|
||||||
|
> style={{ color: "var(--color-secondary)" }}
|
||||||
|
> /> [bspeice](https://github.com/bspeice)
|
||||||
|
>
|
||||||
|
> <FontAwesomeIcon
|
||||||
|
> icon={faLinkedin}
|
||||||
|
> style={{ color: "var(--color-secondary)" }}
|
||||||
|
> /> [bradleespeice](https://www.linkedin.com/in/bradleespeice/)
|
||||||
|
@ -1,9 +1,18 @@
|
|||||||
import Layout from "./LayoutPage";
|
import Layout from "./LayoutPage";
|
||||||
|
|
||||||
|
export const Page = () => (
|
||||||
|
<>
|
||||||
|
<p>Is this thing on?</p>
|
||||||
|
<p>
|
||||||
|
<a href="/2023/06/flam3">Code</a>
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
export default function () {
|
export default function () {
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
<p>Is this thing on?</p>
|
<Page />
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
100
pages/style.css
@ -2,12 +2,23 @@
|
|||||||
|
|
||||||
@import "@fontsource/lato";
|
@import "@fontsource/lato";
|
||||||
@import "@fontsource/jetbrains-mono";
|
@import "@fontsource/jetbrains-mono";
|
||||||
@import "prism-themes/themes/prism-material-dark";
|
@import "highlight.js/styles/atom-one-dark.css";
|
||||||
|
@import "katex/dist/katex.min.css";
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--color-primary: #000;
|
||||||
|
--color-secondary: #777;
|
||||||
|
--color-tertiary: #999;
|
||||||
|
--color-highlight: #f4f4f4;
|
||||||
|
|
||||||
|
--color-primary-highlight: #fff;
|
||||||
|
--color-secondary-highlight: #999;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: "Lato", sans-serif;
|
font-family: "Lato", sans-serif;
|
||||||
font-size: 14pt;
|
font-size: 15pt;
|
||||||
line-height: 1.4;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1,
|
h1,
|
||||||
@ -20,6 +31,20 @@ h6 {
|
|||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
color: var(--color-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
|
color: var(--color-tertiary);
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--color-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
p,
|
p,
|
||||||
ul {
|
ul {
|
||||||
margin-top: 0.5em;
|
margin-top: 0.5em;
|
||||||
@ -30,43 +55,76 @@ pre {
|
|||||||
padding: 1em 0;
|
padding: 1em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
code,
|
code {
|
||||||
code[class*="language-"] {
|
|
||||||
font-family: "JetBrains Mono", monospace;
|
font-family: "JetBrains Mono", monospace;
|
||||||
|
font-size: 13pt;
|
||||||
|
background-color: var(--color-highlight);
|
||||||
|
}
|
||||||
|
|
||||||
|
a,
|
||||||
|
a > code {
|
||||||
|
text-decoration: dotted;
|
||||||
|
border-color: var(--color-secondary);
|
||||||
|
border-bottom-style: dotted;
|
||||||
|
border-bottom-width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a,
|
||||||
|
a > code {
|
||||||
|
transition: all ease 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover,
|
||||||
|
a:hover > code,
|
||||||
|
a > code:hover {
|
||||||
|
color: var(--color-primary-highlight);
|
||||||
|
background-color: var(--color-secondary-highlight);
|
||||||
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gridOffset {
|
.gridOffset {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns:
|
grid-template-columns:
|
||||||
[full-start] minmax(1em, 2fr)
|
[full-start] minmax(1em, 2fr)
|
||||||
[main-start] minmax(0, 45em) [main-end]
|
[main-start] minmax(0, 42em) [main-end]
|
||||||
minmax(0, 1fr)
|
minmax(0, 5fr) [full-end];
|
||||||
[side-start] minmax(0, 3fr) [side-end]
|
|
||||||
minmax(1em, 2fr) [full-end];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.gridOffset > :not(.gridOffsetSide) {
|
.gridOffset > * {
|
||||||
grid-column: main;
|
grid-column: main;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gridOffset > div.remark-highlight,
|
.gridOffset hr {
|
||||||
.gridOffset > div.remark-highlight > pre {
|
display: inherit;
|
||||||
|
grid-column: main;
|
||||||
|
grid-template-columns: inherit;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: var(--color-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.gridOffset > pre {
|
||||||
display: inherit;
|
display: inherit;
|
||||||
grid-column: full;
|
grid-column: full;
|
||||||
grid-template-columns: inherit;
|
grid-template-columns: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gridOffset > div.remark-highlight > pre > code {
|
.gridOffset > pre > code {
|
||||||
grid-column: main;
|
grid-column: main / full;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gridOffsetSide {
|
.navbar {
|
||||||
grid-column: side;
|
text-align: right;
|
||||||
margin-top: 1em;
|
margin-top: 0.5em;
|
||||||
margin-bottom: 1em;
|
margin-bottom: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar > * {
|
.navbar :not(.icon) {
|
||||||
margin-left: 0.5em;
|
padding-left: 0.25em;
|
||||||
margin-right: 0.5em;
|
padding-right: 0.25em;
|
||||||
|
text-decoration: none;
|
||||||
|
border-bottom-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
margin-right: 0.3em;
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
|
---
|
||||||
|
title: "Global Memory Usage: The Whole World"
|
||||||
|
description: Static considered slightly less harmful.
|
||||||
|
published: 2019-02-05
|
||||||
|
---
|
||||||
|
|
||||||
import Blog from "../../LayoutBlog";
|
import Blog from "../../LayoutBlog";
|
||||||
export default Blog({
|
export default Blog(frontmatter);
|
||||||
title: "Global Memory Usage: The Whole World",
|
|
||||||
description: "Static considered slightly less harmful.",
|
$m_at^h$
|
||||||
published: "2019-02-05",
|
|
||||||
});
|
|
||||||
|
|
||||||
The first memory type we'll look at is pretty special: when Rust can prove that a _value_ is fixed
|
The first memory type we'll look at is pretty special: when Rust can prove that a _value_ is fixed
|
||||||
for the life of a program (`const`), and when a _reference_ is unique for the life of a program
|
for the life of a program (`const`), and when a _reference_ is unique for the life of a program
|
||||||
|
117
posts/2023/06/flam3/0-canvas.tsx
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
|
import { DEFAULT_SIZE, RenderParams, Renderer } from "./0-utility.js";
|
||||||
|
import { paramsGasket } from "./1-gasket.js";
|
||||||
|
import { paramsBaseline } from "./2a-baseline.js";
|
||||||
|
import { paramsPost } from "./2b-post.js";
|
||||||
|
import { paramsFinal } from "./2c-final.js";
|
||||||
|
import { paramsBinary } from "./3a-binary.js";
|
||||||
|
import { paramsLinear } from "./3b-linear.js";
|
||||||
|
import { paramsLogarithmic } from "./3c-logarithmic.js";
|
||||||
|
import { paramsColor } from "./4-color.js";
|
||||||
|
import { paramsGasketFlame } from "./5a-gasket.js";
|
||||||
|
import { paramsSolo1, paramsSolo2, paramsSolo3 } from "./5b-solo.js";
|
||||||
|
|
||||||
|
// @ts-expect-error: Vite URL import
|
||||||
|
import urlGasket from "./images/1-gasket.png";
|
||||||
|
// @ts-expect-error: Vite URL import
|
||||||
|
import urlBaseline from "./images/2a-baseline.png";
|
||||||
|
// @ts-expect-error: Vite URL import
|
||||||
|
import urlPost from "./images/2b-post.png";
|
||||||
|
// @ts-expect-error: Vite URL import
|
||||||
|
import urlFinal from "./images/2c-final.png";
|
||||||
|
// @ts-expect-error: Vite URL import
|
||||||
|
import urlBinary from "./images/3a-binary.png";
|
||||||
|
// @ts-expect-error: Vite URL import
|
||||||
|
import urlLinear from "./images/3b-linear.png";
|
||||||
|
// @ts-expect-error: Vite URL import
|
||||||
|
import urlLogarithmic from "./images/3c-logarithmic.png";
|
||||||
|
// @ts-expect-error: Vite URL import
|
||||||
|
import urlColor from "./images/4-color.png";
|
||||||
|
// @ts-expect-error: Vite URL import
|
||||||
|
import urlGasketFlame from "./images/5a-gasket.png";
|
||||||
|
// @ts-expect-error: Vite URL import
|
||||||
|
import urlSolo1 from "./images/5b-solo1.png";
|
||||||
|
// @ts-expect-error: Vite URL import
|
||||||
|
import urlSolo2 from "./images/5b-solo2.png";
|
||||||
|
// @ts-expect-error: Vite URL import
|
||||||
|
import urlSolo3 from "./images/5b-solo3.png";
|
||||||
|
|
||||||
|
export const DEFAULT_STEP: number = 0.1;
|
||||||
|
|
||||||
|
export type CanvasParams = RenderParams & { url: string };
|
||||||
|
|
||||||
|
export const CanvasRenderer: React.FC<CanvasParams> = (params) => {
|
||||||
|
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
||||||
|
const [useUrl, setUseUrl] = useState(true);
|
||||||
|
|
||||||
|
var qualityCurrent: number = 0;
|
||||||
|
var rendererCurrent: Renderer = params.renderer(DEFAULT_SIZE);
|
||||||
|
|
||||||
|
const animate = () => {
|
||||||
|
const ctx = canvasRef.current?.getContext("2d");
|
||||||
|
if (!ctx) {
|
||||||
|
console.log("Ref not ready");
|
||||||
|
requestAnimationFrame(animate);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const image = ctx.createImageData(DEFAULT_SIZE, DEFAULT_SIZE);
|
||||||
|
rendererCurrent.run(DEFAULT_STEP);
|
||||||
|
rendererCurrent.render(image);
|
||||||
|
ctx.putImageData(image, 0, 0);
|
||||||
|
|
||||||
|
if (qualityCurrent < params.quality) {
|
||||||
|
qualityCurrent += DEFAULT_STEP;
|
||||||
|
requestAnimationFrame(animate);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const useCanvas = () => {
|
||||||
|
setUseUrl(false);
|
||||||
|
requestAnimationFrame(animate);
|
||||||
|
};
|
||||||
|
|
||||||
|
const reset = () => {
|
||||||
|
qualityCurrent = 0;
|
||||||
|
rendererCurrent = params.renderer(DEFAULT_SIZE);
|
||||||
|
requestAnimationFrame(animate);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<img src={params.url} onClick={useCanvas} hidden={!useUrl} />
|
||||||
|
<canvas
|
||||||
|
ref={canvasRef}
|
||||||
|
width={DEFAULT_SIZE}
|
||||||
|
height={DEFAULT_SIZE}
|
||||||
|
onClick={reset}
|
||||||
|
hidden={useUrl}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CanvasGasket: React.FC<{}> = () =>
|
||||||
|
CanvasRenderer({ url: urlGasket, ...paramsGasket });
|
||||||
|
export const CanvasBaseline: React.FC<{}> = () =>
|
||||||
|
CanvasRenderer({ url: urlBaseline, ...paramsBaseline });
|
||||||
|
export const CanvasPost: React.FC<{}> = () =>
|
||||||
|
CanvasRenderer({ url: urlPost, ...paramsPost });
|
||||||
|
export const CanvasFinal: React.FC<{}> = () =>
|
||||||
|
CanvasRenderer({ url: urlFinal, ...paramsFinal });
|
||||||
|
export const CanvasBinary: React.FC<{}> = () =>
|
||||||
|
CanvasRenderer({ url: urlBinary, ...paramsBinary });
|
||||||
|
export const CanvasLinear: React.FC<{}> = () =>
|
||||||
|
CanvasRenderer({ url: urlLinear, ...paramsLinear });
|
||||||
|
export const CanvasLogarithmic: React.FC<{}> = () =>
|
||||||
|
CanvasRenderer({ url: urlLogarithmic, ...paramsLogarithmic });
|
||||||
|
export const CanvasColor: React.FC<{}> = () =>
|
||||||
|
CanvasRenderer({ url: urlColor, ...paramsColor });
|
||||||
|
export const CanvasGasketFlame: React.FC<{}> = () =>
|
||||||
|
CanvasRenderer({ url: urlGasketFlame, ...paramsGasketFlame });
|
||||||
|
export const CanvasSolo1: React.FC<{}> = () =>
|
||||||
|
CanvasRenderer({ url: urlSolo1, ...paramsSolo1 });
|
||||||
|
export const CanvasSolo2: React.FC<{}> = () =>
|
||||||
|
CanvasRenderer({ url: urlSolo2, ...paramsSolo2 });
|
||||||
|
export const CanvasSolo3: React.FC<{}> = () =>
|
||||||
|
CanvasRenderer({ url: urlSolo3, ...paramsSolo3 });
|
96
posts/2023/06/flam3/0-utility.ts
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
export const DEFAULT_SIZE: number = 400;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Image render manager
|
||||||
|
*
|
||||||
|
* This class tracks the chaos game state so we can periodically
|
||||||
|
* get an image.
|
||||||
|
*/
|
||||||
|
export abstract class Renderer {
|
||||||
|
/**
|
||||||
|
* Build a render manager. For simplicity, this class assumes
|
||||||
|
* we're working with a square image.
|
||||||
|
*
|
||||||
|
* @param size Image width and height
|
||||||
|
*/
|
||||||
|
constructor(public readonly size: number) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the chaos game
|
||||||
|
*
|
||||||
|
* @param quality iteration count
|
||||||
|
*/
|
||||||
|
abstract run(quality: number): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output the current chaos game state to image
|
||||||
|
*
|
||||||
|
* @param image output pixel buffer
|
||||||
|
*/
|
||||||
|
abstract render(image: ImageData): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type RenderParams = {
|
||||||
|
quality: number;
|
||||||
|
renderer: (size: number) => Renderer;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns random number in the bi-unit square (-1, 1)
|
||||||
|
*/
|
||||||
|
export function randomBiUnit() {
|
||||||
|
// Math.random() produces a number in the range [0, 1),
|
||||||
|
// scale to (-1, 1)
|
||||||
|
return Math.random() * 2 - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns random integer (with equal weight) in the range [min, max)
|
||||||
|
*/
|
||||||
|
export function randomInteger(min: number, max: number) {
|
||||||
|
return Math.floor(Math.random() * (max - min)) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param choices array of [weight, value] pairs
|
||||||
|
* @returns pair of [index, value]
|
||||||
|
*/
|
||||||
|
export function weightedChoice<T>(choices: [number, T][]): [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 [i, t];
|
||||||
|
}
|
||||||
|
|
||||||
|
choice -= weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw "unreachable";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param x pixel coordinate
|
||||||
|
* @param y pixel coordinate
|
||||||
|
* @param width image width
|
||||||
|
* @returns index into ImageData buffer for a specific pixel
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param x pixel coordinate
|
||||||
|
* @param y pixel coordinate
|
||||||
|
* @param width image width
|
||||||
|
* @returns index into a histogram for a specific pixel
|
||||||
|
*/
|
||||||
|
export function histIndex(x: number, y: number, width: number) {
|
||||||
|
return y * width + x;
|
||||||
|
}
|
95
posts/2023/06/flam3/1-gasket.ts
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
import {
|
||||||
|
randomBiUnit,
|
||||||
|
randomInteger,
|
||||||
|
imageIndex,
|
||||||
|
Renderer,
|
||||||
|
histIndex,
|
||||||
|
RenderParams,
|
||||||
|
} from "./0-utility.js";
|
||||||
|
|
||||||
|
type Transform = (x: number, y: number) => [number, number];
|
||||||
|
|
||||||
|
export class RendererGasket extends Renderer {
|
||||||
|
private values = new Uint8Array(this.size * this.size);
|
||||||
|
private x = randomBiUnit();
|
||||||
|
private y = randomBiUnit();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translate values in the flame coordinate system to pixel coordinates
|
||||||
|
*
|
||||||
|
* A trivial implementation would take the range [-1, 1], shift it to [0, 2],
|
||||||
|
* then scale by the image size:
|
||||||
|
* pixelX = Math.floor((x + 1) * this.size / 2)
|
||||||
|
* pixelY = Math.floor((y + 1) * this.size / 2)
|
||||||
|
*
|
||||||
|
* However, because the gasket solution set only has values in the range [0, 1],
|
||||||
|
* that would lead to wasting 3/4 of the pixels. We'll instead plot just the range
|
||||||
|
* we care about:
|
||||||
|
* pixelX = Math.floor(x * this.size)
|
||||||
|
* pixelY = Math.floor(x * this.size)
|
||||||
|
*
|
||||||
|
* @param x point in the range [-1, 1]
|
||||||
|
* @param y point in the range [-1, 1]
|
||||||
|
*/
|
||||||
|
plot(x: number, y: number): void {
|
||||||
|
var pixelX = Math.floor(x * this.size);
|
||||||
|
var pixelY = Math.floor(y * this.size);
|
||||||
|
|
||||||
|
if (
|
||||||
|
pixelX < 0 ||
|
||||||
|
pixelX >= this.size ||
|
||||||
|
pixelY < 0 ||
|
||||||
|
pixelY >= this.size
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const index = histIndex(pixelX, pixelY, this.size);
|
||||||
|
this.values[index] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
run(quality: number): void {
|
||||||
|
const transforms: Transform[] = [
|
||||||
|
(x, y) => [x / 2, y / 2],
|
||||||
|
(x, y) => [(x + 1) / 2, y / 2],
|
||||||
|
(x, y) => [x / 2, (y + 1) / 2],
|
||||||
|
];
|
||||||
|
|
||||||
|
// NOTE: `x` and `y` are set as fields on this class
|
||||||
|
// (rather than here in the main chaos game method) because
|
||||||
|
// we render in chunks (to avoid bogging down the browser)
|
||||||
|
|
||||||
|
const iterations = quality * this.size * this.size;
|
||||||
|
for (var i = 0; i < iterations; i++) {
|
||||||
|
const transformIndex = randomInteger(0, transforms.length);
|
||||||
|
[this.x, this.y] = transforms[transformIndex](this.x, this.y);
|
||||||
|
|
||||||
|
if (i >= 20) {
|
||||||
|
this.plot(this.x, this.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render(image: ImageData): void {
|
||||||
|
for (var pixelX = 0; pixelX < image.width; pixelX++) {
|
||||||
|
for (var pixelY = 0; pixelY < image.height; pixelY++) {
|
||||||
|
const hIndex = histIndex(pixelX, pixelY, this.size);
|
||||||
|
if (!this.values[hIndex]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the pixel black
|
||||||
|
const iIndex = imageIndex(pixelX, pixelY, this.size);
|
||||||
|
image.data[iIndex + 0] = 0;
|
||||||
|
image.data[iIndex + 1] = 0;
|
||||||
|
image.data[iIndex + 2] = 0;
|
||||||
|
image.data[iIndex + 3] = 0xff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const paramsGasket: RenderParams = {
|
||||||
|
quality: 1,
|
||||||
|
renderer: (size) => new RendererGasket(size),
|
||||||
|
};
|
223
posts/2023/06/flam3/2a-baseline.ts
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
import {
|
||||||
|
RenderParams,
|
||||||
|
Renderer,
|
||||||
|
histIndex,
|
||||||
|
imageIndex,
|
||||||
|
randomBiUnit,
|
||||||
|
weightedChoice,
|
||||||
|
} from "./0-utility.js";
|
||||||
|
|
||||||
|
export type Variation = (
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
transformCoefs: Coefs
|
||||||
|
) => [number, number];
|
||||||
|
|
||||||
|
export type Coefs = {
|
||||||
|
a: number;
|
||||||
|
b: number;
|
||||||
|
c: number;
|
||||||
|
d: number;
|
||||||
|
e: number;
|
||||||
|
f: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const identityCoefs = {
|
||||||
|
a: 1,
|
||||||
|
b: 0,
|
||||||
|
c: 0,
|
||||||
|
d: 0,
|
||||||
|
e: 1,
|
||||||
|
f: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
function r(x: number, y: number) {
|
||||||
|
return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
function theta(x: number, y: number) {
|
||||||
|
return Math.atan2(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
function omega(): number {
|
||||||
|
return Math.random() > 0.5 ? Math.PI : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const linear: Variation = (x, y) => [x, y];
|
||||||
|
|
||||||
|
export const julia: Variation = (x, y) => {
|
||||||
|
const sqrtR = Math.sqrt(r(x, y));
|
||||||
|
const thetaVal = theta(x, y) / 2 + omega();
|
||||||
|
return [sqrtR * Math.cos(thetaVal), sqrtR * Math.sin(thetaVal)];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const popcorn: Variation = (x, y, transformCoefs) => {
|
||||||
|
return [
|
||||||
|
x + transformCoefs.c * Math.sin(Math.tan(3 * y)),
|
||||||
|
y + transformCoefs.f * Math.sin(Math.tan(3 * x)),
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const pdj: (
|
||||||
|
pdjA: number,
|
||||||
|
pdjB: number,
|
||||||
|
pdjC: number,
|
||||||
|
pdjD: number
|
||||||
|
) => Variation = (pdjA, pdjB, pdjC, pdjD) => {
|
||||||
|
return (x, y) => [
|
||||||
|
Math.sin(pdjA * y) - Math.cos(pdjB * x),
|
||||||
|
Math.sin(pdjC * x) - Math.cos(pdjD * y),
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
export class Transform {
|
||||||
|
constructor(
|
||||||
|
public readonly coefs: Coefs,
|
||||||
|
public readonly variations: [number, Variation][]
|
||||||
|
) {}
|
||||||
|
|
||||||
|
apply(x: number, y: number): [number, number] {
|
||||||
|
const xformX = this.coefs.a * x + this.coefs.b * y + this.coefs.c;
|
||||||
|
const xformY = this.coefs.d * x + this.coefs.e * y + this.coefs.f;
|
||||||
|
|
||||||
|
var [curX, curY] = [0, 0];
|
||||||
|
this.variations.forEach(([blend, variation]) => {
|
||||||
|
const [varX, varY] = variation(xformX, xformY, this.coefs);
|
||||||
|
curX += blend * varX;
|
||||||
|
curY += blend * varY;
|
||||||
|
});
|
||||||
|
|
||||||
|
return [curX, curY];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translate values in the flame coordinate system to pixel coordinates
|
||||||
|
*
|
||||||
|
* The way `flam3` actually calculates the "camera" for mapping a point
|
||||||
|
* to its pixel coordinate is fairly involved - it also needs to calculate
|
||||||
|
* zoom and rotation. We'll make some simplifying assumptions:
|
||||||
|
* - The final image is square
|
||||||
|
* - We want to plot the range [-2, 2]
|
||||||
|
*
|
||||||
|
* The reference parameters were designed in Apophysis, which uses the
|
||||||
|
* range [-2, 2] by default (the `scale` parameter in XML defines the
|
||||||
|
* "pixels per unit", and with the default zoom, is chosen to give a
|
||||||
|
* range of [-2, 2]).
|
||||||
|
*
|
||||||
|
* @param x point in the range [-2, 2]
|
||||||
|
* @param y point in the range [-2, 2]
|
||||||
|
* @param size image size
|
||||||
|
* @returns pair of pixel coordinates
|
||||||
|
*/
|
||||||
|
export function camera(x: number, y: number, size: number): [number, number] {
|
||||||
|
return [Math.floor(((x + 2) * size) / 4), Math.floor(((y + 2) * size) / 4)];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RendererFlame extends Renderer {
|
||||||
|
private values = new Uint8Array(this.size * this.size);
|
||||||
|
protected x = randomBiUnit();
|
||||||
|
protected y = randomBiUnit();
|
||||||
|
|
||||||
|
constructor(size: number, public readonly transforms: [number, Transform][]) {
|
||||||
|
super(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
plot(x: number, y: number) {
|
||||||
|
const [pixelX, pixelY] = camera(x, y, this.size);
|
||||||
|
|
||||||
|
if (
|
||||||
|
pixelX < 0 ||
|
||||||
|
pixelX >= this.size ||
|
||||||
|
pixelY < 0 ||
|
||||||
|
pixelY >= this.size
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hIndex = histIndex(pixelX, pixelY, this.size);
|
||||||
|
this.values[hIndex] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
run(quality: number): void {
|
||||||
|
const iterations = quality * this.size * this.size;
|
||||||
|
for (var i = 0; i < iterations; i++) {
|
||||||
|
const [_, transform] = weightedChoice(this.transforms);
|
||||||
|
[this.x, this.y] = transform.apply(this.x, this.y);
|
||||||
|
|
||||||
|
if (i > 20) {
|
||||||
|
this.plot(this.x, this.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render(image: ImageData): void {
|
||||||
|
for (var x = 0; x < this.size; x++) {
|
||||||
|
for (var y = 0; y < this.size; y++) {
|
||||||
|
const hIndex = histIndex(x, y, this.size);
|
||||||
|
if (!this.values[hIndex]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const iIndex = imageIndex(x, y, this.size);
|
||||||
|
image.data[iIndex + 0] = 0;
|
||||||
|
image.data[iIndex + 1] = 0;
|
||||||
|
image.data[iIndex + 2] = 0;
|
||||||
|
image.data[iIndex + 3] = 0xff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const transform1Weight = 0.56453495;
|
||||||
|
export const transform1 = new Transform(
|
||||||
|
{
|
||||||
|
a: -1.381068,
|
||||||
|
b: -1.381068,
|
||||||
|
c: 0,
|
||||||
|
d: 1.381068,
|
||||||
|
e: -1.381068,
|
||||||
|
f: 0,
|
||||||
|
},
|
||||||
|
[[1, julia]]
|
||||||
|
);
|
||||||
|
|
||||||
|
export const transform2Weight = 0.013135;
|
||||||
|
export const transform2 = new Transform(
|
||||||
|
{
|
||||||
|
a: 0.031393,
|
||||||
|
b: 0.031367,
|
||||||
|
c: 0,
|
||||||
|
d: -0.031367,
|
||||||
|
e: 0.031393,
|
||||||
|
f: 0,
|
||||||
|
},
|
||||||
|
[
|
||||||
|
[1, linear],
|
||||||
|
[1, popcorn],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
export const transform3Weight = 0.42233;
|
||||||
|
export const transform3 = new Transform(
|
||||||
|
{
|
||||||
|
a: 1.51523,
|
||||||
|
b: -3.048677,
|
||||||
|
c: 0.724135,
|
||||||
|
d: 0.740356,
|
||||||
|
e: -1.455964,
|
||||||
|
f: -0.362059,
|
||||||
|
},
|
||||||
|
[[1, pdj(1.09358, 2.13048, 2.54127, 2.37267)]]
|
||||||
|
);
|
||||||
|
|
||||||
|
export const transformAll: [number, Transform][] = [
|
||||||
|
[transform1Weight, transform1],
|
||||||
|
[transform2Weight, transform2],
|
||||||
|
[transform3Weight, transform3],
|
||||||
|
];
|
||||||
|
|
||||||
|
export const paramsBaseline: RenderParams = {
|
||||||
|
quality: 1,
|
||||||
|
renderer: (size) => new RendererFlame(size, transformAll),
|
||||||
|
};
|
68
posts/2023/06/flam3/2b-post.ts
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import { RenderParams } from "./0-utility.js";
|
||||||
|
import {
|
||||||
|
Coefs,
|
||||||
|
Variation,
|
||||||
|
Transform,
|
||||||
|
transform1Weight,
|
||||||
|
transform1,
|
||||||
|
transform2Weight,
|
||||||
|
transform2,
|
||||||
|
transform3Weight,
|
||||||
|
transform3,
|
||||||
|
identityCoefs,
|
||||||
|
RendererFlame,
|
||||||
|
} from "./2a-baseline.js";
|
||||||
|
|
||||||
|
export class TransformPost extends Transform {
|
||||||
|
constructor(
|
||||||
|
coefs: Coefs,
|
||||||
|
variations: [number, Variation][],
|
||||||
|
public readonly post: Coefs
|
||||||
|
) {
|
||||||
|
super(coefs, variations);
|
||||||
|
}
|
||||||
|
|
||||||
|
apply(x: number, y: number): [number, number] {
|
||||||
|
const [transformX, transformY] = super.apply(x, y);
|
||||||
|
return [
|
||||||
|
transformX * this.post.a + transformY * this.post.b + this.post.c,
|
||||||
|
transformX * this.post.d + transformY * this.post.e + this.post.f,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const transform1Post = new TransformPost(
|
||||||
|
transform1.coefs,
|
||||||
|
transform1.variations,
|
||||||
|
identityCoefs
|
||||||
|
);
|
||||||
|
|
||||||
|
export const transform2Post = new TransformPost(
|
||||||
|
transform2.coefs,
|
||||||
|
transform2.variations,
|
||||||
|
{
|
||||||
|
a: 1,
|
||||||
|
b: 0,
|
||||||
|
c: 0.241352,
|
||||||
|
d: 0,
|
||||||
|
e: 1,
|
||||||
|
f: 0.271521,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const transform3Post = new TransformPost(
|
||||||
|
transform3.coefs,
|
||||||
|
transform3.variations,
|
||||||
|
identityCoefs
|
||||||
|
);
|
||||||
|
|
||||||
|
export const transformAllPost: [number, TransformPost][] = [
|
||||||
|
[transform1Weight, transform1Post],
|
||||||
|
[transform2Weight, transform2Post],
|
||||||
|
[transform3Weight, transform3Post],
|
||||||
|
];
|
||||||
|
|
||||||
|
export const paramsPost: RenderParams = {
|
||||||
|
quality: 1,
|
||||||
|
renderer: (size) => new RendererFlame(size, transformAllPost),
|
||||||
|
};
|
35
posts/2023/06/flam3/2c-final.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { RenderParams } from "./0-utility.js";
|
||||||
|
import { julia, identityCoefs, RendererFlame } from "./2a-baseline.js";
|
||||||
|
import { TransformPost, transformAllPost } from "./2b-post.js";
|
||||||
|
|
||||||
|
export class RendererFinal extends RendererFlame {
|
||||||
|
constructor(
|
||||||
|
size: number,
|
||||||
|
transforms: [number, TransformPost][],
|
||||||
|
public readonly final: TransformPost
|
||||||
|
) {
|
||||||
|
super(size, transforms);
|
||||||
|
}
|
||||||
|
|
||||||
|
plot(x: number, y: number): void {
|
||||||
|
super.plot(...this.final.apply(x, y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const transformFinal = new TransformPost(
|
||||||
|
{
|
||||||
|
a: 2,
|
||||||
|
b: 0,
|
||||||
|
c: 0,
|
||||||
|
d: 0,
|
||||||
|
e: 2,
|
||||||
|
f: 0,
|
||||||
|
},
|
||||||
|
[[1, julia]],
|
||||||
|
identityCoefs
|
||||||
|
);
|
||||||
|
|
||||||
|
export const paramsFinal: RenderParams = {
|
||||||
|
quality: 1,
|
||||||
|
renderer: (size) => new RendererFinal(size, transformAllPost, transformFinal),
|
||||||
|
};
|
67
posts/2023/06/flam3/3a-binary.ts
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import { RenderParams, histIndex, imageIndex } from "./0-utility.js";
|
||||||
|
import {
|
||||||
|
camera,
|
||||||
|
transform1Weight,
|
||||||
|
transform2Weight,
|
||||||
|
transform3Weight,
|
||||||
|
} from "./2a-baseline.js";
|
||||||
|
import {
|
||||||
|
TransformPost,
|
||||||
|
transform1Post,
|
||||||
|
transform2Post,
|
||||||
|
transform3Post,
|
||||||
|
transformAllPost,
|
||||||
|
} from "./2b-post.js";
|
||||||
|
import { RendererFinal, transformFinal } from "./2c-final.js";
|
||||||
|
|
||||||
|
export class RendererHistogram extends RendererFinal {
|
||||||
|
protected histogram: number[] = [];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
size: number,
|
||||||
|
transforms: [number, TransformPost][],
|
||||||
|
final: TransformPost
|
||||||
|
) {
|
||||||
|
super(size, transforms, final);
|
||||||
|
|
||||||
|
for (var i = 0; i < this.size * this.size; i++) {
|
||||||
|
this.histogram.push(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
plot(x: number, y: number): void {
|
||||||
|
[x, y] = this.final.apply(x, y);
|
||||||
|
const [pixelX, pixelY] = camera(x, y, this.size);
|
||||||
|
|
||||||
|
if (
|
||||||
|
pixelX < 0 ||
|
||||||
|
pixelY >= this.size ||
|
||||||
|
pixelY < 0 ||
|
||||||
|
pixelY >= this.size
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hIndex = histIndex(pixelX, pixelY, this.size);
|
||||||
|
this.histogram[hIndex] += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
render(image: ImageData): void {
|
||||||
|
for (var x = 0; x < this.size; x++) {
|
||||||
|
for (var y = 0; y < this.size; y++) {
|
||||||
|
const hIndex = histIndex(x, y, this.size);
|
||||||
|
const iIndex = imageIndex(x, y, this.size);
|
||||||
|
image.data[iIndex + 0] = 0;
|
||||||
|
image.data[iIndex + 1] = 0;
|
||||||
|
image.data[iIndex + 2] = 0;
|
||||||
|
image.data[iIndex + 3] = (this.histogram[hIndex] > 0 ? 1 : 0) * 0xff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const paramsBinary: RenderParams = {
|
||||||
|
quality: 1,
|
||||||
|
renderer: (size) =>
|
||||||
|
new RendererHistogram(size, transformAllPost, transformFinal),
|
||||||
|
};
|
30
posts/2023/06/flam3/3b-linear.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { RenderParams, histIndex, imageIndex } from "./0-utility.js";
|
||||||
|
import { transformAllPost } from "./2b-post.js";
|
||||||
|
import { transformFinal } from "./2c-final.js";
|
||||||
|
import { RendererHistogram } from "./3a-binary.js";
|
||||||
|
|
||||||
|
class RendererLinear extends RendererHistogram {
|
||||||
|
render(image: ImageData): void {
|
||||||
|
const maxHistogram = this.histogram.reduce(
|
||||||
|
(max, v) => Math.max(max, v),
|
||||||
|
-Infinity
|
||||||
|
);
|
||||||
|
|
||||||
|
for (var x = 0; x < this.size; x++) {
|
||||||
|
for (var y = 0; y < this.size; y++) {
|
||||||
|
const hIndex = histIndex(x, y, this.size);
|
||||||
|
const iIndex = imageIndex(x, y, this.size);
|
||||||
|
image.data[iIndex + 0] = 0;
|
||||||
|
image.data[iIndex + 1] = 0;
|
||||||
|
image.data[iIndex + 2] = 0;
|
||||||
|
image.data[iIndex + 3] = (this.histogram[hIndex] / maxHistogram) * 0xff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const paramsLinear: RenderParams = {
|
||||||
|
quality: 10,
|
||||||
|
renderer: (size) =>
|
||||||
|
new RendererLinear(size, transformAllPost, transformFinal),
|
||||||
|
};
|
33
posts/2023/06/flam3/3c-logarithmic.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { RenderParams, histIndex, imageIndex } from "./0-utility.js";
|
||||||
|
import { transformAllPost } from "./2b-post.js";
|
||||||
|
import { transformFinal } from "./2c-final.js";
|
||||||
|
import { RendererHistogram } from "./3a-binary.js";
|
||||||
|
|
||||||
|
export class RendererLogarithmic extends RendererHistogram {
|
||||||
|
render(image: ImageData): void {
|
||||||
|
// Because log(0) is -Infinity, all the math actually works out.
|
||||||
|
const histogramLog = this.histogram.map(Math.log);
|
||||||
|
const histogramLogMax = histogramLog.reduce(
|
||||||
|
(max, v) => Math.max(max, v),
|
||||||
|
-Infinity
|
||||||
|
);
|
||||||
|
|
||||||
|
for (var x = 0; x < this.size; x++) {
|
||||||
|
for (var y = 0; y < this.size; y++) {
|
||||||
|
const hIndex = histIndex(x, y, this.size);
|
||||||
|
const iIndex = imageIndex(x, y, this.size);
|
||||||
|
image.data[iIndex + 0] = 0;
|
||||||
|
image.data[iIndex + 1] = 0;
|
||||||
|
image.data[iIndex + 2] = 0;
|
||||||
|
image.data[iIndex + 3] =
|
||||||
|
(histogramLog[hIndex] / histogramLogMax) * 0xff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const paramsLogarithmic: RenderParams = {
|
||||||
|
quality: 10,
|
||||||
|
renderer: (size) =>
|
||||||
|
new RendererLogarithmic(size, transformAllPost, transformFinal),
|
||||||
|
};
|
192
posts/2023/06/flam3/4-color.ts
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
import {
|
||||||
|
RenderParams,
|
||||||
|
histIndex,
|
||||||
|
imageIndex,
|
||||||
|
weightedChoice,
|
||||||
|
} from "./0-utility.js";
|
||||||
|
import {
|
||||||
|
Coefs,
|
||||||
|
Variation,
|
||||||
|
camera,
|
||||||
|
transform1Weight,
|
||||||
|
transform2Weight,
|
||||||
|
transform3Weight,
|
||||||
|
} from "./2a-baseline.js";
|
||||||
|
import {
|
||||||
|
TransformPost,
|
||||||
|
transform1Post,
|
||||||
|
transform2Post,
|
||||||
|
transform3Post,
|
||||||
|
} from "./2b-post.js";
|
||||||
|
import { RendererFinal, transformFinal } from "./2c-final.js";
|
||||||
|
|
||||||
|
export class TransformColor extends TransformPost {
|
||||||
|
constructor(
|
||||||
|
coefs: Coefs,
|
||||||
|
variations: [number, Variation][],
|
||||||
|
post: Coefs,
|
||||||
|
public readonly color: number
|
||||||
|
) {
|
||||||
|
super(coefs, variations, post);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RendererColor extends RendererFinal {
|
||||||
|
protected color: number = Math.random();
|
||||||
|
protected red: number[] = [];
|
||||||
|
protected green: number[] = [];
|
||||||
|
protected blue: number[] = [];
|
||||||
|
protected alpha: number[] = [];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
size: number,
|
||||||
|
transforms: [number, TransformColor][],
|
||||||
|
final: TransformColor,
|
||||||
|
private readonly palette: number[]
|
||||||
|
) {
|
||||||
|
super(size, transforms, final);
|
||||||
|
for (var i = 0; i < this.size * this.size; i++) {
|
||||||
|
this.red.push(0);
|
||||||
|
this.green.push(0);
|
||||||
|
this.blue.push(0);
|
||||||
|
this.alpha.push(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
colorFromIndex(c: number): [number, number, number] {
|
||||||
|
// A smarter coloring implementation would interpolate between points in the palette,
|
||||||
|
// but we'll use a step function here to keep things simple
|
||||||
|
const colorIndex = Math.floor(c * (this.palette.length / 3)) * 3;
|
||||||
|
return [
|
||||||
|
paletteNumber[colorIndex + 0],
|
||||||
|
paletteNumber[colorIndex + 1],
|
||||||
|
paletteNumber[colorIndex + 2],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
plotColor(x: number, y: number, color: number): void {
|
||||||
|
const [finalX, finalY] = this.final.apply(x, y);
|
||||||
|
const [pixelX, pixelY] = camera(finalX, finalY, this.size);
|
||||||
|
if (pixelX < 0 || pixelX > this.size || pixelY < 0 || pixelY > this.size) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: The reference parameters use a final `symmetry` of 1,
|
||||||
|
// which effectively disables the final transform's contribution
|
||||||
|
// to color mixing (see the `color_speed` in flam3).
|
||||||
|
// While we'd normally want to apply the same color transformation
|
||||||
|
// like we do in the `run` method, it is skipped here so the output
|
||||||
|
// image matches the reference image
|
||||||
|
//
|
||||||
|
// const finalColor = (color + (final as TransformColor).color) / 2
|
||||||
|
const finalColor = color;
|
||||||
|
const [r, g, b] = this.colorFromIndex(finalColor);
|
||||||
|
|
||||||
|
const hIndex = histIndex(pixelX, pixelY, this.size);
|
||||||
|
this.red[hIndex] += r;
|
||||||
|
this.green[hIndex] += g;
|
||||||
|
this.blue[hIndex] += b;
|
||||||
|
this.alpha[hIndex] += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
run(quality: number): void {
|
||||||
|
const iterations = quality * this.size * this.size;
|
||||||
|
for (var i = 0; i < iterations; i++) {
|
||||||
|
const [_, transform] = weightedChoice(this.transforms);
|
||||||
|
[this.x, this.y] = transform.apply(this.x, this.y);
|
||||||
|
this.color = (this.color + (transform as TransformColor).color) / 2;
|
||||||
|
|
||||||
|
if (i > 20) {
|
||||||
|
this.plotColor(this.x, this.y, this.color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render(image: ImageData): void {
|
||||||
|
for (var x = 0; x < image.width; x++) {
|
||||||
|
for (var y = 0; y < image.height; y++) {
|
||||||
|
const hIndex = histIndex(x, y, image.width);
|
||||||
|
|
||||||
|
// NOTE: Calculating the scaling factor for accumulated color value to final
|
||||||
|
// pixel coloring is very involved (gamma, vibrancy, etc.). This scaling implementation
|
||||||
|
// is only intended to approximate the reference parameters.
|
||||||
|
const aScale =
|
||||||
|
Math.log10(this.alpha[hIndex]) / (this.alpha[hIndex] * 1.5);
|
||||||
|
|
||||||
|
const iIdx = imageIndex(x, y, this.size);
|
||||||
|
image.data[iIdx + 0] = this.red[hIndex] * aScale * 0xff;
|
||||||
|
image.data[iIdx + 1] = this.green[hIndex] * aScale * 0xff;
|
||||||
|
image.data[iIdx + 2] = this.blue[hIndex] * aScale * 0xff;
|
||||||
|
image.data[iIdx + 3] = this.alpha[hIndex] * aScale * 0xff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const transform1ColorValue = 0;
|
||||||
|
export const transform1Color = new TransformColor(
|
||||||
|
transform1Post.coefs,
|
||||||
|
transform1Post.variations,
|
||||||
|
transform1Post.post,
|
||||||
|
transform1ColorValue
|
||||||
|
);
|
||||||
|
export const transform2ColorValue = 0.844;
|
||||||
|
export const transform2Color = new TransformColor(
|
||||||
|
transform2Post.coefs,
|
||||||
|
transform2Post.variations,
|
||||||
|
transform2Post.post,
|
||||||
|
transform2ColorValue
|
||||||
|
);
|
||||||
|
|
||||||
|
export const transform3ColorValue = 0.349;
|
||||||
|
export const transform3Color = new TransformColor(
|
||||||
|
transform3Post.coefs,
|
||||||
|
transform3Post.variations,
|
||||||
|
transform3Post.post,
|
||||||
|
transform3ColorValue
|
||||||
|
);
|
||||||
|
|
||||||
|
export const transformFinalColorValue = 0;
|
||||||
|
export const transformFinalColor = new TransformColor(
|
||||||
|
transformFinal.coefs,
|
||||||
|
transformFinal.variations,
|
||||||
|
transformFinal.post,
|
||||||
|
transformFinalColorValue
|
||||||
|
);
|
||||||
|
|
||||||
|
// Copied from the reference parameters
|
||||||
|
export const paletteHex =
|
||||||
|
"7E3037762C45722B496E2A4E6A29506728536527546326565C265C5724595322574D2155482153462050451F4E441E4D431E4C3F1E473F1E453F1E433F1E3F3F1E3B3E1E393E1E37421D36431C38451C3A471B3B491B3C4A1A3C4B1A3D4D1A3E4F19405318435517445817465A16475D15495E154960154A65134E6812506B12526E1153711055720F55740F55770E577A0E59810C58840B58880A588B09588F08589107569307559A05539D0451A1034FA5024BA90147AA0046AC0045B00242B4043DBB0634BE082EC20A29C30B27C50C26C90F1DCC1116D32110D6280EDA300CDC380ADF4109E04508E24A08E45106E75704EA6402EC6B01EE7300EE7600EF7A00F07E00F18300F29000F29300F39600F39900F39C00F3A000F3A100F3A201F2A502F1A805F0A906EFAA08EEA909EEA80AEDA60CEBA50FE5A313E1A113DD9F13DB9E13D99D14D49C15D09815CC9518C79318BE8B1ABB891BB9871DB4811FB07D1FAB7621A671239C6227975C289256299053298E502A89482C853F2D803A2E7E3037762C45742B47722B496E2A4E6A29516728536326565C265C5724595322575022564E2255482153452050451F4E431E4C3F1E473E1D463D1D453F1E43411E413F1E3B3E1E37421D36421D38431D3B451C3A471B3A491B3C4B1A3D4D1A3E4F19405318435418445518455817465A16475D154960154A65134E66124F6812506B12526E1153711055740F55770E577A0E597E0D57810C58840B58880A588B09588F08589307559A05539C04529E0452A1034FA5024BA90147AC0045B00242B4043DB7053ABB0634BE0831C20A29C50C26C90F1DCC1116D01711D32110D72A0EDA300CDD390ADF4109E24A08E45106E75704E95F03EA6402EC6C01EE7300EF7A00F07E00F18300F28900F29000F39300F39600F39C00F3A000F3A100F3A201F2A502F2A503F1A805F0A807EFAA08EEA80AEDA60CEBA50FE9A411E5A313E1A113DD9F13D99D14D49C15D09815CC9518C79318C38F1ABE8B1AB9871DB4811FB07D1FAB7621A67123A16A249C6227975E289256298E502A89482C853F2D803A2E";
|
||||||
|
|
||||||
|
// https://stackoverflow.com/a/34356351
|
||||||
|
export function hexToBytes(hex: string) {
|
||||||
|
var bytes = [];
|
||||||
|
for (var i = 0; i < hex.length; i += 2) {
|
||||||
|
bytes.push(parseInt(hex.substring(i, i + 2), 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const paletteBytes = hexToBytes(paletteHex);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Re-scale pixel color values to the range [0, 1], done to match
|
||||||
|
* 'flam3_get_palette'
|
||||||
|
*/
|
||||||
|
export const paletteNumber = paletteBytes.map((b) => b / 0xff);
|
||||||
|
|
||||||
|
export const paramsColor: RenderParams = {
|
||||||
|
quality: 30,
|
||||||
|
renderer: (size) =>
|
||||||
|
new RendererColor(
|
||||||
|
size,
|
||||||
|
[
|
||||||
|
[transform1Weight, transform1Color],
|
||||||
|
[transform2Weight, transform2Color],
|
||||||
|
[transform3Weight, transform3Color],
|
||||||
|
],
|
||||||
|
transformFinalColor,
|
||||||
|
paletteNumber
|
||||||
|
),
|
||||||
|
};
|
49
posts/2023/06/flam3/5a-gasket.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import { RenderParams } from "./0-utility.js";
|
||||||
|
import { RendererFlame, Transform, linear } from "./2a-baseline.js";
|
||||||
|
|
||||||
|
export const transformGasket1 = new Transform(
|
||||||
|
{
|
||||||
|
a: 0.5,
|
||||||
|
b: 0,
|
||||||
|
c: 0,
|
||||||
|
d: 0,
|
||||||
|
e: 0.5,
|
||||||
|
f: 0,
|
||||||
|
},
|
||||||
|
[[1, linear]]
|
||||||
|
);
|
||||||
|
|
||||||
|
export const transformGasket2 = new Transform(
|
||||||
|
{
|
||||||
|
a: 0.5,
|
||||||
|
b: 0,
|
||||||
|
c: 0.5,
|
||||||
|
d: 0,
|
||||||
|
e: 0.5,
|
||||||
|
f: 0,
|
||||||
|
},
|
||||||
|
[[1, linear]]
|
||||||
|
);
|
||||||
|
|
||||||
|
export const transformGasket3 = new Transform(
|
||||||
|
{
|
||||||
|
a: 0.5,
|
||||||
|
b: 0,
|
||||||
|
c: 0,
|
||||||
|
d: 0,
|
||||||
|
e: 0.5,
|
||||||
|
f: 0.5,
|
||||||
|
},
|
||||||
|
[[1, linear]]
|
||||||
|
);
|
||||||
|
|
||||||
|
export const transformGasket: [number, Transform][] = [
|
||||||
|
[1 / 3, transformGasket1],
|
||||||
|
[1 / 3, transformGasket2],
|
||||||
|
[1 / 3, transformGasket3],
|
||||||
|
];
|
||||||
|
|
||||||
|
export const paramsGasketFlame: RenderParams = {
|
||||||
|
quality: 1,
|
||||||
|
renderer: (size) => new RendererFlame(size, transformGasket),
|
||||||
|
};
|
46
posts/2023/06/flam3/5b-solo.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { RenderParams, weightedChoice } from "./0-utility.js";
|
||||||
|
import { TransformPost, transformAllPost } from "./2b-post.js";
|
||||||
|
import { transformFinal } from "./2c-final.js";
|
||||||
|
import { RendererLogarithmic } from "./3c-logarithmic.js";
|
||||||
|
|
||||||
|
export class RendererSolo extends RendererLogarithmic {
|
||||||
|
constructor(
|
||||||
|
size: number,
|
||||||
|
transforms: [number, TransformPost][],
|
||||||
|
final: TransformPost,
|
||||||
|
private readonly transformSolo: number
|
||||||
|
) {
|
||||||
|
super(size, transforms, final);
|
||||||
|
}
|
||||||
|
|
||||||
|
run(quality: number): void {
|
||||||
|
const iterations = quality * this.size * this.size;
|
||||||
|
for (var i = 0; i < iterations; i++) {
|
||||||
|
const [transformIndex, transform] = weightedChoice(this.transforms);
|
||||||
|
[this.x, this.y] = transform.apply(this.x, this.y);
|
||||||
|
|
||||||
|
// NOTE: Only plot if the current point is from the solo transform
|
||||||
|
if (i > 20 && transformIndex == this.transformSolo) {
|
||||||
|
this.plot(this.x, this.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const paramsSolo1: RenderParams = {
|
||||||
|
quality: 10,
|
||||||
|
renderer: (size) =>
|
||||||
|
new RendererSolo(size, transformAllPost, transformFinal, 0),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const paramsSolo2: RenderParams = {
|
||||||
|
quality: 10,
|
||||||
|
renderer: (size) =>
|
||||||
|
new RendererSolo(size, transformAllPost, transformFinal, 1),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const paramsSolo3: RenderParams = {
|
||||||
|
quality: 10,
|
||||||
|
renderer: (size) =>
|
||||||
|
new RendererSolo(size, transformAllPost, transformFinal, 2),
|
||||||
|
};
|
BIN
posts/2023/06/flam3/images/1-gasket.png
Normal file
After Width: | Height: | Size: 6.4 KiB |
BIN
posts/2023/06/flam3/images/2a-baseline.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
posts/2023/06/flam3/images/2b-post.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
posts/2023/06/flam3/images/2c-final.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
posts/2023/06/flam3/images/3a-binary.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
posts/2023/06/flam3/images/3b-linear.png
Normal file
After Width: | Height: | Size: 73 KiB |
BIN
posts/2023/06/flam3/images/3c-logarithmic.png
Normal file
After Width: | Height: | Size: 116 KiB |
BIN
posts/2023/06/flam3/images/4-color.png
Normal file
After Width: | Height: | Size: 272 KiB |
BIN
posts/2023/06/flam3/images/5a-gasket.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
posts/2023/06/flam3/images/5b-solo1.png
Normal file
After Width: | Height: | Size: 88 KiB |
BIN
posts/2023/06/flam3/images/5b-solo2.png
Normal file
After Width: | Height: | Size: 6.0 KiB |
BIN
posts/2023/06/flam3/images/5b-solo3.png
Normal file
After Width: | Height: | Size: 47 KiB |
15
posts/2023/06/flam3/index.tsx
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import Blog from "../../../LayoutBlog";
|
||||||
|
import { CanvasColor } from "./0-canvas.js";
|
||||||
|
|
||||||
|
export default function () {
|
||||||
|
const Layout = Blog({
|
||||||
|
title: "The fractal flame algorithm",
|
||||||
|
description: "Explaining the paper",
|
||||||
|
published: "2023-06-25",
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<Layout>
|
||||||
|
<CanvasColor />
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
}
|
45
posts/2023/06/flam3/offline.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import * as fs from "fs/promises";
|
||||||
|
import { createCanvas } from "canvas";
|
||||||
|
import { DEFAULT_SIZE, RenderParams } from "./0-utility.js";
|
||||||
|
import { paramsGasket } from "./1-gasket.js";
|
||||||
|
import { paramsBaseline } from "./2a-baseline.js";
|
||||||
|
import { paramsPost } from "./2b-post.js";
|
||||||
|
import { paramsFinal } from "./2c-final.js";
|
||||||
|
import { paramsBinary } from "./3a-binary.js";
|
||||||
|
import { paramsLinear } from "./3b-linear.js";
|
||||||
|
import { paramsLogarithmic } from "./3c-logarithmic.js";
|
||||||
|
import { paramsColor } from "./4-color.js";
|
||||||
|
import { paramsGasketFlame } from "./5a-gasket.js";
|
||||||
|
import { paramsSolo1, paramsSolo2, paramsSolo3 } from "./5b-solo.js";
|
||||||
|
|
||||||
|
export type OfflineParams = RenderParams & { filename: string };
|
||||||
|
|
||||||
|
const paramsAll: OfflineParams[] = [
|
||||||
|
{ filename: "images/1-gasket.png", ...paramsGasket },
|
||||||
|
{ filename: "images/2a-baseline.png", ...paramsBaseline },
|
||||||
|
{ filename: "images/2b-post.png", ...paramsPost },
|
||||||
|
{ filename: "images/2c-final.png", ...paramsFinal },
|
||||||
|
{ filename: "images/3a-binary.png", ...paramsBinary },
|
||||||
|
{ filename: "images/3b-linear.png", ...paramsLinear },
|
||||||
|
{ filename: "images/3c-logarithmic.png", ...paramsLogarithmic },
|
||||||
|
{ filename: "images/4-color.png", ...paramsColor },
|
||||||
|
{ filename: "images/5a-gasket.png", ...paramsGasketFlame },
|
||||||
|
{ filename: "images/5b-solo1.png", ...paramsSolo1 },
|
||||||
|
{ filename: "images/5b-solo2.png", ...paramsSolo2 },
|
||||||
|
{ filename: "images/5b-solo3.png", ...paramsSolo3 },
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const param of paramsAll) {
|
||||||
|
const render = param.renderer(DEFAULT_SIZE);
|
||||||
|
render.run(param.quality);
|
||||||
|
|
||||||
|
const canvas = createCanvas(DEFAULT_SIZE, DEFAULT_SIZE);
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
const data = ctx.createImageData(DEFAULT_SIZE, DEFAULT_SIZE);
|
||||||
|
|
||||||
|
render.render(data as any);
|
||||||
|
ctx.putImageData(data, 0, 0);
|
||||||
|
|
||||||
|
const buffer = canvas.toBuffer();
|
||||||
|
await fs.writeFile(param.filename, buffer);
|
||||||
|
}
|
659
posts/2023/06/flam3/package-lock.json
generated
Normal file
@ -0,0 +1,659 @@
|
|||||||
|
{
|
||||||
|
"name": "blog-flam3",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "blog-flam3",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"canvas": "^2.11.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^5.1.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@mapbox/node-pre-gyp": {
|
||||||
|
"version": "1.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz",
|
||||||
|
"integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"detect-libc": "^2.0.0",
|
||||||
|
"https-proxy-agent": "^5.0.0",
|
||||||
|
"make-dir": "^3.1.0",
|
||||||
|
"node-fetch": "^2.6.7",
|
||||||
|
"nopt": "^5.0.0",
|
||||||
|
"npmlog": "^5.0.1",
|
||||||
|
"rimraf": "^3.0.2",
|
||||||
|
"semver": "^7.3.5",
|
||||||
|
"tar": "^6.1.11"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"node-pre-gyp": "bin/node-pre-gyp"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/abbrev": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
|
||||||
|
},
|
||||||
|
"node_modules/agent-base": {
|
||||||
|
"version": "6.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
|
||||||
|
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"debug": "4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ansi-regex": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/aproba": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ=="
|
||||||
|
},
|
||||||
|
"node_modules/are-we-there-yet": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==",
|
||||||
|
"dependencies": {
|
||||||
|
"delegates": "^1.0.0",
|
||||||
|
"readable-stream": "^3.6.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/balanced-match": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
||||||
|
},
|
||||||
|
"node_modules/brace-expansion": {
|
||||||
|
"version": "1.1.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||||
|
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||||
|
"dependencies": {
|
||||||
|
"balanced-match": "^1.0.0",
|
||||||
|
"concat-map": "0.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/canvas": {
|
||||||
|
"version": "2.11.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.2.tgz",
|
||||||
|
"integrity": "sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@mapbox/node-pre-gyp": "^1.0.0",
|
||||||
|
"nan": "^2.17.0",
|
||||||
|
"simple-get": "^3.0.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/chownr": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/color-support": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
|
||||||
|
"bin": {
|
||||||
|
"color-support": "bin.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/concat-map": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
|
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
|
||||||
|
},
|
||||||
|
"node_modules/console-control-strings": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ=="
|
||||||
|
},
|
||||||
|
"node_modules/debug": {
|
||||||
|
"version": "4.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||||
|
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"ms": "2.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"supports-color": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/decompress-response": {
|
||||||
|
"version": "4.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz",
|
||||||
|
"integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==",
|
||||||
|
"dependencies": {
|
||||||
|
"mimic-response": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/delegates": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ=="
|
||||||
|
},
|
||||||
|
"node_modules/detect-libc": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/emoji-regex": {
|
||||||
|
"version": "8.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||||
|
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||||
|
},
|
||||||
|
"node_modules/fs-minipass": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
|
||||||
|
"dependencies": {
|
||||||
|
"minipass": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fs-minipass/node_modules/minipass": {
|
||||||
|
"version": "3.3.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
|
||||||
|
"integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
|
||||||
|
"dependencies": {
|
||||||
|
"yallist": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fs.realpath": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
|
||||||
|
},
|
||||||
|
"node_modules/gauge": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"aproba": "^1.0.3 || ^2.0.0",
|
||||||
|
"color-support": "^1.1.2",
|
||||||
|
"console-control-strings": "^1.0.0",
|
||||||
|
"has-unicode": "^2.0.1",
|
||||||
|
"object-assign": "^4.1.1",
|
||||||
|
"signal-exit": "^3.0.0",
|
||||||
|
"string-width": "^4.2.3",
|
||||||
|
"strip-ansi": "^6.0.1",
|
||||||
|
"wide-align": "^1.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/glob": {
|
||||||
|
"version": "7.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||||
|
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"fs.realpath": "^1.0.0",
|
||||||
|
"inflight": "^1.0.4",
|
||||||
|
"inherits": "2",
|
||||||
|
"minimatch": "^3.1.1",
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"path-is-absolute": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/has-unicode": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="
|
||||||
|
},
|
||||||
|
"node_modules/https-proxy-agent": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
|
||||||
|
"dependencies": {
|
||||||
|
"agent-base": "6",
|
||||||
|
"debug": "4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/inflight": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||||
|
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
|
||||||
|
"dependencies": {
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"wrappy": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/inherits": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||||
|
},
|
||||||
|
"node_modules/is-fullwidth-code-point": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lru-cache": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||||
|
"dependencies": {
|
||||||
|
"yallist": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/make-dir": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
|
||||||
|
"dependencies": {
|
||||||
|
"semver": "^6.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/make-dir/node_modules/semver": {
|
||||||
|
"version": "6.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||||
|
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||||
|
"bin": {
|
||||||
|
"semver": "bin/semver.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mimic-response": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/minimatch": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||||
|
"dependencies": {
|
||||||
|
"brace-expansion": "^1.1.7"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/minipass": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/minizlib": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
|
||||||
|
"dependencies": {
|
||||||
|
"minipass": "^3.0.0",
|
||||||
|
"yallist": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/minizlib/node_modules/minipass": {
|
||||||
|
"version": "3.3.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
|
||||||
|
"integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
|
||||||
|
"dependencies": {
|
||||||
|
"yallist": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mkdirp": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
|
||||||
|
"bin": {
|
||||||
|
"mkdirp": "bin/cmd.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ms": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
|
},
|
||||||
|
"node_modules/nan": {
|
||||||
|
"version": "2.17.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz",
|
||||||
|
"integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ=="
|
||||||
|
},
|
||||||
|
"node_modules/node-fetch": {
|
||||||
|
"version": "2.6.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz",
|
||||||
|
"integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==",
|
||||||
|
"dependencies": {
|
||||||
|
"whatwg-url": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "4.x || >=6.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"encoding": "^0.1.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"encoding": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/nopt": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"abbrev": "1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"nopt": "bin/nopt.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/npmlog": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==",
|
||||||
|
"dependencies": {
|
||||||
|
"are-we-there-yet": "^2.0.0",
|
||||||
|
"console-control-strings": "^1.1.0",
|
||||||
|
"gauge": "^3.0.0",
|
||||||
|
"set-blocking": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/object-assign": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
|
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/once": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||||
|
"dependencies": {
|
||||||
|
"wrappy": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/path-is-absolute": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/readable-stream": {
|
||||||
|
"version": "3.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||||
|
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||||
|
"dependencies": {
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"string_decoder": "^1.1.1",
|
||||||
|
"util-deprecate": "^1.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/rimraf": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||||
|
"dependencies": {
|
||||||
|
"glob": "^7.1.3"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"rimraf": "bin.js"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/safe-buffer": {
|
||||||
|
"version": "5.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||||
|
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/semver": {
|
||||||
|
"version": "7.5.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
|
||||||
|
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
|
||||||
|
"dependencies": {
|
||||||
|
"lru-cache": "^6.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"semver": "bin/semver.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/set-blocking": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
|
||||||
|
},
|
||||||
|
"node_modules/signal-exit": {
|
||||||
|
"version": "3.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
||||||
|
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
|
||||||
|
},
|
||||||
|
"node_modules/simple-concat": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/simple-get": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==",
|
||||||
|
"dependencies": {
|
||||||
|
"decompress-response": "^4.2.0",
|
||||||
|
"once": "^1.3.1",
|
||||||
|
"simple-concat": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/string_decoder": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": "~5.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/string-width": {
|
||||||
|
"version": "4.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||||
|
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||||
|
"dependencies": {
|
||||||
|
"emoji-regex": "^8.0.0",
|
||||||
|
"is-fullwidth-code-point": "^3.0.0",
|
||||||
|
"strip-ansi": "^6.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/strip-ansi": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-regex": "^5.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tar": {
|
||||||
|
"version": "6.1.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/tar/-/tar-6.1.15.tgz",
|
||||||
|
"integrity": "sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==",
|
||||||
|
"dependencies": {
|
||||||
|
"chownr": "^2.0.0",
|
||||||
|
"fs-minipass": "^2.0.0",
|
||||||
|
"minipass": "^5.0.0",
|
||||||
|
"minizlib": "^2.1.1",
|
||||||
|
"mkdirp": "^1.0.3",
|
||||||
|
"yallist": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tr46": {
|
||||||
|
"version": "0.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||||
|
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
|
||||||
|
},
|
||||||
|
"node_modules/typescript": {
|
||||||
|
"version": "5.1.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz",
|
||||||
|
"integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"tsc": "bin/tsc",
|
||||||
|
"tsserver": "bin/tsserver"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.17"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/util-deprecate": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
||||||
|
},
|
||||||
|
"node_modules/webidl-conversions": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
|
||||||
|
},
|
||||||
|
"node_modules/whatwg-url": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||||
|
"dependencies": {
|
||||||
|
"tr46": "~0.0.3",
|
||||||
|
"webidl-conversions": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/wide-align": {
|
||||||
|
"version": "1.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
|
||||||
|
"integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
|
||||||
|
"dependencies": {
|
||||||
|
"string-width": "^1.0.2 || 2 || 3 || 4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/wrappy": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
||||||
|
},
|
||||||
|
"node_modules/yallist": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
posts/2023/06/flam3/package.json
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "blog-flam3",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"render": "tsc && node ./dist/offline.js"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"canvas": "^2.11.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^5.1.6"
|
||||||
|
}
|
||||||
|
}
|
14
posts/2023/06/flam3/tsconfig.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"module": "ESNext",
|
||||||
|
"lib": ["DOM", "ES2021"],
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"outDir": "./dist"
|
||||||
|
},
|
||||||
|
"exclude": ["*.tsx"]
|
||||||
|
}
|
120
posts/2023/06/flam3/variations.flame
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
<Flames name="variations">
|
||||||
|
<flame name="post xform" version="Apophysis 2.08 beta" size="600 600" center="0 0" scale="150" oversample="1" filter="0.2" quality="1" background="0 0 0" brightness="4" gamma="4" >
|
||||||
|
<xform weight="0.422330042096567" color="0" pdj="1" coefs="1.51523 0.740356 -3.048677 -1.455964 0.724135 -0.362059" pdj_a="1.09358" pdj_b="2.13048" pdj_c="2.54127" pdj_d="2.37267" />
|
||||||
|
<xform weight="0.564534951145298" color="0" julia="1" coefs="-1.381068 1.381068 -1.381068 -1.381068 0 0" />
|
||||||
|
<xform weight="0.0131350067581356" color="0" linear="1" popcorn="1" coefs="0.031393 -0.031367 0.031367 0.031393 0 0" post="1 0 0 1 0.241352 0.271521" />
|
||||||
|
<palette count="256" format="RGB">
|
||||||
|
3A78875998AA5E9DAC78B1C2599BAB36798A2252601B3438
|
||||||
|
1823270D1215080705010101000000000002080A090A0809
|
||||||
|
0C070D0B090A030406010101000000000000000000000000
|
||||||
|
0A00000B0A080E1213101B1F21202830243A6737357A3C31
|
||||||
|
864424643A22452F1838251427190E1C12080E0F110E1213
|
||||||
|
1014152110183720105D320FA0531F9144180409080A1312
|
||||||
|
0C13140E13160E15160E17160F16171015180B161C0A1225
|
||||||
|
0A0F2F101E37172E40294C5A3B6B7549798758879975A9BE
|
||||||
|
79A7BF7EA6C0949FA2AA9985B7A27BC4AB72AC965A867654
|
||||||
|
61574E4C4D48374343474141573F3F7C5C36B0914EC1DFF9
|
||||||
|
C4E4FAC8E9FCBEE1F4B5DAEDB2D8EDB0D6ED5398A7386D78
|
||||||
|
1D424A1B3B4219343B1B383E1D3C411D3B462155623D7C8B
|
||||||
|
46747F4F6C74636454785C3584663E917047BEA467CEA86A
|
||||||
|
DEAC6DC5975EAC834F916E41765A335F3D21431F21241625
|
||||||
|
1F202B1A2B321A2D321B30331B323A1628360E1D220E1D21
|
||||||
|
0F1D20101C1F111C1E111D1E121E1E2B21153B2B1B725432
|
||||||
|
85542C9854279B63369F7346AD7C3AB2763AB18F4FB39453
|
||||||
|
B69957B99B56BC9E56C19651CB9346AB6A2A9851254E341D
|
||||||
|
2F261B10181A0E15160C12120D11120A10100D0D0D0C0E0E
|
||||||
|
0B0F100B10120C11140F191A101F221829331A373B1E3D52
|
||||||
|
1A40551744591D556420424C1E3B431D3C41112C33102328
|
||||||
|
101B1D10191E111820101D2311242A1B33371B3A3F276476
|
||||||
|
3E637D556284545F7D7759355C41261B30290E16180B0F0F
|
||||||
|
0908060405030002010A0E0F12171A1C1B2B17343C3C7481
|
||||||
|
467F8F508A9E528FA23E81923769722E69772248512B545E
|
||||||
|
35616C688589807F85939FB5ABD6E6B3D6EA89B7CE5891A4
|
||||||
|
467E92356C81194A6B1A373F132C310E1C1F050409020205
|
||||||
|
0000020000000101010800000B0000170A000D0D0D0D1110
|
||||||
|
0F0E14100F141F11082619082F1904210F05111717101919
|
||||||
|
0F1B1B101F22182C2B252E2B282D311B2E321A2E2F162E30
|
||||||
|
1325270E191B0F1314190D0F2E1211461A27552227612723
|
||||||
|
6C303A56213D3033381C343619343B15383E193A431A4E5C
|
||||||
|
</palette>
|
||||||
|
</flame>
|
||||||
|
<flame name="baseline" version="Apophysis 2.08 beta" size="600 600" center="0 0" scale="150" oversample="1" filter="0.2" quality="1" background="0 0 0" brightness="4" gamma="4" >
|
||||||
|
<xform weight="0.422330042096567" color="0" pdj="1" coefs="1.51523 0.740356 -3.048677 -1.455964 0.724135 -0.362059" pdj_a="1.09358" pdj_b="2.13048" pdj_c="2.54127" pdj_d="2.37267" />
|
||||||
|
<xform weight="0.564534951145298" color="0.13" julia="1" coefs="-1.381068 1.381068 -1.381068 -1.381068 0 0" />
|
||||||
|
<xform weight="0.0131350067581356" color="0.844" linear="1" popcorn="1" coefs="0.031393 -0.031367 0.031367 0.031393 0 0" />
|
||||||
|
<palette count="256" format="RGB">
|
||||||
|
FF0000D31616BD2121A72C2C9137377C4242714747664D4D
|
||||||
|
3A63631D7171008080008B8B00969600A1A100ACAC00B1B1
|
||||||
|
00B7B700CCCC00D7D700E2E200EDED00F8F800FBFB00FFFF
|
||||||
|
2CF0FF42E8FF58E0FF6DD8FF83D1FF8ECDFF99C9FFAFC2FF
|
||||||
|
C5BAFFFFA6FFE9A2FFD39FFFBD9CFFA799FF9C97FF9196FF
|
||||||
|
668FFF508CFF3A89FF2485FF0E82FF0781FF0080FF0B80FF
|
||||||
|
1680FF2C80FF3780FF4280FF4D80FF5880FF5D80FF6380FF
|
||||||
|
7980FF7785F4758BE96A96D35FA1BD59A6B254ACA749B791
|
||||||
|
3EC17C28D7501DE23A12ED2409F61200FF0016E9002CD300
|
||||||
|
58A7006D9100837C00996600AF5000BA4500C53A00DB2400
|
||||||
|
F10E00E90B00D31600BD2100B22600A72C009137007C4200
|
||||||
|
5058003A6300246E001973000E79000080000A7500146A00
|
||||||
|
1E5F003249003C3E004633004B2D005028005A1D00651200
|
||||||
|
8100008C00009800009E0000A40000AF0000BB0000C70000
|
||||||
|
D20000EA0000F00000F60000FD0000F2160BE82C16DD4221
|
||||||
|
C76E37BC8342B2994DACA452A7AF589CC56392DB6E87F179
|
||||||
|
80FF8080E99680E39B80DEA180D3AC80C8B780BEC180B3CC
|
||||||
|
809DE2808EF08080FF7A80F47580E96A80D35F80BD5480A7
|
||||||
|
4980913380662D805B2880501D803A12802407800E008000
|
||||||
|
2C841A3784204285265887336E8940838B4D998D5AAF8E66
|
||||||
|
C59073FF9595FF9393FF9292FF9090FF8D8DFF8B8BFF8888
|
||||||
|
FF8383FF8181FF8080FF7E7EFF7B7BFF7979FF7777FF7783
|
||||||
|
FF768EFF769AFF75A6FF75B1FF74BDFF74C9FF73D4FF73E0
|
||||||
|
FF72F8FF71FBFF71FFFF6BEDFF65DBFF5FC9FF5AB7FF54A5
|
||||||
|
FF4E93FF4881FF426FFF3C5DFF374BFF2D2DFA293AF62548
|
||||||
|
F12155ED1E63E81A70E4167EDF128BDB0E99D60AA6D106B4
|
||||||
|
CD02C1CA00CACC00B9CE00A7CF0096D10085D30073D50062
|
||||||
|
D70050D8003FDA002EDC001CDE000BDF0000D90C06D4180C
|
||||||
|
CE2413C82F19C33B1FBD4725B7532BB25F32AC6B38A6773E
|
||||||
|
9D8A489D7E429E723C9E66359F5B2FA04F29A04323A1371D
|
||||||
|
A12B16A21F10A3130AA30804A40000A000009C0000980000
|
||||||
|
9400009000008C00008800008400008000007C0000750000
|
||||||
|
</palette>
|
||||||
|
</flame>
|
||||||
|
<flame name="final xform" version="Apophysis 2.08 beta" size="600 600" center="0 0" scale="150" oversample="1" filter="0.2" quality="1" background="1 1 1" brightness="4" gamma="4" >
|
||||||
|
<xform weight="0.422330042096567" color="0.349" pdj="1" coefs="1.51523 0.740356 -3.048677 -1.455964 0.724135 -0.362059" pdj_a="1.09358" pdj_b="2.13048" pdj_c="2.54127" pdj_d="2.37267" />
|
||||||
|
<xform weight="0.564534951145298" color="0" julia="1" coefs="-1.381068 1.381068 -1.381068 -1.381068 0 0" />
|
||||||
|
<xform weight="0.0131350067581356" color="0.844" linear="1" popcorn="1" coefs="0.031393 -0.031367 0.031367 0.031393 0 0" post="1 0 0 1 0.241352 0.271521" />
|
||||||
|
<finalxform color="0" symmetry="1" julia="1" coefs="2 0 0 2 0 0" />
|
||||||
|
<palette count="256" format="RGB">
|
||||||
|
7E3037762C45722B496E2A4E6A2950672853652754632656
|
||||||
|
5C265C5724595322574D2155482153462050451F4E441E4D
|
||||||
|
431E4C3F1E473F1E453F1E433F1E3F3F1E3B3E1E393E1E37
|
||||||
|
421D36431C38451C3A471B3B491B3C4A1A3C4B1A3D4D1A3E
|
||||||
|
4F19405318435517445817465A16475D15495E154960154A
|
||||||
|
65134E6812506B12526E1153711055720F55740F55770E57
|
||||||
|
7A0E59810C58840B58880A588B09588F0858910756930755
|
||||||
|
9A05539D0451A1034FA5024BA90147AA0046AC0045B00242
|
||||||
|
B4043DBB0634BE082EC20A29C30B27C50C26C90F1DCC1116
|
||||||
|
D32110D6280EDA300CDC380ADF4109E04508E24A08E45106
|
||||||
|
E75704EA6402EC6B01EE7300EE7600EF7A00F07E00F18300
|
||||||
|
F29000F29300F39600F39900F39C00F3A000F3A100F3A201
|
||||||
|
F2A502F1A805F0A906EFAA08EEA909EEA80AEDA60CEBA50F
|
||||||
|
E5A313E1A113DD9F13DB9E13D99D14D49C15D09815CC9518
|
||||||
|
C79318BE8B1ABB891BB9871DB4811FB07D1FAB7621A67123
|
||||||
|
9C6227975C289256299053298E502A89482C853F2D803A2E
|
||||||
|
7E3037762C45742B47722B496E2A4E6A2951672853632656
|
||||||
|
5C265C5724595322575022564E2255482153452050451F4E
|
||||||
|
431E4C3F1E473E1D463D1D453F1E43411E413F1E3B3E1E37
|
||||||
|
421D36421D38431D3B451C3A471B3A491B3C4B1A3D4D1A3E
|
||||||
|
4F19405318435418445518455817465A16475D154960154A
|
||||||
|
65134E66124F6812506B12526E1153711055740F55770E57
|
||||||
|
7A0E597E0D57810C58840B58880A588B09588F0858930755
|
||||||
|
9A05539C04529E0452A1034FA5024BA90147AC0045B00242
|
||||||
|
B4043DB7053ABB0634BE0831C20A29C50C26C90F1DCC1116
|
||||||
|
D01711D32110D72A0EDA300CDD390ADF4109E24A08E45106
|
||||||
|
E75704E95F03EA6402EC6C01EE7300EF7A00F07E00F18300
|
||||||
|
F28900F29000F39300F39600F39C00F3A000F3A100F3A201
|
||||||
|
F2A502F2A503F1A805F0A807EFAA08EEA80AEDA60CEBA50F
|
||||||
|
E9A411E5A313E1A113DD9F13D99D14D49C15D09815CC9518
|
||||||
|
C79318C38F1ABE8B1AB9871DB4811FB07D1FAB7621A67123
|
||||||
|
A16A249C6227975E289256298E502A89482C853F2D803A2E
|
||||||
|
</palette>
|
||||||
|
</flame>
|
||||||
|
</Flames>
|
@ -1,4 +1,7 @@
|
|||||||
import { PropsWithChildren } from "react";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { faCalendar } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { MDXProvider } from "@mdx-js/react";
|
||||||
|
import React, { PropsWithChildren } from "react";
|
||||||
|
|
||||||
import Base from "../pages/LayoutBase";
|
import Base from "../pages/LayoutBase";
|
||||||
|
|
||||||
@ -9,6 +12,10 @@ interface BlogProps {
|
|||||||
updated?: string;
|
updated?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const components = {
|
||||||
|
pre: (props: any) => <pre className="hljs" {...props} />,
|
||||||
|
};
|
||||||
|
|
||||||
export default function Layout({
|
export default function Layout({
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
@ -19,7 +26,14 @@ export default function Layout({
|
|||||||
<div className="header">
|
<div className="header">
|
||||||
<h1>{title}</h1>
|
<h1>{title}</h1>
|
||||||
<h3>{description}</h3>
|
<h3>{description}</h3>
|
||||||
<p>Published: {published}</p>
|
<h4>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faCalendar}
|
||||||
|
scale={1.2}
|
||||||
|
className="icon icon-post"
|
||||||
|
/>
|
||||||
|
{published}
|
||||||
|
</h4>
|
||||||
{updated && <p>Last updated: {updated}</p>}
|
{updated && <p>Last updated: {updated}</p>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -27,8 +41,8 @@ export default function Layout({
|
|||||||
const withChildren: React.FC<PropsWithChildren> = ({ children }) => (
|
const withChildren: React.FC<PropsWithChildren> = ({ children }) => (
|
||||||
<Base>
|
<Base>
|
||||||
{header}
|
{header}
|
||||||
<hr />
|
<div style={{ paddingTop: "2em" }} />
|
||||||
{children}
|
<MDXProvider components={components}>{children}</MDXProvider>
|
||||||
</Base>
|
</Base>
|
||||||
);
|
);
|
||||||
return withChildren;
|
return withChildren;
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"target": "ESNext",
|
"target": "ESNext",
|
||||||
"useDefineForClassFields": true,
|
"useDefineForClassFields": true,
|
||||||
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||||
"allowJs": false,
|
"allowJs": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"esModuleInterop": false,
|
"esModuleInterop": false,
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
|
@ -2,17 +2,30 @@ import { defineConfig } from "vite";
|
|||||||
import blog from "@bspeice/vite-plugin-blog";
|
import blog from "@bspeice/vite-plugin-blog";
|
||||||
import mdx from "@mdx-js/rollup";
|
import mdx from "@mdx-js/rollup";
|
||||||
import react from "@vitejs/plugin-react-swc";
|
import react from "@vitejs/plugin-react-swc";
|
||||||
|
import rehypeHighlight from "rehype-highlight";
|
||||||
import remarkPrism from "remark-prism";
|
import rehypeKatex from "rehype-katex";
|
||||||
|
import remarkFrontmatter from "remark-frontmatter";
|
||||||
|
import remarkMath from "remark-math";
|
||||||
|
import remarkMdxFrontmatter from "remark-mdx-frontmatter";
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
build: {
|
||||||
|
rollupOptions: {
|
||||||
|
external: ["react-icons"],
|
||||||
|
},
|
||||||
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
blog({
|
blog({
|
||||||
"/": "/pages/index.tsx",
|
"/": "/pages/index.tsx",
|
||||||
"/about": "/pages/about.mdx",
|
"/about": "/pages/about.mdx",
|
||||||
"/2019/02/the-whole-world": "/posts/2019/02/the-whole-world.mdx",
|
"/2019/02/the-whole-world": "/posts/2019/02/the-whole-world.mdx",
|
||||||
|
"/2023/06/flam3": "/posts/2023/06/flam3/index.tsx",
|
||||||
|
}),
|
||||||
|
mdx({
|
||||||
|
remarkPlugins: [remarkFrontmatter, remarkMath, remarkMdxFrontmatter],
|
||||||
|
rehypePlugins: [rehypeHighlight, rehypeKatex],
|
||||||
|
providerImportSource: "@mdx-js/react",
|
||||||
}),
|
}),
|
||||||
mdx({ remarkPlugins: [remarkPrism] }),
|
|
||||||
react(),
|
react(),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|