From fec813773eb2e6b3982f8b8ddf475f2d6bcd9533 Mon Sep 17 00:00:00 2001 From: Pomax Date: Tue, 4 Aug 2020 11:06:54 -0700 Subject: [PATCH] better building --- .gitignore | 4 + chapters/README.md | 0 chapters/toc.js | 81 ++++++++++ config.json | 18 +++ en-GB/index.html | 209 ++++++++++++++++++++++++++ index.en-GB.html => index.html | 6 + index.template.html | 4 + index.ja-JP.html => ja-JP/index.html | 81 ++++++++++ tools/build-index.js | 161 ++------------------ tools/build/convert-markdown.js | 52 +++++++ tools/build/create-index-page.js | 50 ++++++ tools/build/generate-lang-switcher.js | 25 +++ tools/build/get-all-chapter-files.js | 29 ++++ tools/{ => build}/latex-to-svg.js | 4 +- tools/build/process-locale.js | 54 +++++++ index.zh-CN.html => zh-CN/index.html | 81 ++++++++++ 16 files changed, 709 insertions(+), 150 deletions(-) create mode 100644 .gitignore delete mode 100644 chapters/README.md create mode 100644 chapters/toc.js create mode 100644 config.json create mode 100644 en-GB/index.html rename index.en-GB.html => index.html (97%) rename index.ja-JP.html => ja-JP/index.html (53%) create mode 100644 tools/build/convert-markdown.js create mode 100644 tools/build/create-index-page.js create mode 100644 tools/build/generate-lang-switcher.js create mode 100644 tools/build/get-all-chapter-files.js rename tools/{ => build}/latex-to-svg.js (96%) create mode 100644 tools/build/process-locale.js rename index.zh-CN.html => zh-CN/index.html (51%) diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..4230481e --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules +./en-GB +./zh-CN +./ja-JP diff --git a/chapters/README.md b/chapters/README.md deleted file mode 100644 index e69de29b..00000000 diff --git a/chapters/toc.js b/chapters/toc.js new file mode 100644 index 00000000..a0eaec28 --- /dev/null +++ b/chapters/toc.js @@ -0,0 +1,81 @@ +/** + * This is an ordered list of all sections used in the Bezier primer. + * + * The ordering you see here reflects the ordering in which sections + '* are present on the Primer page', + * a REALLY good reason to =) + * + */ +module.exports = [ + 'preface', + + // the basic topic(s) introduction(s) + 'introduction', + 'whatis', + 'explanation', + 'control', + 'weightcontrol', + 'extended', + + // basic operations + 'matrix', + 'decasteljau', + 'flattening', + 'splitting', + 'matrixsplit', + 'reordering', + + // information that can be obtained through analysis + 'derivatives', + 'pointvectors', + 'pointvectors3d', + 'components', + 'extremities', + 'boundingbox', + 'aligning', + 'tightbounds', + 'inflections', + 'canonical', + 'yforx', + + // accurate arc length is hard, yo + 'arclength', + 'arclengthapprox', + 'curvature', + 'tracing', + + // curve intersections + 'intersections', + 'curveintersection', + + // curve manipulation + 'abc', + 'moulding', + 'pointcurves', + 'curvefitting', + + // A quick foray into Catmull-Rom splines + 'catmullconv', + 'catmullmoulding', + + // "things made of more than on curve" + 'polybezier', + 'shapes', + // 'drawing', + + // curve offsetting + 'projections', + 'offsetting', + 'graduatedoffset', + + // circle and arc approximation + 'circles', + 'circles_cubic', + 'arcapproximation', + + // A quick foray in to B-Spline land + 'bsplines', + + // comments come last for obvious reasons + 'comments', +]; diff --git a/config.json b/config.json new file mode 100644 index 00000000..a74c6956 --- /dev/null +++ b/config.json @@ -0,0 +1,18 @@ +{ + "defaultLocale": "en-GB", + "localeName": { + "en-GB": "English", + "zh-CN": "中文", + "ja-JP": "日本語" + }, + "langSwitchLabel": { + "en-GB": "Read this in your own language:", + "zh-CN": "Read this in your own language:", + "ja-JP": "Read this in your own language:" + }, + "disabledMessage": { + "en-GB": "Scripts are disabled. Showing fallback image.", + "zh-CN": "脚本已禁用,并显示后备图像。", + "ja-JP": "JSがなくて、画像を表示しています。" + } +} diff --git a/en-GB/index.html b/en-GB/index.html new file mode 100644 index 00000000..f631fdf4 --- /dev/null +++ b/en-GB/index.html @@ -0,0 +1,209 @@ + + + + + + + A Primer on Bézier Curves + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

A Primer on Bézier Curves

+

+ A free, online book for when you really need to know how to do Bézier + things. +

+ Read this in your own language: + +
+ +
+
+ +
+
    +
  1. A lightning introduction
  2. +
  3. So what makes a Bézier Curve?
  4. +
+
+ +
+ +
+
+

A lightning introduction

+

+ Let's start with the good stuff: when we're talking about Bézier + curves, we're talking about the things that you can see in the + following graphics. They run from some start point to some end + point, with their curvature influenced by one or more "intermediate" + control points. Now, because all the graphics on this page are + interactive, go manipulate those curves a bit: click-drag the + points, and see how their shape changes based on what you do. +

+
+ + + + Scripts are disabled. Showing fallback image. + + + + + + + Scripts are disabled. Showing fallback image. + + +
+ +

+ These curves are used a lot in computer aided design and computer + aided manufacturing (CAD/CAM) applications, as well as in graphic + design programs like Adobe Illustrator and Photoshop, Inkscape, + GIMP, etc. and in graphic technologies like scalable vector graphics + (SVG) and OpenType fonts (TTF/OTF). A lot of things use Bézier + curves, so if you want to learn more about them... prepare to get + your learn on! +

+
+
+

So what makes a Bézier Curve?

+

+ Playing with the points for curves may have given you a feel for how + Bézier curves behave, but what are Bézier curves, really? + There are two ways to explain what a Bézier curve is, and they turn + out to be the entirely equivalent, but one of them uses complicated + maths, and the other uses really simple maths. So... let's start + with the simple explanation: +

+

+ Bézier curves are the result of + linear interpolations. That sounds complicated but you've been doing linear + interpolation since you were very young: any time you had to point + at something between two other things, you've been applying linear + interpolation. It's simply "picking a point between two points". +

+

+ If we know the distance between those two points, and we want a new + point that is, say, 20% the distance away from the first point (and + thus 80% the distance away from the second point) then we can + compute that really easily: +

+ +

+ So let's look at that in action: the following graphic is + interactive in that you can use your up and down arrow keys to + increase or decrease the interpolation ratio, to see what happens. + We start with three points, which gives us two lines. Linear + interpolation over those lines gives us two points, between which we + can again perform linear interpolation, yielding a single point. And + that point —and all points we can form in this way for all ratios + taken together— form our Bézier curve: +

+ + + + Scripts are disabled. Showing fallback image. + + + +

And that brings us to the complicated maths: calculus.

+

+ While it doesn't look like that's what we've just done, we actually + just drew a quadratic curve, in steps, rather than in a single go. + One of the fascinating parts about Bézier curves is that they can + both be described in terms of polynomial functions, as well as in + terms of very simple interpolations of interpolations of [...]. + That, in turn, means we can look at what these curves can do based + on both "real maths" (by examining the functions, their derivatives, + and all that stuff), as well as by looking at the "mechanical" + composition (which tells us, for instance, that a curve will never + extend beyond the points we used to construct it). +

+

+ So let's start looking at Bézier curves a bit more in depth: their + mathematical expressions, the properties we can derive from them, + and the various things we can do to, and with, Bézier curves. +

+
+
+
+ + + + diff --git a/index.en-GB.html b/index.html similarity index 97% rename from index.en-GB.html rename to index.html index 6e3175c7..52d01a0e 100644 --- a/index.en-GB.html +++ b/index.html @@ -49,6 +49,12 @@ A free, online book for when you really need to know how to do Bézier things. + Read this in your own language: +
diff --git a/index.template.html b/index.template.html index 0e2200a5..1c582cb4 100644 --- a/index.template.html +++ b/index.template.html @@ -8,6 +8,8 @@ A Primer on Bézier Curves + {{ base }} + @@ -37,6 +39,8 @@

A Primer on Bézier Curves

A free, online book for when you really need to know how to do Bézier things.

+ {{ langSwitchLabel }} +
    {{ langSwitcher }}
diff --git a/index.ja-JP.html b/ja-JP/index.html similarity index 53% rename from index.ja-JP.html rename to ja-JP/index.html index fce0b7a8..d21de875 100644 --- a/index.ja-JP.html +++ b/ja-JP/index.html @@ -7,6 +7,8 @@ A Primer on Bézier Curves + + + Read this in your own language: +
@@ -57,6 +65,7 @@
  1. バッとした導入
  2. +
  3. So what makes a Bézier Curve?
@@ -110,6 +119,78 @@ graphics)・OpenTypeフォント(otf/ttf)のようなグラフィック技術でも利用されています。ベジエ曲線はたくさんのものに使われていますので、これについてもっと詳しく学びたいのであれば……さあ、準備しましょう!

