mirror of
https://github.com/Pomax/BezierInfo-2.git
synced 2025-09-02 21:02:49 +02:00
modules in sketch code
This commit is contained in:
56
README.md
56
README.md
@@ -1,56 +0,0 @@
|
||||
# BezierInfo-2 
|
||||
|
||||
Dev repository for https://Pomax.github.io/bezierinfo
|
||||
|
||||
## Working on the code
|
||||
|
||||
- To compile changes: `npm start`.
|
||||
- To run this dev version, run `npm test`, which will run a webserver and open [http://localhost:8080](http://localhost:8080) in your default browser.
|
||||
|
||||
## Dev location
|
||||
|
||||
- Dev repository: https://github.com/Pomax/BezierInfo-2
|
||||
- Dev preview: https://Pomax.github.io/BezierInfo-2
|
||||
|
||||
### Dev requirements
|
||||
|
||||
- [Node.js](https://nodejs.org)
|
||||
- XeLaTeX (available through [TeXLive](https://www.tug.org/texlive) on unix/linux/OSX and [MiKTeX](https://miktex.org) on Windows)
|
||||
- pdfcrop (available through [TeXLive](https://www.tug.org/texlive) on unix/linux/OSX and [MiKTeX](https://miktex.org) on Windows)
|
||||
- The [`pdf2svg`](http://www.cityinthesky.co.uk/opensource/pdf2svg/) utility
|
||||
|
||||
### Fonts required for proper LaTeX typesetting
|
||||
|
||||
All fonts come with TeXLive and MiKTeX, and should be easy to install. Note that you will need the modern OpenType (otf/ttf) fonts, not the obsolete type1 fonts.
|
||||
|
||||
- en-GB fonts: TeX Gyre Pagella from the `tex-gyre` package
|
||||
- ja-JP font: IPAex Mincho from the `ipaex` package (_not_ `ipaex-type1`)
|
||||
- zh-CN font: Arhpic gbsn from the `arphic-ttf` package (_not_ `arphic`)
|
||||
- maths fonts: TeX Gyre Pagella Math fonts from the `tex-gyre-math` package
|
||||
|
||||
### Running a build
|
||||
|
||||
As mentioned up top, run a build using `npm start`.
|
||||
|
||||
If you have all the prerequisites installed, this should "just work", although I can't make any guarantees on how long it will take: on my rather beefy workstation it takes around 85 seconds to run a build for all locales (`en-GB`, `zh-CN`, and `ja-JP`) when there are no new SVG images to generate.
|
||||
|
||||
## Main site location
|
||||
|
||||
- Main repository: https://github.com/Pomax/bezierInfo
|
||||
- Main site: https://pomax.github.io/bezierInfo
|
||||
|
||||
## Localization
|
||||
|
||||
Interested in (helping with) localizing the Primer to your own language? That's awesome! Please read [the instructions on how to start localizing](https://github.com/Pomax/BezierInfo-2/wiki/localize) and please file issues if anything is unclear.
|
||||
|
||||
## Additional information
|
||||
|
||||
Interested in the actual architecture and tech stack? Read the blog post on how Webpack's sync processing and MathJAx's async processing were made to work together:
|
||||
|
||||
http://pomax.github.io/1451617530567/react-with-latex-without-needing-client-side-mathjax
|
||||
|
||||
And read about the tech choices made to enable localization in:
|
||||
|
||||
http://pomax.github.io/1489108158510/localization-is-hard
|
||||
|
||||
Finally, a fair number of people have helped by filing PRs for fixes for typos small and large over the years, all of whom are listed on the [contributors](https://github.com/Pomax/BezierInfo-2/graphs/contributors) page for this project. And a special thanks goes out to Simon Cozens who [went through the entire book](https://github.com/Pomax/BezierInfo-2/pulls?utf8=%E2%9C%93&q=is%3Apr+author%3Asimoncozens) to fix typos and phrasing.
|
@@ -1,32 +0,0 @@
|
||||
module.exports = {
|
||||
setupQuadratic: function(api) {
|
||||
var curve = api.getDefaultQuadratic();
|
||||
api.setCurve(curve);
|
||||
},
|
||||
|
||||
setupCubic: function(api) {
|
||||
var curve = api.getDefaultCubic();
|
||||
api.setCurve(curve);
|
||||
},
|
||||
|
||||
draw: function(api, curve) {
|
||||
api.reset();
|
||||
api.drawSkeleton(curve);
|
||||
|
||||
var i,t,p,tg,n,m,nd=20;
|
||||
for(i=0; i<=10; i++) {
|
||||
t = i/10.0;
|
||||
p = curve.get(t);
|
||||
tg = curve.derivative(t);
|
||||
m = Math.sqrt(tg.x*tg.x + tg.y*tg.y);
|
||||
tg = {x:tg.x/m, y:tg.y/m};
|
||||
n = curve.normal(t);
|
||||
api.setColor("blue");
|
||||
api.drawLine(p, {x:p.x+tg.x*nd, y:p.y+tg.y*nd});
|
||||
api.setColor("red");
|
||||
api.drawLine(p, {x:p.x+n.x*nd, y:p.y+n.y*nd});
|
||||
api.setColor("black");
|
||||
api.drawCircle(p,3);
|
||||
}
|
||||
}
|
||||
};
|
@@ -24,7 +24,7 @@ Let's unpack that a little:
|
||||
|
||||
And then we're done, we found "the" normal vector for a 3D curve. Let's see what that looks like for a sample curve, shall we? You can move your cursor across the graphic from left to right, to show the normal at a point with a t value that is based on your cursor position: all the way on the left is 0, all the way on the right = 1, midway is t=0.5, etc:
|
||||
|
||||
<Graphic title="Some known and unknown vectors" setup={this.setup} draw={this.drawFrenetVectors}/>
|
||||
<graphics-element title="Some known and unknown vectors" src="./frenet.js"></graphics-element>
|
||||
|
||||
However, if you've played with that graphic a bit, you might have noticed something odd. The normal seems to "suddenly twist around" around between t=0.5 and t=0.75 - why is doing that?
|
||||
|
||||
@@ -109,7 +109,7 @@ Ignoring comments, this is certainly more code than when we were just computing
|
||||
|
||||
Speaking of better looking, what does this actually look like? Let's revisit that earlier curve, but this time use rotation minimising frames rather than Frenet frames:
|
||||
|
||||
<Graphic title="Æsthetically much better 3D curve normals" setup={this.setup} draw={this.drawRMFNormals}/>
|
||||
<graphics-element title="Some known and unknown vectors" src="./rotation-minimizing.js"></graphics-element>
|
||||
|
||||
Now that looks much better!
|
||||
|
||||
|
9
chapters/pointvectors3d/frenet.js
Normal file
9
chapters/pointvectors3d/frenet.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import { vectorlib } from "./vector-lib.js";
|
||||
|
||||
setup() {
|
||||
// ...
|
||||
}
|
||||
|
||||
draw() {
|
||||
clear();
|
||||
}
|
7
chapters/pointvectors3d/rotation-minimizing.js
Normal file
7
chapters/pointvectors3d/rotation-minimizing.js
Normal file
@@ -0,0 +1,7 @@
|
||||
setup() {
|
||||
|
||||
}
|
||||
|
||||
draw() {
|
||||
clear();
|
||||
}
|
72
chapters/pointvectors3d/vector-lib.js
Normal file
72
chapters/pointvectors3d/vector-lib.js
Normal file
@@ -0,0 +1,72 @@
|
||||
const vectorlib = {
|
||||
normalize: function(v) {
|
||||
let z = v.z || 0;
|
||||
var d = Math.sqrt(v.x*v.x + v.y*v.y + z*z);
|
||||
let r = { x:v.x/d, y:v.y/d };
|
||||
if (v.z !== undefined) r.z = z/d;
|
||||
return r;
|
||||
},
|
||||
|
||||
vdot: function(v1, v2) {
|
||||
let z1 = v1.z || 0;
|
||||
let z2 = v2.z || 0;
|
||||
return v1.x * v2.x + v1.y * v2.y + z1 * z2;
|
||||
},
|
||||
|
||||
vscale: function(v, s) {
|
||||
let r = {
|
||||
x: s * v1.x,
|
||||
y: s * v1.y
|
||||
}
|
||||
if (v.z !== undefined) {
|
||||
r.z = s * v.z
|
||||
}
|
||||
return r;
|
||||
},
|
||||
|
||||
vplus: function(v1, v2) {
|
||||
let r = {
|
||||
x: v1.x + v2.x,
|
||||
y: v1.y + v2.y
|
||||
};
|
||||
if (v1.z !== undefined || v2.z !== undefined) {
|
||||
r.z = (v1.z||0) + (v2.z||0);
|
||||
};
|
||||
return r;
|
||||
},
|
||||
|
||||
vminus: function(v1, v2) {
|
||||
let r = {
|
||||
x: v1.x - v2.x,
|
||||
y: v1.y - v2.y
|
||||
};
|
||||
if (v1.z !== undefined || v2.z !== undefined) {
|
||||
r.z = (v1.z||0) - (v2.z||0);
|
||||
};
|
||||
return r;
|
||||
},
|
||||
|
||||
vcross: function(v1, v2) {
|
||||
if (v1.z === undefined || v2.z === undefined) {
|
||||
throw new Error(`Cross product is not defined for 2D vectors.`);
|
||||
}
|
||||
return {
|
||||
x: v1.y * v2.z - v1.z * v2.y,
|
||||
y: v1.z * v2.x - v1.x * v2.z,
|
||||
z: v1.x * v2.y - v1.y * v2.x
|
||||
};
|
||||
},
|
||||
|
||||
vlerp: function(t, v1, v2) {
|
||||
let r = {
|
||||
x: (1-t)*v1.x + t*v2.x,
|
||||
y: (1-t)*v1.y + t*v2.y
|
||||
};
|
||||
if (v1.z !== undefined || v2.z !== undefined) {
|
||||
r.z = (1-t)*(v1.z||0) + t*(v2.z||0);
|
||||
};
|
||||
return r;
|
||||
}
|
||||
};
|
||||
|
||||
export { vectorlib }
|
Binary file not shown.
After Width: | Height: | Size: 967 B |
Binary file not shown.
After Width: | Height: | Size: 967 B |
Binary file not shown.
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
40
index.html
40
index.html
@@ -2727,11 +2727,22 @@ function drawCurve(points[], t):
|
||||
position: all the way on the left is 0, all the way on the right =
|
||||
1, midway is t=0.5, etc:
|
||||
</p>
|
||||
<Graphic
|
||||
<graphics-element
|
||||
title="Some known and unknown vectors"
|
||||
setup="{this.setup}"
|
||||
draw="{this.drawFrenetVectors}"
|
||||
/>
|
||||
width="275"
|
||||
height="275"
|
||||
src="./chapters/pointvectors3d/frenet.js"
|
||||
>
|
||||
<fallback-image>
|
||||
<img
|
||||
width="275px"
|
||||
height="275px"
|
||||
src="images\chapters\pointvectors3d\22cb6c545b8e25b55b0f415414ea53fa.png"
|
||||
loading="lazy"
|
||||
/>
|
||||
Scripts are disabled. Showing fallback image.
|
||||
</fallback-image></graphics-element
|
||||
>
|
||||
|
||||
<p>
|
||||
However, if you've played with that graphic a bit, you might have
|
||||
@@ -2866,11 +2877,22 @@ function drawCurve(points[], t):
|
||||
revisit that earlier curve, but this time use rotation minimising
|
||||
frames rather than Frenet frames:
|
||||
</p>
|
||||
<Graphic
|
||||
title="Æsthetically much better 3D curve normals"
|
||||
setup="{this.setup}"
|
||||
draw="{this.drawRMFNormals}"
|
||||
/>
|
||||
<graphics-element
|
||||
title="Some known and unknown vectors"
|
||||
width="275"
|
||||
height="275"
|
||||
src="./chapters/pointvectors3d/rotation-minimizing.js"
|
||||
>
|
||||
<fallback-image>
|
||||
<img
|
||||
width="275px"
|
||||
height="275px"
|
||||
src="images\chapters\pointvectors3d\259fa2934b1a09bcb02d6928019f3159.png"
|
||||
loading="lazy"
|
||||
/>
|
||||
Scripts are disabled. Showing fallback image.
|
||||
</fallback-image></graphics-element
|
||||
>
|
||||
|
||||
<p>Now that looks much better!</p>
|
||||
<p>
|
||||
|
@@ -2323,11 +2323,22 @@ function drawCurve(points[], t):
|
||||
position: all the way on the left is 0, all the way on the right =
|
||||
1, midway is t=0.5, etc:
|
||||
</p>
|
||||
<Graphic
|
||||
<graphics-element
|
||||
title="Some known and unknown vectors"
|
||||
setup="{this.setup}"
|
||||
draw="{this.drawFrenetVectors}"
|
||||
/>
|
||||
width="275"
|
||||
height="275"
|
||||
src="./chapters/pointvectors3d/frenet.js"
|
||||
>
|
||||
<fallback-image>
|
||||
<img
|
||||
width="275px"
|
||||
height="275px"
|
||||
src="images\chapters\pointvectors3d\22cb6c545b8e25b55b0f415414ea53fa.png"
|
||||
loading="lazy"
|
||||
/>
|
||||
Scripts are disabled. Showing fallback image.
|
||||
</fallback-image></graphics-element
|
||||
>
|
||||
|
||||
<p>
|
||||
However, if you've played with that graphic a bit, you might have
|
||||
@@ -2462,11 +2473,22 @@ function drawCurve(points[], t):
|
||||
revisit that earlier curve, but this time use rotation minimising
|
||||
frames rather than Frenet frames:
|
||||
</p>
|
||||
<Graphic
|
||||
title="Æsthetically much better 3D curve normals"
|
||||
setup="{this.setup}"
|
||||
draw="{this.drawRMFNormals}"
|
||||
/>
|
||||
<graphics-element
|
||||
title="Some known and unknown vectors"
|
||||
width="275"
|
||||
height="275"
|
||||
src="./chapters/pointvectors3d/rotation-minimizing.js"
|
||||
>
|
||||
<fallback-image>
|
||||
<img
|
||||
width="275px"
|
||||
height="275px"
|
||||
src="images\chapters\pointvectors3d\259fa2934b1a09bcb02d6928019f3159.png"
|
||||
loading="lazy"
|
||||
/>
|
||||
Scripts are disabled. Showing fallback image.
|
||||
</fallback-image></graphics-element
|
||||
>
|
||||
|
||||
<p>Now that looks much better!</p>
|
||||
<p>
|
||||
|
@@ -78,19 +78,20 @@ class GraphicsElement extends CustomElement {
|
||||
* Load the graphics code, either from a src URL, a <program-code> element, or .textContent
|
||||
*/
|
||||
async loadSource() {
|
||||
let src = false;
|
||||
let codeElement = this.querySelector(`program-code`);
|
||||
|
||||
let code = ``;
|
||||
|
||||
if (codeElement) {
|
||||
let src = codeElement.getAttribute("src");
|
||||
src = codeElement.getAttribute("src");
|
||||
if (src) {
|
||||
code = await fetch(src).then((response) => response.text());
|
||||
} else {
|
||||
code = codeElement.textContent;
|
||||
}
|
||||
} else {
|
||||
let src = this.getAttribute("src");
|
||||
src = this.getAttribute("src");
|
||||
if (src) {
|
||||
code = await fetch(src).then((response) => response.text());
|
||||
} else {
|
||||
@@ -108,7 +109,7 @@ class GraphicsElement extends CustomElement {
|
||||
|
||||
new MutationObserver((_records) => {
|
||||
// nornmally we don't want to completely recreate the shadow DOM
|
||||
this.processSource(codeElement.textContent);
|
||||
this.processSource(src, codeElement.textContent);
|
||||
}).observe(codeElement, {
|
||||
characterData: true,
|
||||
attributes: false,
|
||||
@@ -117,13 +118,13 @@ class GraphicsElement extends CustomElement {
|
||||
});
|
||||
|
||||
// But on the first pass, we do.
|
||||
this.processSource(code, true);
|
||||
this.processSource(src, code, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform the graphics source code into global and class code.
|
||||
*/
|
||||
processSource(code, rerender = false) {
|
||||
processSource(src, code, rerender = false) {
|
||||
this.rawCode = code;
|
||||
if (this.script) {
|
||||
if (this.script.parentNode) {
|
||||
@@ -136,6 +137,24 @@ class GraphicsElement extends CustomElement {
|
||||
const uid = `bg-uid-${Date.now()}-${`${Math.random()}`.replace(`0.`, ``)}`;
|
||||
window[uid] = this;
|
||||
|
||||
// Step 1: fix the imports. This is ... a bit of work.
|
||||
let path;
|
||||
let base = document.querySelector(`base`);
|
||||
if (base) {
|
||||
path = base.href;
|
||||
} else {
|
||||
let loc = window.location.toString();
|
||||
path = loc.substring(0, loc.lastIndexOf(`/`) + 1);
|
||||
}
|
||||
let modulepath = `${path}${src}`;
|
||||
let modulebase = modulepath.substring(0, modulepath.lastIndexOf(`/`) + 1);
|
||||
|
||||
// okay, I lied, it's actually quite a lot of work.
|
||||
code = code.replace(/(import .+? from) "([^"]+)"/g, (_, main, group) => {
|
||||
return `${main} "${modulebase}${group}"`;
|
||||
});
|
||||
|
||||
// Then, step 2: split up the code into "global" vs. "class" code.
|
||||
const split = splitCodeSections(code);
|
||||
const globalCode = split.quasiGlobal;
|
||||
const classCode = performCodeSurgery(split.classCode);
|
||||
|
@@ -10,7 +10,13 @@ const __root = path.join(__dirname, `..`, `..`, `..`);
|
||||
/**
|
||||
* ...docs go here...
|
||||
*/
|
||||
async function generateFallbackImage(localeStrings, src, width, height) {
|
||||
async function generateFallbackImage(
|
||||
chapter,
|
||||
localeStrings,
|
||||
src,
|
||||
width,
|
||||
height
|
||||
) {
|
||||
const locale = localeStrings.getCurrentLocale();
|
||||
|
||||
// Get the sketch code
|
||||
@@ -35,7 +41,7 @@ async function generateFallbackImage(localeStrings, src, width, height) {
|
||||
// If we get here, we need to actually run the magic: convert
|
||||
// this to a valid JS module code and write this to a temporary
|
||||
// file so we can import it.
|
||||
const nodeCode = generateGraphicsModule(code, width, height);
|
||||
const nodeCode = generateGraphicsModule(chapter, code, width, height);
|
||||
const fileName = `./nodecode.${Date.now()}.${Math.random()}.js`;
|
||||
const tempFile = path.join(__dirname, fileName);
|
||||
fs.writeFileSync(tempFile, nodeCode, `utf8`);
|
||||
@@ -45,7 +51,7 @@ async function generateFallbackImage(localeStrings, src, width, height) {
|
||||
// turn into an actual image file.
|
||||
const { canvas } = await import(fileName);
|
||||
|
||||
// fs.unlinkSync(tempFile);
|
||||
fs.unlinkSync(tempFile);
|
||||
|
||||
// The canvas runs setup() + draw() as part of the module load, so
|
||||
// all we have to do now is get the image data and writ it to file.
|
||||
|
@@ -5,7 +5,13 @@ import prettier from "prettier";
|
||||
/**
|
||||
* ...docs go here...
|
||||
*/
|
||||
function generateGraphicsModule(code, width, height) {
|
||||
function generateGraphicsModule(chapter, code, width, height) {
|
||||
// step 1: fix the imports
|
||||
code = code.replace(/(import .+? from) "([^"]+)"/g, (_, main, group) => {
|
||||
return `${main} "../../../chapters/${chapter}/${group}"`;
|
||||
});
|
||||
|
||||
// step 2: split up the code into "global" vs. "class" code
|
||||
const split = splitCodeSections(code);
|
||||
const globalCode = split.quasiGlobal;
|
||||
const classCode = performCodeSurgery(split.classCode);
|
||||
@@ -15,10 +21,10 @@ function generateGraphicsModule(code, width, height) {
|
||||
import CanvasBuilder from 'canvas';
|
||||
import { GraphicsAPI, Bezier, Vector, Matrix } from "../../../lib/custom-element/api/graphics-api.js";
|
||||
|
||||
const noop = (()=>{});
|
||||
|
||||
${globalCode}
|
||||
|
||||
const noop = (()=>{});
|
||||
|
||||
class Example extends GraphicsAPI { ${classCode} }
|
||||
|
||||
const example = new Example(undefined, ${width}, ${height}, (w,h) => {
|
||||
|
@@ -52,6 +52,7 @@ async function preprocessGraphicsElement(chapter, localeStrings, markdown) {
|
||||
}
|
||||
|
||||
let imageHash = await generateFallbackImage(
|
||||
chapter,
|
||||
localeStrings,
|
||||
src,
|
||||
width,
|
||||
|
@@ -77,7 +77,7 @@ async function processLocale(locale, localeStrings, chapterFiles) {
|
||||
}
|
||||
|
||||
const end = Date.now();
|
||||
// console.log(`Processing ${locale} took ${(end - start) / 1000}s`);
|
||||
console.log(`Processing ${locale} took ${(end - start) / 1000}s`);
|
||||
|
||||
return chapters;
|
||||
}
|
||||
|
@@ -2333,11 +2333,22 @@ function drawCurve(points[], t):
|
||||
position: all the way on the left is 0, all the way on the right =
|
||||
1, midway is t=0.5, etc:
|
||||
</p>
|
||||
<Graphic
|
||||
<graphics-element
|
||||
title="Some known and unknown vectors"
|
||||
setup="{this.setup}"
|
||||
draw="{this.drawFrenetVectors}"
|
||||
/>
|
||||
width="275"
|
||||
height="275"
|
||||
src="./chapters/pointvectors3d/frenet.js"
|
||||
>
|
||||
<fallback-image>
|
||||
<img
|
||||
width="275px"
|
||||
height="275px"
|
||||
src="images\chapters\pointvectors3d\22cb6c545b8e25b55b0f415414ea53fa.png"
|
||||
loading="lazy"
|
||||
/>
|
||||
Scripts are disabled. Showing fallback image.
|
||||
</fallback-image></graphics-element
|
||||
>
|
||||
|
||||
<p>
|
||||
However, if you've played with that graphic a bit, you might have
|
||||
@@ -2472,11 +2483,22 @@ function drawCurve(points[], t):
|
||||
revisit that earlier curve, but this time use rotation minimising
|
||||
frames rather than Frenet frames:
|
||||
</p>
|
||||
<Graphic
|
||||
title="Æsthetically much better 3D curve normals"
|
||||
setup="{this.setup}"
|
||||
draw="{this.drawRMFNormals}"
|
||||
/>
|
||||
<graphics-element
|
||||
title="Some known and unknown vectors"
|
||||
width="275"
|
||||
height="275"
|
||||
src="./chapters/pointvectors3d/rotation-minimizing.js"
|
||||
>
|
||||
<fallback-image>
|
||||
<img
|
||||
width="275px"
|
||||
height="275px"
|
||||
src="images\chapters\pointvectors3d\259fa2934b1a09bcb02d6928019f3159.png"
|
||||
loading="lazy"
|
||||
/>
|
||||
Scripts are disabled. Showing fallback image.
|
||||
</fallback-image></graphics-element
|
||||
>
|
||||
|
||||
<p>Now that looks much better!</p>
|
||||
<p>
|
||||
|
Reference in New Issue
Block a user