From 6ddee66a33144bb7c05b89ef7e3efb8045027eb0 Mon Sep 17 00:00:00 2001 From: Pomax Date: Tue, 4 Aug 2020 14:59:56 -0700 Subject: [PATCH] movables --- README.md | 58 ++++++ chapters/introduction/content.en-GB.md | 8 +- chapters/introduction/cubic.js | 15 +- chapters/introduction/quadratic.js | 15 +- chapters/preface/content.en-GB.md | 45 +++++ chapters/preface/content.ja-JP.md | 43 ++++ chapters/preface/content.zh-CN.md | 36 ++++ chapters/preface/index.js | 2 + chapters/whatis/content.en-GB.md | 4 +- chapters/whatis/interpolation.js | 109 ++++++++++- config.json | 18 -- index.html | 206 ++++++++++++++++++-- index.template.html | 16 +- ja-JP/index.html | 106 +++++++++- lib/custom-element/api/base-api.js | 52 +++-- lib/custom-element/api/graphics-api.js | 97 +++++++-- lib/custom-element/api/types/bezier/base.js | 8 + lib/custom-element/api/types/point.js | 2 +- lib/custom-element/graphics-element.js | 2 +- locale-strings.json | 43 ++++ style.css | 33 ++++ tools/build.js | 52 ++--- tools/build/create-index-page.js | 32 ++- tools/build/generate-lang-switcher.js | 6 +- tools/build/get-all-chapter-files.js | 2 +- tools/build/process-locale.js | 41 ++-- zh-CN/index.html | 94 ++++++++- 27 files changed, 978 insertions(+), 167 deletions(-) create mode 100644 README.md create mode 100644 chapters/preface/content.en-GB.md create mode 100644 chapters/preface/content.ja-JP.md create mode 100644 chapters/preface/content.zh-CN.md create mode 100644 chapters/preface/index.js delete mode 100644 config.json create mode 100644 locale-strings.json diff --git a/README.md b/README.md new file mode 100644 index 00000000..6a047cb7 --- /dev/null +++ b/README.md @@ -0,0 +1,58 @@ +# BezierInfo-2 ![CI-CD](https://github.com/Pomax/BezierInfo-2/workflows/CI-CD/badge.svg?branch=master) + +Dev repository for https://Pomax.github.io/bezierinfo + +## Working on the code + +- To run this dev version: `npm start`, then view in the browser on [http://localhost:8080](http://localhost:8080) +- To compile for production: `npm run build`. + +Both operations run continuous linting. Also note that there are specific requirements for development, see the section on dev requirements below. + +## 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 run build`. + +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. diff --git a/chapters/introduction/content.en-GB.md b/chapters/introduction/content.en-GB.md index 3d77814b..66fe1f2f 100644 --- a/chapters/introduction/content.en-GB.md +++ b/chapters/introduction/content.en-GB.md @@ -3,16 +3,16 @@ 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.
- + - + {{ disableMessage }} - + - + {{ disableMessage }} diff --git a/chapters/introduction/cubic.js b/chapters/introduction/cubic.js index 4004f514..69130b7f 100644 --- a/chapters/introduction/cubic.js +++ b/chapters/introduction/cubic.js @@ -1,10 +1,17 @@ setup() { - this.curve = Bezier.create(this, 50,150, 10,10, 190,10, 150,150); + this.curve = Bezier.defaultCubic(this); + setMovable(this.curve.points); } draw() { clear(); - this.curve.drawSkeleton(); - this.curve.drawCurve(); - this.curve.drawPoints(); + const curve = this.curve; + curve.drawSkeleton(); + curve.drawCurve(); + curve.drawPoints(); +} + +onMouseMove() { + this.curve.update(); + redraw(); } diff --git a/chapters/introduction/quadratic.js b/chapters/introduction/quadratic.js index e8cc724a..e0a83171 100644 --- a/chapters/introduction/quadratic.js +++ b/chapters/introduction/quadratic.js @@ -1,10 +1,17 @@ setup() { - this.curve = Bezier.create(this, 10,50, 100,160, 190,50); + this.curve = Bezier.defaultQuadratic(this); + setMovable(this.curve.points); } draw() { clear(); - this.curve.drawSkeleton(); - this.curve.drawCurve(); - this.curve.drawPoints(); + const curve = this.curve; + curve.drawSkeleton(); + curve.drawCurve(); + curve.drawPoints(); +} + +onMouseMove() { + this.curve.update(); + redraw(); } diff --git a/chapters/preface/content.en-GB.md b/chapters/preface/content.en-GB.md new file mode 100644 index 00000000..945ab24a --- /dev/null +++ b/chapters/preface/content.en-GB.md @@ -0,0 +1,45 @@ +# Preface + +In order to draw things in 2D, we usually rely on lines, which typically get classified into two categories: straight lines, and curves. The first of these are as easy to draw as they are easy to make a computer draw. Give a computer the first and last point in the line, and BAM! straight line. No questions asked. + +Curves, however, are a much bigger problem. While we can draw curves with ridiculous ease freehand, computers are a bit handicapped in that they can't draw curves unless there is a mathematical function that describes how it should be drawn. In fact, they even need this for straight lines, but the function is ridiculously easy, so we tend to ignore that as far as computers are concerned; all lines are "functions", regardless of whether they're straight or curves. However, that does mean that we need to come up with fast-to-compute functions that lead to nice looking curves on a computer. There are a number of these, and in this article we'll focus on a particular function that has received quite a bit of attention and is used in pretty much anything that can draw curves: Bézier curves. + +They're named after [Pierre Bézier](https://en.wikipedia.org/wiki/Pierre_B%C3%A9zier), who is principally responsible for making them known to the world as a curve well-suited for design work (publishing his investigations in 1962 while working for Renault), although he was not the first, or only one, to "invent" these type of curves. One might be tempted to say that the mathematician [Paul de Casteljau](https://en.wikipedia.org/wiki/Paul_de_Casteljau) was first, as he began investigating the nature of these curves in 1959 while working at Citroën, and came up with a really elegant way of figuring out how to draw them. However, de Casteljau did not publish his work, making the question "who was first" hard to answer in any absolute sense. Or is it? Bézier curves are, at their core, "Bernstein polynomials", a family of mathematical functions investigated by [Sergei Natanovich Bernstein](https://en.wikipedia.org/wiki/Sergei_Natanovich_Bernstein), whose publications on them date back at least as far as 1912. + +Anyway, that's mostly trivia, what you are more likely to care about is that these curves are handy: you can link up multiple Bézier curves so that the combination looks like a single curve. If you've ever drawn Photoshop "paths" or worked with vector drawing programs like Flash, Illustrator or Inkscape, those curves you've been drawing are Bézier curves. + +But what if you need to program them yourself? What are the pitfalls? How do you draw them? What are the bounding boxes, how do you determine intersections, how can you extrude a curve, in short: how do you do everything that you might want when you do with these curves? That's what this page is for. Prepare to be mathed! + +
+## PS: buy me a coffee? + +If you enjoyed this book enough to print it out, you might be wondering if there is some way to give something back. To answer that question: you can always buy me a coffee, however-much a coffee is where you live, or if you want to pay a fair price for this book, you can buy me a really expensive coffee =) + +This book has grown over the years from a short primer to a 100+ print-page-equivalent ebook on the subject of Bézier curves, and a lot of coffee went into the making of it. I don't regret a minute I spent on writing it, but I can always do with some more coffee to keep on writing! Please visit https://pomax.github.io/bezierinfo and click on the link in the online preface to donate some coffee money. +
+ +—Pomax (or in the tweetworld, [@TheRealPomax](https://twitter.com/TheRealPomax)) + +
+ +## Note: virtually all Bézier graphics are interactive. + +This page uses interactive examples, relying heavily on [Bezier.js](http://pomax.github.io/bezierjs), as well as maths formulae which are typeset into SVG using the [XeLaTeX](https://ctan.org/pkg/xetex) typesetting system and [pdf2svg](https://github.com/dawbarton/pdf2svg) by [David Barton](http://www.cityinthesky.co.uk/). The page is generated offline as a React application. + +## This book is open source. + +This book is an open source software project, and lives on two github repositories. The first is [https://github.com/pomax/bezierinfo](https://github.com/pomax/bezierinfo) and is the purely-for-presentation version you are viewing right now. The other repository is [https://github.com/pomax/BezierInfo-2](https://github.com/pomax/BezierInfo-2), which is the development version, housing all the HTML, JavaScript, and CSS. You can fork either of these, and pretty much do with them as you please, except for passing it off as your own work wholesale, of course =) + +## How complicated is the maths going to be? + +Most of the mathematics in this Primer are early high school maths. If you understand basic arithmetic, and you know how to read English, you should be able to get by just fine. There will at times be *far* more complicated maths, but if you don't feel like digesting them, you can safely skip over them by either skipping over the "detail boxes" in section or by just jumping to the end of a section with maths that looks too involving. The end of sections typically simply list the conclusions so you can just work with those values directly. + +## Questions, comments: + +If you have suggestions for new sections, hit up the [Github issue tracker](https://github.com/pomax/BezierInfo-2/issues) (also reachable from the repo linked to in the upper right). If you have questions about the material, there's currently no comment section while I'm doing the rewrite, but you can use the issue tracker for that as well. Once the rewrite is done, I'll add a general comment section back in, and maybe a more topical "select this section of text and hit the 'question' button to ask a question about it" system. We'll see. + +## Help support the book! + +If you enjoyed this book, or you simply found it useful for something you were trying to get done, and you were wondering how to let me know you appreciated this book, you have two options: you can either head on over to the [Patreon page](https://patreon.com/bezierinfo) for this book, or if you prefer to make a one-time donation, head on over to the [buy Pomax a coffee](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=QPRDLNGDANJSW) page. This work has grown from a small primer to a 100-plus print-page-equivalent reader on the subject of Bézier curves over the years, and a lot of coffee went into the making of it. I don't regret a minute I spent on writing it, but I can always do with some more coffee to keep on writing! + +
diff --git a/chapters/preface/content.ja-JP.md b/chapters/preface/content.ja-JP.md new file mode 100644 index 00000000..f353766f --- /dev/null +++ b/chapters/preface/content.ja-JP.md @@ -0,0 +1,43 @@ +# まえがき + +2次元上になにかを描くとき、普通は線を使いますが、これは直線と曲線の2つに分類することができます。直線を描くのはとても簡単で、これをコンピュータに描かせるのも容易です。直線の始点と終点をコンピュータに与えてやれば、ポン!直線が描けました。疑問の余地もありません。 + +しかしながら、曲線の方はもっと大きな問題です。私たちはフリーハンドでいとも簡単に曲線を描くことができますが、コンピュータの方は少し不利です。曲線の描き方を表した数学的な関数が与えられないと、コンピュータは曲線を描くことができないのです。実際には、直線でさえも関数が必要になります。直線の関数はとても簡単なので、わたしたちはよく無視してしまいますが、コンピュータにとっては直線であれ曲線であれ、線はすべて「関数」なのです。しかしこれは、コンピュータで速く計算できて、きれいな曲線が得られるような関数を見つける必要がある、ということになります。そのような関数はたくさんありますが、多くの関心を集め続け、そしてどんな場面でも使われている、ある特定の関数に対してこの記事では焦点を絞ります。この関数は「ベジエ」曲線を描きます。 + +ベジエ曲線は[Pierre Bézier](https://ja.wikipedia.org/wiki/ピエール・ベジェ)から名付けられました。この曲線がデザイン作業に適していることを世界に知らしめたのが、彼なのです(ルノーに勤務し、1962年にその研究を発表しました)。ただし、この曲線を「発明」したのは彼が最初で唯一というわけではありません。数学者[Paul de Casteljau](https://en.wikipedia.org/wiki/Paul_de_Casteljau)はシトロエンで働いていた1959年、この曲線の性質について研究し、ベジエ曲線の非常にエレガントな描き方を発見しました。これが最初だと言う人もいます。しかしながら、de Casteljauは自分の成果を発表しなかったため、「誰が最初か?」という問いに答えるのがとても難しくなっています。またベジエ曲線は、核心的には[Sergei Natanovich Bernstein](https://ja.wikipedia.org/wiki/セルゲイ・ベルンシュテイン)が研究した「ベルンシュタイン多項式」という数学関数の一種ですが、こちらの公刊に関しては少なくとも1912年まで遡ることができます。いずれにせよ、これらはほとんど瑣末なことです。より注目すべきなのは、ベジエ曲線は取り扱いに便利だいうことです。たとえば複数のベジエ曲線を繋いで、1つの曲線に見えるようにすることができます。もしあなたがPhotoshopで「パス」を描いたり、FlashやIllustrator、Inkscapeのようなベクタードローイングソフトを使ったことがあるのであれば、そこで描いてきた曲線はベジエ曲線です。 + +では、これを自分でプログラムしなければならないとなったらどうでしょう?ハマりどころは何でしょうか?どうやってベジエ曲線を描くのでしょう?バウンディングボックスとは何で、どうやって交点を求め、どうやったら曲線を押し出せるのでしょうか?つまるところ、ベジエ曲線に対して行いたいあらゆる操作は、どのようにすればいいのでしょう?このページはそれに答えるためにあります。数学にとりかかりましょう! + +
+## 追伸:コーヒーをおごってくれませんか? + +この本のことを印刷するほど気に入ったのであれば、あなたは「何かお礼をする方法はないか」と考えているかもしれません。この質問に対する答えはこうです:いつでもわたしにコーヒーをおごってください。あなたが住んでいるところの、コーヒー1杯の値段でかまいません。この本にかなりの金額を支払いたいのであれば、非常に高価なコーヒーをおごってくれてもかまいません =) + +この本は小さな入門からはじまり、印刷85ページ以上に相当するようなベジエ曲線の電子書籍へと、年々成長してきています。そして、多くのコーヒーがその執筆に費やされてきました。わたしは執筆に使った時間が惜しいとは思いませんが、もう少しコーヒーがあれば、ずっと書き続けることができるのです!https://pomax.github.io/bezierinfoにアクセスしてオンライン版のまえがきにあるリンクをクリックし、コーヒー代を寄付してください。 +
+ +—Pomax (Twitter上では[@TheRealPomax](https://twitter.com/TheRealPomax)) + +
+ +## 注:ベジエの図はすべてインタラクティブになっています。 + +このページでは[Bezier.js](http://pomax.github.io/bezierjs)を活用し、インタラクティブな例示を行っています。また、[MathJax](http://mathjax.org)というすばらしいライブラリによって、(LaTeX式の)「本物」の数学組版を行っています。このページはWebpackを使い、Reactアプリケーションとしてオフラインで生成されていますが、このために「ソースを表示」オプションを追加することがかなり難しくなってしまいました。これをどうやって追加すべきかは今もまだ考え中ですが、かといって、数年ぶりとなる今回の更新を延期したくはありませんでした。 + +## 本書はオープンソースです。 + +この本はオープンソースソフトウェアのプロジェクトで、2つのGitHubリポジトリ上に存在しています。1つ目の[https://github.com/pomax/bezierinfo](https://github.com/pomax/bezierinfo)は表示のためだけに用意されたバージョンで、あなたが今読んでいるものです。もう一方の[https://github.com/pomax/BezierInfo-2](https://github.com/pomax/BezierInfo-2)は開発版で、すべてのHTML・JavaScript・CSSが含まれています。どちらのリポジトリもフォークすることができますし、あなたの好きなように使ってかまいません。ただし、これを自分の成果だと騙って売ることはもちろん除きます。=) + +## 数学はどこまで難しくなりますか? + +この入門に出てくる数学は、大半が高校初年度程度です。基本的な計算を理解していて、英語の読み方が分かっていれば、こなすことができるはずです。時には*ずっと*難しい数学も出てきますが、もし理解できないように感じたら、そこは読み飛ばしても大丈夫です。節の中の「詳細欄」を飛ばしてもいいですし、厄介そうな数学があれば節の最後まで読み飛ばしてもかまいません。各節の最後にはたいてい結論を並べてありますので、これを直に利用することもできます。 + +## 質問・コメント: + +新しい節の提案があれば、[GitHubのissueトラッカー](https://github.com/pomax/BezierInfo-2/issues)をクリックしてください(右上にあるリポジトリのリンクからでもたどり着けます)。改訂中のため、現在はこのページにはコメント欄がありませんが、内容に関する質問がある場合にもissueトラッカーを使ってかまいません。改訂が完了したら、総合的なコメント欄を復活させる予定です。あるいは、「質問対象の節を選択して『質問』ボタンをクリック」するような項目別のシステムになるかもしれません。いずれわかります。 + +## コーヒーをおごってくれませんか? + +この本が気に入った場合や、取り組んでいたことに役に立つと思った場合、あるいは、この本への感謝をわたしに伝えるにはどうすればいいのかわからない場合。そのような場合には、[わたしにコーヒーをおごってください](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=QPRDLNGDANJSW)。あなたが住んでいるところのコーヒー1杯の値段でかまいません。この本は小さな入門からはじまり、印刷で70ページほどに相当するようなベジエ曲線の読み物へと、年々成長してきています。そして、多くのコーヒーが執筆に費やされてきました。わたしは執筆に使った時間が惜しいとは思いませんが、もう少しコーヒーがあれば、ずっと書き続けることができるのです! + +
diff --git a/chapters/preface/content.zh-CN.md b/chapters/preface/content.zh-CN.md new file mode 100644 index 00000000..5315ccbb --- /dev/null +++ b/chapters/preface/content.zh-CN.md @@ -0,0 +1,36 @@ +# 序言 + +我们通常用线条来绘制2D图形,大致分为两种线条:直线和曲线。不论我们动手还是用电脑,都能很容易地画出第一种线条。只要给电脑起点和终点,砰!直线就画出来了。没什么好疑问的。 + +然而,绘制曲线却是个大问题。虽然我们可以很容易地徒手画出曲线,但除非给出描述曲线的数学函数,不然计算机无法画出曲线。实际上,画直线时也需要数学函数,但画直线所需的方程式很简单,我们在这里不去考虑。在计算机看来,所有线条都是“函数”,不管它们是直线还是曲线。然而,这就表示我们需要找到能在计算机上表现良好的曲线方程。这样的曲线有很多种,在本文我们主要关注一类特殊的、备受关注的函数,基本上任何画曲线的地方都会用到它:贝塞尔曲线。 + +它们是以[Pierre Bézier](https://en.wikipedia.org/wiki/Pierre_B%C3%A9zier)命名的,尽管他并不是第一个,或者说唯一“发明”了这种曲线的人,但他让世界知道了这种曲线十分适合设计工作(在1962年为Renault工作并发表了他的研究)。有人也许会说数学家[Paul de Casteljau](https://en.wikipedia.org/wiki/Paul_de_Casteljau)是第一个发现这类曲线特性的人,在Citroën工作时,他提出了一种很优雅的方法来画这些曲线。然而,de Casteljau没有发表他的工作,这使得“谁先发现”这一问题很难有一个确切的答案。 +贝塞尔曲线本质上是伯恩斯坦多项式,这是[Sergei Natanovich Bernstein](https://en.wikipedia.org/wiki/Sergei_Natanovich_Bernstein)研究的一种数学函数,关于它们的出版物至少可以追溯到1912年。无论如何,这些都只是一些冷知识,你可能更在意的是这些曲线很方便:你可以连接多条贝塞尔曲线,并且连接起来的曲线看起来就像是一条曲线。甚至,在你在Photoshop中画“路径”或使用一些像Flash、Illustrator和Inkscape这样的矢量绘图程序时,所画的曲线都是贝塞尔曲线。 + +那么,要是你自己想编程实现它们呢?有哪些陷阱?你怎么画它们?包围盒是怎么样的,怎么确定交点,怎么拉伸曲线,简单来说:你怎么对曲线做一切你想做的事?这就是这篇文章想说的。准备好学习一些数学吧! + +—Pomax (推特账号, [@TheRealPomax](https://twitter.com/TheRealPomax)) + +
+ +## 注意:几乎所有的贝塞尔图形都是可交互的。 + +这个页面使用了基于[Bezier.js](http://pomax.github.io/bezierjs) 的可交互例子,还有一些用[MathJax](http://MathJax.org) 排版的“真正的”数学(LaTeX形式)。这个页面是用Webpack离线生成的React应用,这便让加入“查看源码”选项更具挑战性了。我仍然试图将它们添加回来,但跟前几年的版本相比,不觉得它能够支撑部署这个更新。 + +## 这本书是开源的。 + +这本书是开源的软件项目,现有两个github仓库。第一个[https://github.com/pomax/bezierinfo]( https://github.com/pomax/bezierinfo ),它是你现在在看的这个,纯粹用来展示的版本。另外一个[https://github.com/pomax/BezierInfo-2]( https://github.com/pomax/BezierInfo-2 ),是带有所有html, javascript和css的开发版本。你可以fork任意一个,随便做些什么,当然除了把它当作自己的作品来商用。 =) + +## 用到的数学将有多复杂? + +这份入门读物用到的大部分数学知识都是高中所学的。如果你理解基本的计算并能看懂英文的话,就能上手这份材料。有时候会用到*复杂*一点的数学,但如果你不想深究它们,可以选择跳过段落里的“详解”部分,或者直接跳到章节末尾,避开那些看起来很深入的数学。章节的末尾往往会列出一些结论,因此你可以直接利用这些结论。 + +## 问题,评论: + +如果你有对于新章节的一些建议,点击 [Github issue tracker](https://github.com/pomax/BezierInfo-2/issues) (也可以点右上角的repo链接)。如果你有关于材料的一些问题,由于我现在在做改写工作,目前没有评论功能,但你可以用issue跟踪来发表评论。一旦完成重写工作,我会把评论功能加上,或者会有“选择文字段落,点击‘问题’按钮来提问”的系统。到时候我们看看。 + +## 给我买杯咖啡? + +如果你很喜欢这本书,或发现它对你要做的事很有帮助,或者你想知道怎么表达自己对这本书的感激,你可以 [给我买杯咖啡](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=QPRDLNGDANJSW) ,所少钱取决于你。这份工作持续了很多年,从一份小小的简要到70多页关于贝塞尔曲线的读物,在完成它的过程中倾注了很多咖啡。我从未后悔花在这上面的每一分钟,但如果有更多咖啡的话,我可以坚持写下去! + +
diff --git a/chapters/preface/index.js b/chapters/preface/index.js new file mode 100644 index 00000000..05dcdb47 --- /dev/null +++ b/chapters/preface/index.js @@ -0,0 +1,2 @@ +var generateBase = require("../../generate-base"); +module.exports = generateBase("preface"); \ No newline at end of file diff --git a/chapters/whatis/content.en-GB.md b/chapters/whatis/content.en-GB.md index 03fe774b..9960c8d7 100644 --- a/chapters/whatis/content.en-GB.md +++ b/chapters/whatis/content.en-GB.md @@ -21,9 +21,9 @@ Given \left ( 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: - + - + {{ disableMessage }} diff --git a/chapters/whatis/interpolation.js b/chapters/whatis/interpolation.js index 8baa3525..6451ef8c 100644 --- a/chapters/whatis/interpolation.js +++ b/chapters/whatis/interpolation.js @@ -1 +1,108 @@ -interpolation.js \ No newline at end of file +setup() { + this.step = 25; + this.curve = Bezier.create(this, 70,250, 20,110, 250,60); + setMovable(this.curve.points); +} + +draw() { + resetTransform(); + clear(`white`); + this.drawBasics(); + this.drawPointCurve(); + this.drawInterpolations(); +} + +drawBasics() { + setStroke(`black`); + setFill(`black`); + this.curve.drawSkeleton(); + this.curve.drawPoints(); + text(`First linear interpolation, spaced ${this.step}% (${Math.floor(99/this.step)} steps)`, {x:5, y:15}); + + translate(this.height, 0); + + line({x:0, y:0}, {x:0, y:this.height}); + this.curve.drawSkeleton(); + this.curve.drawPoints(); + text(`Second interpolation, between each generated pair`, {x:5, y:15}); + + translate(this.height, 0); + + line({x:0, y:0}, {x:0, y:this.height}); + this.curve.drawSkeleton(); + this.curve.drawPoints(); + text(`Curve points generated this way`, {x:5, y:15}); +} + +drawPointCurve() { + setStroke(`lightgrey`); + for(let i=1, e=50, p; i<=e; i++) { + p = this.curve.get(i/e); + circle(p, 1); + } +} + +drawInterpolations() { + for(let i=this.step; i<100; i+=this.step) { + resetTransform(); + this.setIterationColor(i); + let [np2, np3] = this.drawFirstInterpolation(this.curve.points, i); + let np4 = this.drawSecondInterpolation(np2, np3, i); + this.drawOnCurve(np4, i); + } +} + +setIterationColor(i) { + let c = `#${(2*i).toString(16)}00${(255 - 2*i).toString(16)}`; + setFill(c); + setStroke(`${c}55`); +} + +drawFirstInterpolation(p, i) { + let np2 = p[1].subtract(p[1].subtract(p[0]).scale(1 - i/100)); + circle(np2, 5); + text(`${i}%`, np2.add({x:10,y:0})); + + let np3 = p[2].subtract(p[2].subtract(p[1]).scale(1 - i/100)); + circle(np3, 5); + text(`${i}%`, np3.add({x:-10,y:-15})); + + return [np2, np3]; +} + +drawSecondInterpolation(np2, np3, i) { + translate(this.height, 0); + + line(np2, np3); + circle(np2, 5); + circle(np3, 5); + + let np4 = np3.subtract(np3.subtract(np2).scale(1 - i/100)); + circle(np4, 2); + text(`${i}%`, np4.add({x:10,y:10})); + + return np4; +} + +drawOnCurve(np4, i) { + translate(this.height, 0); + circle(np4, 2); + text(`ratio = ${i/100}`, np4.add({x:10,y:15})); +} + +onKeyDown() { + if (this.keyboard[`ArrowDown`]) { + this.step--; + if (this.step < 10) this.step = 10; + } + if (this.keyboard[`ArrowUp`]) { + this.step++; + if (this.step > 90) this.step = 90; + } + redraw(); +} + +onMouseMove() { + this.curve.update(); + redraw(); +} \ No newline at end of file diff --git a/config.json b/config.json deleted file mode 100644 index a74c6956..00000000 --- a/config.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "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/index.html b/index.html index 52d01a0e..ca007bcf 100644 --- a/index.html +++ b/index.html @@ -19,7 +19,7 @@ property="og:description" content="A detailed explanation of Bézier curves, and how to do the many things that we commonly want to do with them." /> - + @@ -55,12 +55,184 @@
  • 日本語
  • 中文
  • + Don't see your language listed? + Help translate this content!
    -
    +
    +
    +

    Preface

    +

    + In order to draw things in 2D, we usually rely on lines, which + typically get classified into two categories: straight lines, and + curves. The first of these are as easy to draw as they are easy to + make a computer draw. Give a computer the first and last point in + the line, and BAM! straight line. No questions asked. +

    +

    + Curves, however, are a much bigger problem. While we can draw curves + with ridiculous ease freehand, computers are a bit handicapped in + that they can't draw curves unless there is a mathematical function + that describes how it should be drawn. In fact, they even need this + for straight lines, but the function is ridiculously easy, so we + tend to ignore that as far as computers are concerned; all lines are + "functions", regardless of whether they're straight or curves. + However, that does mean that we need to come up with fast-to-compute + functions that lead to nice looking curves on a computer. There are + a number of these, and in this article we'll focus on a particular + function that has received quite a bit of attention and is used in + pretty much anything that can draw curves: Bézier curves. +

    +

    + They're named after + Pierre Bézier, who is principally responsible for making them known to the world + as a curve well-suited for design work (publishing his + investigations in 1962 while working for Renault), although he was + not the first, or only one, to "invent" these type of curves. One + might be tempted to say that the mathematician + Paul de Casteljau + was first, as he began investigating the nature of these curves in + 1959 while working at Citroën, and came up with a really elegant way + of figuring out how to draw them. However, de Casteljau did not + publish his work, making the question "who was first" hard to answer + in any absolute sense. Or is it? Bézier curves are, at their core, + "Bernstein polynomials", a family of mathematical functions + investigated by + Sergei Natanovich Bernstein, whose publications on them date back at least as far as 1912. +

    +

    + Anyway, that's mostly trivia, what you are more likely to care about + is that these curves are handy: you can link up multiple Bézier + curves so that the combination looks like a single curve. If you've + ever drawn Photoshop "paths" or worked with vector drawing programs + like Flash, Illustrator or Inkscape, those curves you've been + drawing are Bézier curves. +

    +

    + But what if you need to program them yourself? What are the + pitfalls? How do you draw them? What are the bounding boxes, how do + you determine intersections, how can you extrude a curve, in short: + how do you do everything that you might want when you do with these + curves? That's what this page is for. Prepare to be mathed! +

    +
    + ## PS: buy me a coffee? + +

    + If you enjoyed this book enough to print it out, you might be + wondering if there is some way to give something back. To answer + that question: you can always buy me a coffee, however-much a + coffee is where you live, or if you want to pay a fair price for + this book, you can buy me a really expensive coffee =) +

    +

    + This book has grown over the years from a short primer to a 100+ + print-page-equivalent ebook on the subject of Bézier curves, and a + lot of coffee went into the making of it. I don't regret a minute + I spent on writing it, but I can always do with some more coffee + to keep on writing! Please visit + https://pomax.github.io/bezierinfo + and click on the link in the online preface to donate some coffee + money. +

    +
    + +

    + —Pomax (or in the tweetworld, + @TheRealPomax) +

    +
    +

    Note: virtually all Bézier graphics are interactive.

    +

    + This page uses interactive examples, relying heavily on + Bezier.js, as well + as maths formulae which are typeset into SVG using the + XeLaTeX typesetting + system and + pdf2svg by + David Barton. The + page is generated offline as a React application. +

    +

    This book is open source.

    +

    + This book is an open source software project, and lives on two + github repositories. The first is + https://github.com/pomax/bezierinfo + and is the purely-for-presentation version you are viewing right + now. The other repository is + https://github.com/pomax/BezierInfo-2, which is the development version, housing all the HTML, + JavaScript, and CSS. You can fork either of these, and pretty much + do with them as you please, except for passing it off as your own + work wholesale, of course =) +

    +

    How complicated is the maths going to be?

    +

    + Most of the mathematics in this Primer are early high school + maths. If you understand basic arithmetic, and you know how to + read English, you should be able to get by just fine. There will + at times be far more complicated maths, but if you don't + feel like digesting them, you can safely skip over them by either + skipping over the "detail boxes" in section or by just jumping to + the end of a section with maths that looks too involving. The end + of sections typically simply list the conclusions so you can just + work with those values directly. +

    +

    Questions, comments:

    +

    + If you have suggestions for new sections, hit up the + Github issue tracker + (also reachable from the repo linked to in the upper right). If + you have questions about the material, there's currently no + comment section while I'm doing the rewrite, but you can use the + issue tracker for that as well. Once the rewrite is done, I'll add + a general comment section back in, and maybe a more topical + "select this section of text and hit the 'question' button to ask + a question about it" system. We'll see. +

    +

    Help support the book!

    +

    + If you enjoyed this book, or you simply found it useful for + something you were trying to get done, and you were wondering how + to let me know you appreciated this book, you have two options: + you can either head on over to the + Patreon page for this + book, or if you prefer to make a one-time donation, head on over + to the + buy Pomax a coffee + page. This work has grown from a small primer to a 100-plus + print-page-equivalent reader on the subject of Bézier curves over + the years, and a lot of coffee went into the making of it. I don't + regret a minute I spent on writing it, but I can always do with + some more coffee to keep on writing! +

    +
    +
    +
    +

    Table of Contents

    1. A lightning introduction
    2. So what makes a Bézier Curve?
    3. @@ -83,32 +255,32 @@

      Scripts are disabled. Showing fallback image. Scripts are disabled. Showing fallback image. @@ -168,12 +340,16 @@

      - + Scripts are disabled. Showing fallback image. diff --git a/index.template.html b/index.template.html index 1c582cb4..96ea79df 100644 --- a/index.template.html +++ b/index.template.html @@ -5,19 +5,18 @@ - A Primer on Bézier Curves + {{ title }} {{ base }} - + - - + + @@ -37,17 +36,20 @@
      -

      A Primer on Bézier Curves

      -

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

      +

      {{ title }}

      +

      {{ subtitle }}

      {{ langSwitchLabel }}
        {{ langSwitcher }}
      + {{ langHelpLabel }}
      + {{ preface }}
      +

      {{ tocLabel }}

        {{ toc }}
      diff --git a/ja-JP/index.html b/ja-JP/index.html index d21de875..84790eae 100644 --- a/ja-JP/index.html +++ b/ja-JP/index.html @@ -21,7 +21,7 @@ property="og:description" content="A detailed explanation of Bézier curves, and how to do the many things that we commonly want to do with them." /> - + @@ -57,12 +57,104 @@
    4. 日本語
    5. 中文
    6. + Don't see your language listed? + Help translate this content!
      -
      +
      +
      +

      まえがき

      +

      + 2次元上になにかを描くとき、普通は線を使いますが、これは直線と曲線の2つに分類することができます。直線を描くのはとても簡単で、これをコンピュータに描かせるのも容易です。直線の始点と終点をコンピュータに与えてやれば、ポン!直線が描けました。疑問の余地もありません。 +

      +

      + しかしながら、曲線の方はもっと大きな問題です。私たちはフリーハンドでいとも簡単に曲線を描くことができますが、コンピュータの方は少し不利です。曲線の描き方を表した数学的な関数が与えられないと、コンピュータは曲線を描くことができないのです。実際には、直線でさえも関数が必要になります。直線の関数はとても簡単なので、わたしたちはよく無視してしまいますが、コンピュータにとっては直線であれ曲線であれ、線はすべて「関数」なのです。しかしこれは、コンピュータで速く計算できて、きれいな曲線が得られるような関数を見つける必要がある、ということになります。そのような関数はたくさんありますが、多くの関心を集め続け、そしてどんな場面でも使われている、ある特定の関数に対してこの記事では焦点を絞ります。この関数は「ベジエ」曲線を描きます。 +

      +

      + ベジエ曲線はPierre Bézierから名付けられました。この曲線がデザイン作業に適していることを世界に知らしめたのが、彼なのです(ルノーに勤務し、1962年にその研究を発表しました)。ただし、この曲線を「発明」したのは彼が最初で唯一というわけではありません。数学者Paul de Casteljauはシトロエンで働いていた1959年、この曲線の性質について研究し、ベジエ曲線の非常にエレガントな描き方を発見しました。これが最初だと言う人もいます。しかしながら、de + Casteljauは自分の成果を発表しなかったため、「誰が最初か?」という問いに答えるのがとても難しくなっています。またベジエ曲線は、核心的にはSergei Natanovich Bernsteinが研究した「ベルンシュタイン多項式」という数学関数の一種ですが、こちらの公刊に関しては少なくとも1912年まで遡ることができます。いずれにせよ、これらはほとんど瑣末なことです。より注目すべきなのは、ベジエ曲線は取り扱いに便利だいうことです。たとえば複数のベジエ曲線を繋いで、1つの曲線に見えるようにすることができます。もしあなたがPhotoshopで「パス」を描いたり、FlashやIllustrator、Inkscapeのようなベクタードローイングソフトを使ったことがあるのであれば、そこで描いてきた曲線はベジエ曲線です。 +

      +

      + では、これを自分でプログラムしなければならないとなったらどうでしょう?ハマりどころは何でしょうか?どうやってベジエ曲線を描くのでしょう?バウンディングボックスとは何で、どうやって交点を求め、どうやったら曲線を押し出せるのでしょうか?つまるところ、ベジエ曲線に対して行いたいあらゆる操作は、どのようにすればいいのでしょう?このページはそれに答えるためにあります。数学にとりかかりましょう! +

      +
      + ## 追伸:コーヒーをおごってくれませんか? + +

      + この本のことを印刷するほど気に入ったのであれば、あなたは「何かお礼をする方法はないか」と考えているかもしれません。この質問に対する答えはこうです:いつでもわたしにコーヒーをおごってください。あなたが住んでいるところの、コーヒー1杯の値段でかまいません。この本にかなりの金額を支払いたいのであれば、非常に高価なコーヒーをおごってくれてもかまいません + =) +

      +

      + この本は小さな入門からはじまり、印刷85ページ以上に相当するようなベジエ曲線の電子書籍へと、年々成長してきています。そして、多くのコーヒーがその執筆に費やされてきました。わたしは執筆に使った時間が惜しいとは思いませんが、もう少しコーヒーがあれば、ずっと書き続けることができるのです!https://pomax.github.io/bezierinfoにアクセスしてオンライン版のまえがきにあるリンクをクリックし、コーヒー代を寄付してください。 +

      +
      + +

      + —Pomax (Twitter上では@TheRealPomax) +

      +
      +

      注:ベジエの図はすべてインタラクティブになっています。

      +

      + このページではBezier.jsを活用し、インタラクティブな例示を行っています。また、MathJaxというすばらしいライブラリによって、(LaTeX式の)「本物」の数学組版を行っています。このページはWebpackを使い、Reactアプリケーションとしてオフラインで生成されていますが、このために「ソースを表示」オプションを追加することがかなり難しくなってしまいました。これをどうやって追加すべきかは今もまだ考え中ですが、かといって、数年ぶりとなる今回の更新を延期したくはありませんでした。 +

      +

      本書はオープンソースです。

      +

      + この本はオープンソースソフトウェアのプロジェクトで、2つのGitHubリポジトリ上に存在しています。1つ目のhttps://github.com/pomax/bezierinfoは表示のためだけに用意されたバージョンで、あなたが今読んでいるものです。もう一方のhttps://github.com/pomax/BezierInfo-2は開発版で、すべてのHTML・JavaScript・CSSが含まれています。どちらのリポジトリもフォークすることができますし、あなたの好きなように使ってかまいません。ただし、これを自分の成果だと騙って売ることはもちろん除きます。=) +

      +

      数学はどこまで難しくなりますか?

      +

      + この入門に出てくる数学は、大半が高校初年度程度です。基本的な計算を理解していて、英語の読み方が分かっていれば、こなすことができるはずです。時にはずっと難しい数学も出てきますが、もし理解できないように感じたら、そこは読み飛ばしても大丈夫です。節の中の「詳細欄」を飛ばしてもいいですし、厄介そうな数学があれば節の最後まで読み飛ばしてもかまいません。各節の最後にはたいてい結論を並べてありますので、これを直に利用することもできます。 +

      +

      質問・コメント:

      +

      + 新しい節の提案があれば、GitHubのissueトラッカーをクリックしてください(右上にあるリポジトリのリンクからでもたどり着けます)。改訂中のため、現在はこのページにはコメント欄がありませんが、内容に関する質問がある場合にもissueトラッカーを使ってかまいません。改訂が完了したら、総合的なコメント欄を復活させる予定です。あるいは、「質問対象の節を選択して『質問』ボタンをクリック」するような項目別のシステムになるかもしれません。いずれわかります。 +

      +

      コーヒーをおごってくれませんか?

      +

      + この本が気に入った場合や、取り組んでいたことに役に立つと思った場合、あるいは、この本への感謝をわたしに伝えるにはどうすればいいのかわからない場合。そのような場合には、わたしにコーヒーをおごってください。あなたが住んでいるところのコーヒー1杯の値段でかまいません。この本は小さな入門からはじまり、印刷で70ページほどに相当するようなベジエ曲線の読み物へと、年々成長してきています。そして、多くのコーヒーが執筆に費やされてきました。わたしは執筆に使った時間が惜しいとは思いませんが、もう少しコーヒーがあれば、ずっと書き続けることができるのです! +

      +
      +
      +
      +

      Table of Contents

      1. バッとした導入
      2. So what makes a Bézier Curve?
      3. @@ -162,12 +254,16 @@

        - + JSがなくて、画像を表示しています。 diff --git a/lib/custom-element/api/base-api.js b/lib/custom-element/api/base-api.js index 632ea124..a1a50315 100644 --- a/lib/custom-element/api/base-api.js +++ b/lib/custom-element/api/base-api.js @@ -21,7 +21,11 @@ class BaseAPI { static get methods() { const priv = this.privateMethods; - const names = Object.getOwnPropertyNames(this.prototype).concat([`setSize`, `showFocus`, `redraw`]); + const names = Object.getOwnPropertyNames(this.prototype).concat([ + `setSize`, + `showFocus`, + `redraw`, + ]); return names.filter((v) => priv.indexOf(v) < 0); } @@ -145,21 +149,26 @@ class BaseAPI { * Determine whether or not the canva should be focussed when interacted with. */ showFocus(show = true) { - const canvas = this.canvas; - if (show) { - canvas.setAttribute(`tabIndex`, 0); - canvas.classList.add(`focus-enabled`); - canvas._force_listener = () => this.forceFocus(); - [`touchstart`, `mousedown`].forEach((evtName) => - canvas.addEventListener(evtName, canvas._force_listener) - ); - } else { - canvas.removeAttribute(`tabIndex`); - canvas.classList.remove(`focus-enabled`); - [`touchstart`, `mousedown`].forEach((evtName) => - canvas.removeEventListener(evtName, canvas._force_listener) - ); + if (show === false) { + return this.noFocus(); } + + const canvas = this.canvas; + canvas.setAttribute(`tabIndex`, 0); + canvas.classList.add(`focus-enabled`); + canvas._force_listener = () => this.forceFocus(); + [`touchstart`, `mousedown`].forEach((evtName) => + canvas.addEventListener(evtName, canvas._force_listener) + ); + } + + noFocus() { + const canvas = this.canvas; + canvas.removeAttribute(`tabIndex`); + canvas.classList.remove(`focus-enabled`); + [`touchstart`, `mousedown`].forEach((evtName) => + canvas.removeEventListener(evtName, canvas._force_listener) + ); } /** @@ -187,6 +196,8 @@ class BaseAPI { */ setup() { // console.log(`setup`); + this.showFocus(); + this.moveable = []; } /** @@ -212,15 +223,20 @@ class BaseAPI { function enhanceContext(ctx) { const styles = []; ctx.cacheStyle = () => { - styles.push({ + let e = { strokeStyle: ctx.strokeStyle, fillStyle: ctx.fillStyle, lineWidth: ctx.lineWidth, - }); + transform: ctx.getTransform(), + }; + styles.push(e); }; ctx.restoreStyle = () => { const v = styles.pop(); - Object.keys(v).forEach((k) => (ctx[k] = v[k])); + Object.keys(v).forEach((k) => { + if (k !== `transform`) ctx[k] = v[k]; + else ctx.setTransform(v[k]); + }); }; return ctx; } diff --git a/lib/custom-element/api/graphics-api.js b/lib/custom-element/api/graphics-api.js index 69c56508..a04016e7 100644 --- a/lib/custom-element/api/graphics-api.js +++ b/lib/custom-element/api/graphics-api.js @@ -34,6 +34,64 @@ class GraphicsAPI extends BaseAPI { return Shape.BEZIER; } + onMouseDown(evt) { + super.onMouseDown(evt); + for (let i = 0, e = this.moveable.length, p; i < e; i++) { + p = this.moveable[i]; + if (p.dist(this.cursor) <= 5) { + this.currentPoint = p; + break; + } + } + } + + onMouseMove(evt) { + super.onMouseMove(evt); + if (this.currentPoint) { + this.currentPoint.x = this.cursor.x; + this.currentPoint.y = this.cursor.y; + } else { + for (let i = 0, e = this.moveable.length, p; i < e; i++) { + p = this.moveable[i]; + if (p.dist(this.cursor) <= 5) { + this.setCursor(this.HAND); + return; // NOTE: this is a return, not a break. + } + } + this.setCursor(this.POINTER); + } + } + + onMouseUp(evt) { + super.onMouseUp(evt); + this.currentPoint = undefined; + } + + setMovable(points) { + points.forEach((p) => this.moveable.push(p)); + } + + /** + * transforms: translate + */ + translate(x, y) { + this.ctx.translate(x, y); + } + + /** + * transforms: rotate + */ + rotate(angle) { + this.ctx.rotate(angle); + } + + /** + * transforms: reset + */ + resetTransform() { + this.ctx.resetTransform(); + } + /** * custom element scoped querySelector */ @@ -106,9 +164,12 @@ class GraphicsAPI extends BaseAPI { /** * Reset the canvas bitmap to a uniform color. */ - clear(color=`white`) { + clear(color = `white`) { + this.ctx.cacheStyle(); + this.resetTransform(); this.ctx.fillStyle = color; this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); + this.ctx.restoreStyle(); } /** @@ -123,8 +184,8 @@ class GraphicsAPI extends BaseAPI { */ line(p1, p2) { this.ctx.beginPath(); - this.ctx.moveTo(p1.x, p1.y); - this.ctx.lineTo(p2.x, p2.y); + this.ctx.moveTo(p1.x + 0.5, p1.y + 0.5); + this.ctx.lineTo(p2.x + 0.5, p2.y + 0.5); this.ctx.stroke(); } @@ -133,7 +194,7 @@ class GraphicsAPI extends BaseAPI { */ circle(p, r) { this.ctx.beginPath(); - this.ctx.arc(p.x, p.y, r, 0, this.TAU); + this.ctx.arc(p.x + 0.5, p.y + 0.5, r, 0, this.TAU); this.ctx.fill(); this.ctx.stroke(); } @@ -141,16 +202,16 @@ class GraphicsAPI extends BaseAPI { /** * Draw text on the canvas */ - text(str, x, y) { - this.ctx.fillText(str, x, y); + text(str, p) { + this.ctx.fillText(str, p.x + 0.5, p.y + 0.5); } /** * Draw a rectangle start with {p} in the upper left */ rect(p, w, h) { - this.ctx.fillRect(p.x, p.y, w, h); - this.ctx.strokeRect(p.x, p.y, w, h); + this.ctx.fillRect(p.x + 0.5, p.y + 0.5, w, h); + this.ctx.strokeRect(p.x + 0.5, p.y + 0.5, w, h); } /** @@ -227,12 +288,12 @@ class GraphicsAPI extends BaseAPI { for (let i = 0, e = cpoints.length - 3; i < e; i++) { let [c1, c2, c3, c4] = cpoints.slice(i, i + 4); let p2 = { - x: c2.x + (c3.x - c1.x)/(6*f), - y: c2.y + (c3.y - c1.y)/(6*f) + x: c2.x + (c3.x - c1.x) / (6 * f), + y: c2.y + (c3.y - c1.y) / (6 * f), }; let p3 = { - x: c3.x - (c4.x - c2.x)/(6*f), - y: c3.y - (c4.y - c2.y)/(6*f) + x: c3.x - (c4.x - c2.x) / (6 * f), + y: c3.y - (c4.y - c2.y) / (6 * f), }; ctx.bezierCurveTo(p2.x, p2.y, p3.x, p3.y, c3.x, c3.y); } @@ -242,7 +303,7 @@ class GraphicsAPI extends BaseAPI { * Curve draw function, which assumes Bezier coordinates */ drawBezier(ctx, points) { - for (let i = 0, e = points.length; i < e; i+=3) { + for (let i = 0, e = points.length; i < e; i += 3) { let [p1, p2, p3] = points.slice(i, i + 3); ctx.bezierCurveTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y); } @@ -251,12 +312,12 @@ class GraphicsAPI extends BaseAPI { /** * convenient grid drawing function */ - drawGrid(division=20) { - for(let x=(division/2)|0; xHelp translate this content!", + "zh-CN": "Don't see your language listed? Help translate this content!", + "ja-JP": "Don't see your language listed? Help translate this content!" + }, + "disabledMessage": { + "en-GB": "Scripts are disabled. Showing fallback image.", + "zh-CN": "脚本已禁用,并显示后备图像。", + "ja-JP": "JSがなくて、画像を表示しています。" + } +} diff --git a/style.css b/style.css index e69de29b..2bb4d34e 100644 --- a/style.css +++ b/style.css @@ -0,0 +1,33 @@ +:root[lang="en-GB"] { + font-family: 'Museo', 'Helvetica Neue', 'Helvetica', Arial, sans-serif; +} + +:root[lang="ja-JP"] { + font-family: 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', 'Osaka', 'メイリオ', 'Meiryo', 'MS Pゴシック', 'MS PGothic', 'Helvetica Neue', 'Helvetica', Arial, sans-serif; + font-size: 0.9rem; +} + +:root[lang="zh-CN"] { + font-family: '华文细黑', 'STXihei', 'PingFang TC', '微软雅黑体', 'Microsoft YaHei New', '微软雅黑', 'Microsoft Yahei', '宋体', 'SimSun', 'Helvetica Neue', 'Helvetica', Arial, sans-serif; + font-size: 0.95rem; +} + +h1 { + background: black; + color: white; + padding: 0.2em 0.5em; +} + +#chapters { + counter-reset: section; +} + +#chapters section h1:before { + counter-increment: section; + content: "§" counter(section); + margin-right: 1em; +} + +graphics-element { + background: #e8e8e8; +} diff --git a/tools/build.js b/tools/build.js index 82845433..28aded1a 100644 --- a/tools/build.js +++ b/tools/build.js @@ -1,47 +1,27 @@ -/********************************************************************** - * - * This script is a locale aggregator and JSX generator, yielding - * locale-specific node modules that contain the section content - * keyed on section dir names. - * - * 1. find out which sections exist - * 2. find out how many different locales exist - * 3. for each locale: - * - * 1. for each section: - * - * 1. grab the associated locale - * 2. chunk the data for "should be preserved" vs. - * "should be processed as markdown". - * 3. join the chunks back up after converting the - * still acknowledged as markdown bits. - * 4. aggregate with a function wrapper to allow for - * JS bindings to a handler object. - * - * 2. dump the aggregated locale data as a content.js file - * 3. generate a locale-specific index.html - * - * - **********************************************************************/ - const fs = require("fs-extra"); const path = require("path"); - const getAllChapterFiles = require("./build/get-all-chapter-files.js"); const processLocale = require("./build/process-locale.js"); const createIndexPages = require("./build/create-index-page.js"); +const sectionList = require("../chapters/toc.js").map((v) => + path.posix.join( + __dirname.split(path.sep).join(path.posix.sep), + `..`, + `chapters`, + v + ) +); -// main entry point - -(async function () { - const chapterFiles = await getAllChapterFiles(); +/** + * main entry point: + * - aggregate all content files organized by locale + * - + */ +getAllChapterFiles().then((chapterFiles) => { 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); }); -})(); \ No newline at end of file +}); diff --git a/tools/build/create-index-page.js b/tools/build/create-index-page.js index 1976a7fe..f484006d 100644 --- a/tools/build/create-index-page.js +++ b/tools/build/create-index-page.js @@ -1,7 +1,7 @@ const fs = require("fs-extra"); const path = require("path"); -const config = require("../../config.json"); -const defaultLocale = config.defaultLocale +const localeStrings = require("../../locale-strings.json"); +const defaultLocale = localeStrings.defaultLocale const prettier = require("prettier"); const generateLangSwitcher = require("./generate-lang-switcher.js"); const nunjucks = require("nunjucks"); @@ -23,22 +23,38 @@ module.exports = async function createIndexPages(locale, chapters, languages) { const toc = {}; - const sections = Object.keys(chapters).map((section) => { + const sectionOrder = require("../../chapters/toc.js"); + const preface = `
        ${chapters[sectionOrder[0]]}
        `; + + const sections = sectionOrder.slice(1).map((section) => { let content = chapters[section]; - let title = content.match(/

        ([^<]+)<\/h1>/)[1]; - toc[section] = `
      4. ${title}
      5. `; - return `
        \n${content}
        `; + if (content) { + let title = content.match(/

        ([^<]+)<\/h1>/)[1]; + toc[section] = `
      6. ${title}
      7. `; + return `
        ${content}
        `; + } + return ``; }); - const index = nunjucks.render(`index.template.html`, { + // Set up the templating context + const context = { base, locale, - langSwitchLabel: config.langSwitchLabel[locale], langSwitcher, + preface, toc: Object.values(toc).join(`\n`), chapters: sections.join(`\n`), + }; + + // And inject all the relevant locale strings + Object.keys(localeStrings).forEach(key => { + if (localeStrings[key][locale]) { + context[key] = localeStrings[key][locale]; + } }); + const index = nunjucks.render(`index.template.html`, context); + const data = prettier.format(index, { parser: `html` }); if (locale === defaultLocale) { diff --git a/tools/build/generate-lang-switcher.js b/tools/build/generate-lang-switcher.js index 1ca13d96..61b8874d 100644 --- a/tools/build/generate-lang-switcher.js +++ b/tools/build/generate-lang-switcher.js @@ -1,5 +1,5 @@ -const config = require("../../config.json"); -const defaultLocale = config.defaultLocale +const localeStrings = require("../../locale-strings.json"); +const defaultLocale = localeStrings.defaultLocale module.exports = function generateLangSwitcher(currentLocale, allLocales) { @@ -19,7 +19,7 @@ module.exports = function generateLangSwitcher(currentLocale, allLocales) { link = `../${locale}/index.html`; } } - return `
      8. ${config.localeName[locale]}
      9. `; + return `
      10. ${localeStrings.localeName[locale]}
      11. `; }) .join(`\n`); }; diff --git a/tools/build/get-all-chapter-files.js b/tools/build/get-all-chapter-files.js index 6451819a..42a4e518 100644 --- a/tools/build/get-all-chapter-files.js +++ b/tools/build/get-all-chapter-files.js @@ -8,7 +8,7 @@ const BASEDIR = path.join(__dirname, "..", ".."); /** * ...docs go here... */ -module.exports = function getAllChapterFiles() { +module.exports = /* async */ function getAllChapterFiles() { return new Promise((resolve, reject) => { glob(path.join(BASEDIR, `chapters/**/content*md`), (err, files) => { if (err) reject(err); diff --git a/tools/build/process-locale.js b/tools/build/process-locale.js index 8b4c980f..0774d2c8 100644 --- a/tools/build/process-locale.js +++ b/tools/build/process-locale.js @@ -1,7 +1,7 @@ const fs = require("fs-extra"); const path = require("path"); -const config = require("../../config.json"); -const defaultLocale = config.defaultLocale +const localeStrings = require("../../locale-strings.json"); +const defaultLocale = localeStrings.defaultLocale const convertMarkDown = require("./convert-markdown.js"); const nunjucks = require("nunjucks"); @@ -30,25 +30,34 @@ module.exports = async function processLocale( } }); + const chapters = {}; + let missing = 0; + + await Promise.all( + localeFiles.map(async (file) => { + const chapter = file.match(/chapters\/([^/]+)\/content./)[1]; + try { + const markdown = fs.readFileSync(file).toString("utf8"); + const replaced = nunjucks.renderString(markdown, { + disableMessage: `${localeStrings.disabledMessage[locale]}`, + }); + const converted = await convertMarkDown(replaced); + chapters[chapter] = converted; + } catch (e) { + if (locale === defaultLocale) missing++; + } + }) + ); + + if (locale === defaultLocale) { + console.log(`Warning: ${missing} chapters appear to be missing, based on the ToC listing.`); + } + 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/zh-CN/index.html b/zh-CN/index.html index 9ff1d9c6..6dec5d5a 100644 --- a/zh-CN/index.html +++ b/zh-CN/index.html @@ -21,7 +21,7 @@ property="og:description" content="A detailed explanation of Bézier curves, and how to do the many things that we commonly want to do with them." /> - + @@ -57,12 +57,92 @@
      12. 日本語
      13. 中文
      14. + Don't see your language listed? + Help translate this content!
        -
        +
        +
        +

        序言

        +

        + 我们通常用线条来绘制2D图形,大致分为两种线条:直线和曲线。不论我们动手还是用电脑,都能很容易地画出第一种线条。只要给电脑起点和终点,砰!直线就画出来了。没什么好疑问的。 +

        +

        + 然而,绘制曲线却是个大问题。虽然我们可以很容易地徒手画出曲线,但除非给出描述曲线的数学函数,不然计算机无法画出曲线。实际上,画直线时也需要数学函数,但画直线所需的方程式很简单,我们在这里不去考虑。在计算机看来,所有线条都是“函数”,不管它们是直线还是曲线。然而,这就表示我们需要找到能在计算机上表现良好的曲线方程。这样的曲线有很多种,在本文我们主要关注一类特殊的、备受关注的函数,基本上任何画曲线的地方都会用到它:贝塞尔曲线。 +

        +

        + 它们是以Pierre Bézier命名的,尽管他并不是第一个,或者说唯一“发明”了这种曲线的人,但他让世界知道了这种曲线十分适合设计工作(在1962年为Renault工作并发表了他的研究)。有人也许会说数学家Paul de Casteljau是第一个发现这类曲线特性的人,在Citroën工作时,他提出了一种很优雅的方法来画这些曲线。然而,de + Casteljau没有发表他的工作,这使得“谁先发现”这一问题很难有一个确切的答案。 + 贝塞尔曲线本质上是伯恩斯坦多项式,这是Sergei Natanovich Bernstein研究的一种数学函数,关于它们的出版物至少可以追溯到1912年。无论如何,这些都只是一些冷知识,你可能更在意的是这些曲线很方便:你可以连接多条贝塞尔曲线,并且连接起来的曲线看起来就像是一条曲线。甚至,在你在Photoshop中画“路径”或使用一些像Flash、Illustrator和Inkscape这样的矢量绘图程序时,所画的曲线都是贝塞尔曲线。 +

        +

        + 那么,要是你自己想编程实现它们呢?有哪些陷阱?你怎么画它们?包围盒是怎么样的,怎么确定交点,怎么拉伸曲线,简单来说:你怎么对曲线做一切你想做的事?这就是这篇文章想说的。准备好学习一些数学吧! +

        +

        + —Pomax (推特账号, + @TheRealPomax) +

        +
        +

        注意:几乎所有的贝塞尔图形都是可交互的。

        +

        + 这个页面使用了基于Bezier.js + 的可交互例子,还有一些用MathJax + 排版的“真正的”数学(LaTeX形式)。这个页面是用Webpack离线生成的React应用,这便让加入“查看源码”选项更具挑战性了。我仍然试图将它们添加回来,但跟前几年的版本相比,不觉得它能够支撑部署这个更新。 +

        +

        这本书是开源的。

        +

        + 这本书是开源的软件项目,现有两个github仓库。第一个https://github.com/pomax/bezierinfo,它是你现在在看的这个,纯粹用来展示的版本。另外一个https://github.com/pomax/BezierInfo-2,是带有所有html, + javascript和css的开发版本。你可以fork任意一个,随便做些什么,当然除了把它当作自己的作品来商用。 + =) +

        +

        用到的数学将有多复杂?

        +

        + 这份入门读物用到的大部分数学知识都是高中所学的。如果你理解基本的计算并能看懂英文的话,就能上手这份材料。有时候会用到复杂一点的数学,但如果你不想深究它们,可以选择跳过段落里的“详解”部分,或者直接跳到章节末尾,避开那些看起来很深入的数学。章节的末尾往往会列出一些结论,因此你可以直接利用这些结论。 +

        +

        问题,评论:

        +

        + 如果你有对于新章节的一些建议,点击 + Github issue tracker + (也可以点右上角的repo链接)。如果你有关于材料的一些问题,由于我现在在做改写工作,目前没有评论功能,但你可以用issue跟踪来发表评论。一旦完成重写工作,我会把评论功能加上,或者会有“选择文字段落,点击‘问题’按钮来提问”的系统。到时候我们看看。 +

        +

        给我买杯咖啡?

        +

        + 如果你很喜欢这本书,或发现它对你要做的事很有帮助,或者你想知道怎么表达自己对这本书的感激,你可以 + 给我买杯咖啡 + ,所少钱取决于你。这份工作持续了很多年,从一份小小的简要到70多页关于贝塞尔曲线的读物,在完成它的过程中倾注了很多咖啡。我从未后悔花在这上面的每一分钟,但如果有更多咖啡的话,我可以坚持写下去! +

        +
        +
        +
        +

        Table of Contents

        1. 简单介绍
        2. So what makes a Bézier Curve?
        3. @@ -160,12 +240,16 @@

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