+
+

So what makes a Bézier Curve?

+

+ Playing with the points for curves may have given you a feel for how + Bézier curves behave, but what are Bézier curves, really? + There are two ways to explain what a Bézier curve is, and they turn + out to be the entirely equivalent, but one of them uses complicated + maths, and the other uses really simple maths. So... let's start + with the simple explanation: +

+

+ Bézier curves are the result of + linear interpolations. That sounds complicated but you've been doing linear + interpolation since you were very young: any time you had to point + at something between two other things, you've been applying linear + interpolation. It's simply "picking a point between two points". +

+

+ If we know the distance between those two points, and we want a new + point that is, say, 20% the distance away from the first point (and + thus 80% the distance away from the second point) then we can + compute that really easily: +

+ +

+ So let's look at that in action: the following graphic is + interactive in that you can use your up and down arrow keys to + increase or decrease the interpolation ratio, to see what happens. + We start with three points, which gives us two lines. Linear + interpolation over those lines gives us two points, between which we + can again perform linear interpolation, yielding a single point. And + that point —and all points we can form in this way for all ratios + taken together— form our Bézier curve: +

+ + + + JSがなくて、画像を表示しています。 + + + +

And that brings us to the complicated maths: calculus.

+

+ While it doesn't look like that's what we've just done, we actually + just drew a quadratic curve, in steps, rather than in a single go. + One of the fascinating parts about Bézier curves is that they can + both be described in terms of polynomial functions, as well as in + terms of very simple interpolations of interpolations of [...]. + That, in turn, means we can look at what these curves can do based + on both "real maths" (by examining the functions, their derivatives, + and all that stuff), as well as by looking at the "mechanical" + composition (which tells us, for instance, that a curve will never + extend beyond the points we used to construct it). +

+

+ So let's start looking at Bézier curves a bit more in depth: their + mathematical expressions, the properties we can derive from them, + and the various things we can do to, and with, Bézier curves. +

+
diff --git a/tools/build-index.js b/tools/build-index.js index c975ec0d..82845433 100644 --- a/tools/build-index.js +++ b/tools/build-index.js @@ -25,158 +25,23 @@ **********************************************************************/ const fs = require("fs-extra"); -const glob = require("glob"); const path = require("path"); -const marked = require("marked"); -const prettier = require("prettier"); -const latexToSVG = require("./latex-to-svg"); -const nunjucks = require("nunjucks"); -nunjucks.configure(".", { autoescape: false }); - -// make sure we know what our base location is -const BASEDIR = path.join(__dirname, ".."); - -// bundle all content in a specific locale for use by the app -const defaultLocale = "en-GB"; -var locale = defaultLocale; -var lpos = process.argv.indexOf("--locale"); -if (lpos !== -1) locale = process.argv[lpos + 1]; +const getAllChapterFiles = require("./build/get-all-chapter-files.js"); +const processLocale = require("./build/process-locale.js"); +const createIndexPages = require("./build/create-index-page.js"); // main entry point (async function () { - const locales = await findLocales(); - Object.keys(locales).forEach(async (locale) => { - const chapters = await processLocale(locale, locales[locale]); - createIndexPages(locale, chapters); + const chapterFiles = await getAllChapterFiles(); + const languageCodes = Object.keys(chapterFiles); + const sectionList = fs + .readdirSync(`chapters`) + .filter((v) => v.indexOf(`.`) === -1) + .map((v) => path.posix.join(__dirname.split(path.sep).join(path.posix.sep), `..`, `chapters`, v)); + languageCodes.forEach(async (locale) => { + const chapters = await processLocale(locale, chapterFiles, sectionList); + createIndexPages(locale, chapters, languageCodes); }); -})(); - -// functions - -/** - * ...docs go here... - */ -function findLocales() { - return new Promise((resolve, reject) => { - glob(path.join(BASEDIR, `chapters/**/content*md`), (err, files) => { - if (err) reject(err); - - const locales = {}; - - files.forEach((file) => { - let locale = file.match(/content\.([^.]+)\.md/)[1]; - if (!locales[locale]) { - locales[locale] = []; - } - locales[locale].push(file); - }); - - resolve(locales); - }); - }); -} - -/** - * ...docs go here... - */ -async function processLocale(locale, files) { - const chapters = {}; - - await Promise.all( - files.map(async (file) => { - const chapter = file.match(/chapters\/([^/]+)\/content./)[1]; - const markdown = fs.readFileSync(file).toString("utf8"); - const replaced = nunjucks.renderString(markdown, { - disableMessage: `${getDisabledMessage(locale)}`, - }); - const converted = await convertMarkDown(replaced); - chapters[chapter] = converted; - }) - ); - - return chapters; -} - -/** - * ...docs go here - */ -function getDisabledMessage(locale) { - const localizedMessages = { - "en-GB": `Scripts are disabled. Showing fallback image.`, - "zh-CN": `脚本已禁用,并显示后备图像。`, - "ja-JP": `JSがなくて、画像を表示しています。`, - }; - return localizedMessages[locale]; -} - -/** - * ...docs go here... - */ -async function createIndexPages(locale, chapters) { - const toc = {}; - const sections = Object.keys(chapters).map((section) => { - let content = chapters[section]; - toc[section] = content.match(/

([^<]+)<\/h1>/)[1]; - return `
\n${content}
`; - }); - - const index = nunjucks.render(`index.template.html`, { - locale, - toc: Object.keys(toc) - .map((id) => `
  • ${toc[id]}
  • `) - .join(`\n`), - chapters: sections.join(`\n`), - }); - - const data = prettier.format(index, { parser: `html` }); - fs.writeFileSync(`index.${locale}.html`, data, `utf8`); -} - -/** - * ...docs go here... - */ -async function convertMarkDown(markdown) { - // preprocess marrkdown to extract LaTeX sections - let latexSection = 0, - pos = -1, - data = markdown, - latex = [], - startmark = ``; - - do { - pos = data.indexOf(startmark); - if (pos !== -1) { - let endpos = data.indexOf(endmark, pos) + endmark.length; - let key = `latex${latexSection}`; - latex[key] = data.substring( - pos + startmark.length, - endpos - endmark.length - ); - data = `${data.slice(0, pos)}{{ ${key} }}${data.slice(endpos)}`; - } - } while (pos !== -1); - - await Promise.all( - Object.keys(latex).map(async (key) => { - const svg = await latexToSVG(latex[key]); - return (latex[key] = svg); - }) - ); - - let converted = marked(data, { - gfm: true, - headerIds: false, - mangle: false, - }) - // sigh... - .replace(/&/g, "&") - .replace(/'/g, "'") - .replace(/"/g, '"') - .replace(/

    {{/g, `{{`) - .replace(/}}<\/p>/g, `}}`); - - return nunjucks.renderString(converted, latex); -} +})(); \ No newline at end of file diff --git a/tools/build/convert-markdown.js b/tools/build/convert-markdown.js new file mode 100644 index 00000000..64917645 --- /dev/null +++ b/tools/build/convert-markdown.js @@ -0,0 +1,52 @@ +const marked = require("marked"); +const latexToSVG = require("./latex-to-svg"); +const nunjucks = require("nunjucks"); +nunjucks.configure(".", { autoescape: false }); + + +/** + * ...docs go here... + */ +module.exports = async function convertMarkDown(markdown) { + // preprocess marrkdown to extract LaTeX sections + let latexSection = 0, + pos = -1, + data = markdown, + latex = [], + startmark = ``; + + do { + pos = data.indexOf(startmark); + if (pos !== -1) { + let endpos = data.indexOf(endmark, pos) + endmark.length; + let key = `latex${latexSection}`; + latex[key] = data.substring( + pos + startmark.length, + endpos - endmark.length + ); + data = `${data.slice(0, pos)}{{ ${key} }}${data.slice(endpos)}`; + } + } while (pos !== -1); + + await Promise.all( + Object.keys(latex).map(async (key) => { + const svg = await latexToSVG(latex[key]); + return (latex[key] = svg); + }) + ); + + let converted = marked(data, { + gfm: true, + headerIds: false, + mangle: false, + }) + // sigh... + .replace(/&/g, "&") + .replace(/'/g, "'") + .replace(/"/g, '"') + .replace(/

    {{/g, `{{`) + .replace(/}}<\/p>/g, `}}`); + + return nunjucks.renderString(converted, latex); + } diff --git a/tools/build/create-index-page.js b/tools/build/create-index-page.js new file mode 100644 index 00000000..1976a7fe --- /dev/null +++ b/tools/build/create-index-page.js @@ -0,0 +1,50 @@ +const fs = require("fs-extra"); +const path = require("path"); +const config = require("../../config.json"); +const defaultLocale = config.defaultLocale +const prettier = require("prettier"); +const generateLangSwitcher = require("./generate-lang-switcher.js"); +const nunjucks = require("nunjucks"); + +nunjucks.configure(".", { autoescape: false }); + + +/** + * ...docs go here... + */ +module.exports = async function createIndexPages(locale, chapters, languages) { + let base = ``; + + if (locale !== defaultLocale) { + base = ``; + } + + const langSwitcher = generateLangSwitcher(locale, languages, defaultLocale); + + const toc = {}; + + const sections = Object.keys(chapters).map((section) => { + let content = chapters[section]; + let title = content.match(/

    ([^<]+)<\/h1>/)[1]; + toc[section] = `
  • ${title}
  • `; + return `
    \n${content}
    `; + }); + + const index = nunjucks.render(`index.template.html`, { + base, + locale, + langSwitchLabel: config.langSwitchLabel[locale], + langSwitcher, + toc: Object.values(toc).join(`\n`), + chapters: sections.join(`\n`), + }); + + const data = prettier.format(index, { parser: `html` }); + + if (locale === defaultLocale) { + fs.writeFileSync(`index.html`, data, `utf8`); + } else { + fs.ensureDir(locale); + fs.writeFileSync(path.join(locale, `index.html`), data, `utf8`); + } +}; diff --git a/tools/build/generate-lang-switcher.js b/tools/build/generate-lang-switcher.js new file mode 100644 index 00000000..1ca13d96 --- /dev/null +++ b/tools/build/generate-lang-switcher.js @@ -0,0 +1,25 @@ +const config = require("../../config.json"); +const defaultLocale = config.defaultLocale + + +module.exports = function generateLangSwitcher(currentLocale, allLocales) { + return allLocales + .map((locale) => { + let link; + if (currentLocale === defaultLocale) { + if (locale === defaultLocale) { + link = `./index.html`; + } else { + link = `${locale}/index.html`; + } + } else { + if (locale === defaultLocale) { + link = `../index.html`; + } else { + link = `../${locale}/index.html`; + } + } + return `
  • ${config.localeName[locale]}
  • `; + }) + .join(`\n`); +}; diff --git a/tools/build/get-all-chapter-files.js b/tools/build/get-all-chapter-files.js new file mode 100644 index 00000000..6451819a --- /dev/null +++ b/tools/build/get-all-chapter-files.js @@ -0,0 +1,29 @@ +const fs = require("fs-extra"); +const glob = require("glob"); +const path = require("path"); + +// make sure we know what our base location is +const BASEDIR = path.join(__dirname, "..", ".."); + +/** + * ...docs go here... + */ +module.exports = function getAllChapterFiles() { + return new Promise((resolve, reject) => { + glob(path.join(BASEDIR, `chapters/**/content*md`), (err, files) => { + if (err) reject(err); + + const locales = {}; + + files.forEach((file) => { + let locale = file.match(/content\.([^.]+)\.md/)[1]; + if (!locales[locale]) { + locales[locale] = []; + } + locales[locale].push(file); + }); + + resolve(locales); + }); + }); + } diff --git a/tools/latex-to-svg.js b/tools/build/latex-to-svg.js similarity index 96% rename from tools/latex-to-svg.js rename to tools/build/latex-to-svg.js index ab631c18..83668bc2 100644 --- a/tools/latex-to-svg.js +++ b/tools/build/latex-to-svg.js @@ -1,10 +1,10 @@ const fs = require("fs-extra"); const path = require("path"); const crypto = require("crypto"); -const cleanUp = require("./cleanup"); +const cleanUp = require("../cleanup"); const execSync = require("child_process").execSync; -const baseDir = path.join(__dirname, `..`, `images`, `latex`); +const baseDir = path.join(__dirname, `..`, `..`, `images`, `latex`); fs.ensureDirSync(baseDir); const sourceDir = path.join(baseDir, `source`); diff --git a/tools/build/process-locale.js b/tools/build/process-locale.js new file mode 100644 index 00000000..8b4c980f --- /dev/null +++ b/tools/build/process-locale.js @@ -0,0 +1,54 @@ +const fs = require("fs-extra"); +const path = require("path"); +const config = require("../../config.json"); +const defaultLocale = config.defaultLocale +const convertMarkDown = require("./convert-markdown.js"); +const nunjucks = require("nunjucks"); + +nunjucks.configure(".", { autoescape: false }); + + +/** + * ...docs go here... + */ +module.exports = async function processLocale( + locale, + chapterFiles, + sectionList +) { + const localeFiles = chapterFiles[locale]; + let localized = 0; + + // make sure we fall back to en-GB content if there is no localised version + sectionList.forEach((chapterpath) => { + if (localeFiles.every((file) => file.indexOf(chapterpath) === -1)) { + localeFiles.push( + path.posix.join(chapterpath, `content.${defaultLocale}.md`) + ); + } else { + localized++; + } + }); + + if (localized < sectionList.length) { + console.log(`${locale} partially localized: [${localized}/${sectionList.length}]`) + } else { + console.log(`${locale} fully localized.`) + } + + const chapters = {}; + + await Promise.all( + localeFiles.map(async (file) => { + const chapter = file.match(/chapters\/([^/]+)\/content./)[1]; + const markdown = fs.readFileSync(file).toString("utf8"); + const replaced = nunjucks.renderString(markdown, { + disableMessage: `${config.disabledMessage[locale]}`, + }); + const converted = await convertMarkDown(replaced); + chapters[chapter] = converted; + }) + ); + + return chapters; +}; diff --git a/index.zh-CN.html b/zh-CN/index.html similarity index 51% rename from index.zh-CN.html rename to zh-CN/index.html index 6498bf49..9ff1d9c6 100644 --- a/index.zh-CN.html +++ b/zh-CN/index.html @@ -7,6 +7,8 @@ A Primer on Bézier Curves + + + Read this in your own language: +
    @@ -57,6 +65,7 @@
    1. 简单介绍
    2. +
    3. So what makes a Bézier Curve?
    @@ -108,6 +117,78 @@ Gimp等等。还可以应用在一些图形技术中,像矢量图形(SVG)和OpenType字体(ttf/otf)。许多东西都用到贝塞尔曲线,如果你想更了解它们...准备好继续往下学吧!

    +
    +

    So what makes a Bézier Curve?

    +

    + Playing with the points for curves may have given you a feel for how + Bézier curves behave, but what are Bézier curves, really? + There are two ways to explain what a Bézier curve is, and they turn + out to be the entirely equivalent, but one of them uses complicated + maths, and the other uses really simple maths. So... let's start + with the simple explanation: +

    +

    + Bézier curves are the result of + linear interpolations. That sounds complicated but you've been doing linear + interpolation since you were very young: any time you had to point + at something between two other things, you've been applying linear + interpolation. It's simply "picking a point between two points". +

    +

    + If we know the distance between those two points, and we want a new + point that is, say, 20% the distance away from the first point (and + thus 80% the distance away from the second point) then we can + compute that really easily: +

    + +

    + So let's look at that in action: the following graphic is + interactive in that you can use your up and down arrow keys to + increase or decrease the interpolation ratio, to see what happens. + We start with three points, which gives us two lines. Linear + interpolation over those lines gives us two points, between which we + can again perform linear interpolation, yielding a single point. And + that point —and all points we can form in this way for all ratios + taken together— form our Bézier curve: +

    + + + + 脚本已禁用,并显示后备图像。 + + + +

    And that brings us to the complicated maths: calculus.

    +

    + While it doesn't look like that's what we've just done, we actually + just drew a quadratic curve, in steps, rather than in a single go. + One of the fascinating parts about Bézier curves is that they can + both be described in terms of polynomial functions, as well as in + terms of very simple interpolations of interpolations of [...]. + That, in turn, means we can look at what these curves can do based + on both "real maths" (by examining the functions, their derivatives, + and all that stuff), as well as by looking at the "mechanical" + composition (which tells us, for instance, that a curve will never + extend beyond the points we used to construct it). +

    +

    + So let's start looking at Bézier curves a bit more in depth: their + mathematical expressions, the properties we can derive from them, + and the various things we can do to, and with, Bézier curves. +

    +