From b47a94d23d14e96b19ba9c49583f68554844a16f Mon Sep 17 00:00:00 2001 From: Pomax Date: Wed, 15 Feb 2017 14:59:24 -0800 Subject: [PATCH] proper div escaping during locale conversion --- components/sections/control/content.en-GB.md | 75 +++++++++ components/sections/control/index.js | 102 +----------- .../sections/explanation/content.en-GB.md | 26 +++- components/sections/preface/content.en-GB.md | 3 +- components/sections/whatis/index.js | 2 +- locales/en-GB/content.js | 146 +++++++++++++----- make-locales.js | 145 ++++++++++++----- package.json | 1 + 8 files changed, 312 insertions(+), 188 deletions(-) create mode 100644 components/sections/control/content.en-GB.md diff --git a/components/sections/control/content.en-GB.md b/components/sections/control/content.en-GB.md new file mode 100644 index 00000000..67406514 --- /dev/null +++ b/components/sections/control/content.en-GB.md @@ -0,0 +1,75 @@ +# Controlling Bézier curvatures + +Bézier curves are (like all "splines") interpolation functions, meaning they take a set of points, and generate values somewhere "between" those points. (One of the consequences of this is that you'll never be able to generate a point that lies outside the outline for the control points, commonly called the "hull" for the curve. Useful information!). In fact, we can visualize how each point contributes to the value generated by the function, so we can see which points are important, where, in the curve. + +The following graphs show the interpolation functions for quadratic and cubic curves, with "S" being the strength of a point's contribution to the total sum of the Bézier function. Click or click-drag to see the interpolation percentages for each curve-defining point at a specific t value. + +
+ + + +
+ +Also shown is the interpolation function for a 15th order Bézier function. As you can see, the start and end point contribute considerably more to the curve's shape than any other point in the control point set. + +If we want to change the curve, we need to change the weights of each point, effectively changing the interpolations. The way to do this is about as straight forward as possible: just multiply each point with a value that changes its strength. These values are conventionally called "Weights", and we can add them to our original Bézier function: + +\[ + Bézier(n,t) = \sum_{i=0}^{n} + \underset{binomial\ term}{\underbrace{\binom{n}{i}}} + \cdot\ + \underset{polynomial\ term}{\underbrace{(1-t)^{n-i} \cdot t^{i}}} + \cdot\ + \underset{weight}{\underbrace{w_i}} +\] + +That looks complicated, but as it so happens, the "weights" are actually just the coordinate values we want our curve to have: for an nth order curve, w0 is our start coordinate, wn is our last coordinate, and everything in between is a controlling coordinate. Say we want a cubic curve that starts at (120,160), is controlled by (35,200) and (220,260) and ends at (220,40), we use this Bézier curve: + +\[ +\left \{ \begin{matrix} + x = BLUE[120] \cdot (1-t)^3 + BLUE[35] \cdot 3 \cdot (1-t)^2 \cdot t + BLUE[220] \cdot 3 \cdot (1-t) \cdot t^2 + BLUE[220] \cdot t^3 \\ + y = BLUE[160] \cdot (1-t)^3 + BLUE[200] \cdot 3 \cdot (1-t)^2 \cdot t + BLUE[260] \cdot 3 \cdot (1-t) \cdot t^2 + BLUE[40] \cdot t^3 +\end{matrix} \right. +\] + +Which gives us the curve we saw at the top of the article: + + + +What else can we do with Bézier curves? Quite a lot, actually. The rest of this article covers a multitude of possible operations and algorithms that we can apply, and the tasks they achieve. + +
+ +### How to implement the weighted basis function + +Given that we already know how to implement basis function, adding in the control points is remarkably easy: + +``` +function Bezier(n,t,w[]): + sum = 0 + for(k=0; k diff --git a/components/sections/control/index.js b/components/sections/control/index.js index 7a75e1f2..058a0674 100644 --- a/components/sections/control/index.js +++ b/components/sections/control/index.js @@ -1,11 +1,13 @@ var React = require("react"); -var Graphic = require("../../Graphic.jsx"); -var SectionHeader = require("../../SectionHeader.jsx"); + +var Locale = require("../../../lib/locale"); +var locale = new Locale("en-GB"); +var page = "control"; var Control = React.createClass({ getDefaultProps: function() { return { - title: "Controlling Bézier curvatures" + title: locale.getTitle(page) }; }, @@ -168,99 +170,7 @@ var Control = React.createClass({ }, render: function() { - return ( -
- - -

Bézier curves are (like all "splines") interpolation functions, meaning they take a set of - points, and generate values somewhere "between" those points. (One of the consequences of this - is that you'll never be able to generate a point that lies outside the outline for the control - points, commonly called the "hull" for the curve. Useful information!). In fact, we can visualize - how each point contributes to the value generated by the function, so we can see which points are - important, where, in the curve.

- -

The following graphs show the interpolation functions for quadratic and cubic curves, with "S" - being the strength of a point's contribution to the total sum of the Bézier function. Click or - click-drag to see the interpolation percentages for each curve-defining point at a - specific t value.

- -
- - - -
- -

Also shown is the interpolation function for a 15th order Bézier function. As you can see, - the start and end point contribute considerably more to the curve's shape than any other point - in the control point set.

- -

If we want to change the curve, we need to change the weights of each point, effectively changing - the interpolations. The way to do this is about as straight forward as possible: just multiply each - point with a value that changes its strength. These values are conventionally called "Weights", and - we can add them to our original Bézier function:

- -

\[ - Bézier(n,t) = \sum_{i=0}^{n} - \underset{binomial\ term}{\underbrace{\binom{n}{i}}} - \cdot\ - \underset{polynomial\ term}{\underbrace{(1-t)^{n-i} \cdot t^{i}}} - \cdot\ - \underset{weight}{\underbrace{w_i}} - \]

- -

That looks complicated, but as it so happens, the "weights" are actually just the coordinate values - we want our curve to have: for an nth order curve, w0 is our start coordinate, - wn is our last coordinate, and everything in between is a controlling coordinate. Say we want - a cubic curve that starts at (120,160), is controlled by (35,200) and (220,260) and ends at (220,40), - we use this Bézier curve:

- -

\[ - \left \{ \begin{matrix} - x = BLUE[120] \cdot (1-t)^3 + BLUE[35] \cdot 3 \cdot (1-t)^2 \cdot t + BLUE[220] \cdot 3 \cdot (1-t) \cdot t^2 + BLUE[220] \cdot t^3 \\ - y = BLUE[160] \cdot (1-t)^3 + BLUE[200] \cdot 3 \cdot (1-t)^2 \cdot t + BLUE[260] \cdot 3 \cdot (1-t) \cdot t^2 + BLUE[40] \cdot t^3 - \end{matrix} \right. \]

- -

Which gives us the curve we saw at the top of the article:

- - - -

What else can we do with Bézier curves? Quite a lot, actually. The rest of this article covers - a multitude of possible operations and algorithms that we can apply, and the tasks they achieve.

- -
-

How to implement the weighted basis function

- -

Given that we already know how to implement basis function, adding in the control points - is remarkably easy:

- -
function Bezier(n,t,w[]):
-  sum = 0
-  for(k=0; k
-
-          

And for the extremely optimized versions:

- -
function Bezier(2,t,w[]):
-  t2 = t * t
-  mt = 1-t
-  mt2 = mt * mt
-  return w[0]*mt2 + w[1]*2*mt*t + w[2]*t2
-
-function Bezier(3,t,w[]):
-  t2 = t * t
-  t3 = t2 * t
-  mt = 1-t
-  mt2 = mt * mt
-  mt3 = mt2 * mt
-  return w[0]*mt3 + 3*w[1]*mt2*t + 3*w[2]*mt*t2 + w[3]*t3
- -

And now we know how to program the weighted basis function.

-
- - -
- ); + return
{ locale.getContent(page, this) }
; } }); diff --git a/components/sections/explanation/content.en-GB.md b/components/sections/explanation/content.en-GB.md index d273af34..b5bce927 100644 --- a/components/sections/explanation/content.en-GB.md +++ b/components/sections/explanation/content.en-GB.md @@ -96,21 +96,25 @@ It's basically just a sum of "every combination of a and b", progr And that's the full description for Bézier curves. Σ in this function indicates that this is a series of additions (using the variable listed below the Σ, starting at ...=<value> and ending at the value listed on top of the Σ).
+ ### How to implement the basis function We could naively implement the basis function as a mathematical construct, using the function as our guide, like this: -
function Bezier(n,t):
+```
+function Bezier(n,t):
   sum = 0
   for(k=0; k
+  return sum
+```
 
 I say we could, because we're not going to: the factorial function is *incredibly* expensive. And, as we can see from the above explanation, we can actually create Pascal's triangle quite easily without it: just start at [1], then [1,1], then [1,2,1], then [1,3,3,1], and so on, with each next row fitting 1 more number than the previous row, starting and ending with "1", with all the numbers in between being the sum of the previous row's elements on either side "above" the one we're computing.
 
 We can generate this as a list of lists lightning fast, and then never have to compute the binomial terms because we have a lookup table:
 
-
lut = [      [1],           // n=0
+```
+lut = [      [1],           // n=0
             [1,1],          // n=1
            [1,2,1],         // n=2
           [1,3,3,1],        // n=3
@@ -127,19 +131,23 @@ binomial(n,k):
       nextRow[i] = lut[prev][i-1] + lut[prev][i]
     nextRow[s] = 1
     lut.add(nextRow)
-  return lut[n][k]
+ return lut[n][k] +``` So what's going on here? First, we declare a lookup table with a size that's reasonably large enough to accommodate most lookups. Then, we declare a function to get us the values we need, and we make sure that if an n/k pair is requested that isn't in the LUT yet, we expand it first. Our basis function now looks like this: -
function Bezier(n,t):
+```
+function Bezier(n,t):
   sum = 0
   for(k=0; k<=n; k++):
     sum += binomial(n,k) * (1-t)^(n-k) * t^(k)
-  return sum
+ return sum +``` Perfect. Of course, we can optimize further. For most computer graphics purposes, we don't need arbitrary curves. We need quadratic and cubic curves (this primer actually does do arbitrary curves, so you'll find code similar to shown here), which means we can drastically simplify the code: -
function Bezier(2,t):
+```
+function Bezier(2,t):
   t2 = t * t
   mt = 1-t
   mt2 = mt * mt
@@ -151,9 +159,11 @@ function Bezier(3,t):
   mt = 1-t
   mt2 = mt * mt
   mt3 = mt2 * mt
-  return mt3 + 3*mt2*t + 3*mt*t2 + t3
+ return mt3 + 3*mt2*t + 3*mt*t2 + t3 +``` And now we know how to program the basis function. Exellent. +
So, now we know what the base function(s) look(s) like, time to add in the magic that makes Bézier curves so special: control points. diff --git a/components/sections/preface/content.en-GB.md b/components/sections/preface/content.en-GB.md index d36c7f9e..4d67d912 100644 --- a/components/sections/preface/content.en-GB.md +++ b/components/sections/preface/content.en-GB.md @@ -30,7 +30,6 @@ If you have suggestions for new sections, hit up the [Github issue tracker](http ## Buy me a coffee? -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 can -always [buy me a coffee](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=QPRDLNGDANJSW), however-much a coffee is where you live. This work has grown over the years, from a small primer to a 70ish print-page-equivalent reader 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! +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 can always [buy me a coffee](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=QPRDLNGDANJSW), however-much a coffee is where you live. This work has grown over the years, from a small primer to a 70ish print-page-equivalent reader 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!
diff --git a/components/sections/whatis/index.js b/components/sections/whatis/index.js index 9ff7fcb6..77e8c208 100644 --- a/components/sections/whatis/index.js +++ b/components/sections/whatis/index.js @@ -7,7 +7,7 @@ var page = "whatis"; var Whatis = React.createClass({ getDefaultProps: function() { return { - title: "So what makes a Bézier Curve?" + title: locale.getTitle(page) }; }, diff --git a/locales/en-GB/content.js b/locales/en-GB/content.js index 6ef61fae..48046012 100644 --- a/locales/en-GB/content.js +++ b/locales/en-GB/content.js @@ -12,6 +12,7 @@ module.exports = {

They're named after Pierre Bézier, who is principally responsible for getting them known to the world as a curve well-suited for design work (working for Renault and publishing his investigations in 1962), 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, investigating the nature of these curves in 1959 while working at Citroën, coming 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, with publications on them at least as far back 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 nkscape, those curves you've been drawing are Bézier curves.

So, 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!

—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 "real" maths (in LaTeX form) which is typeset using the most excellent MathJax library. The page is generated offline as a React application, using Webpack, which has made adding "view source" options considerably more challenging. I'm still trying to figure out how to add them back in, but it didn't feel like it should hold up deploying this update compared to the previous years' version.

@@ -22,8 +23,7 @@ module.exports = {

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.

Buy me a coffee?

-

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 can -always buy me a coffee, however-much a coffee is where you live. This work has grown over the years, from a small primer to a 70ish print-page-equivalent reader 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!

+

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 can always buy me a coffee, however-much a coffee is where you live. This work has grown over the years, from a small primer to a 70ish print-page-equivalent reader 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!

; } @@ -33,11 +33,11 @@ always

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.

+
-

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, the 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!

; } @@ -162,22 +162,22 @@ Given \left ( \]

And that's the full description for Bézier curves. Σ in this function indicates that this is a series of additions (using the variable listed below the Σ, starting at ...=<value> and ending at the value listed on top of the Σ).

+
-### How to implement the basis function - -We could naively implement the basis function as a mathematical construct, using the function as our guide, like this: - -
function Bezier(n,t):
+

How to implement the basis function

+

We could naively implement the basis function as a mathematical construct, using the function as our guide, like this:

+
+function Bezier(n,t):
   sum = 0
-  for(k=0; k
-
-I say we could, because we're not going to: the factorial function is incredibly expensive. And, as we can see from the above explanation, we can actually create Pascal's triangle quite easily without it: just start at [1], then [1,1], then [1,2,1], then [1,3,3,1], and so on, with each next row fitting 1 more number than the previous row, starting and ending with "1", with all the numbers in between being the sum of the previous row's elements on either side "above" the one we're computing.
-
-We can generate this as a list of lists lightning fast, and then never have to compute the binomial terms because we have a lookup table:
-
-
lut = [      [1],           // n=0
+  return sum
+
+
+

I say we could, because we're not going to: the factorial function is incredibly expensive. And, as we can see from the above explanation, we can actually create Pascal's triangle quite easily without it: just start at [1], then [1,1], then [1,2,1], then [1,3,3,1], and so on, with each next row fitting 1 more number than the previous row, starting and ending with "1", with all the numbers in between being the sum of the previous row's elements on either side "above" the one we're computing.

+

We can generate this as a list of lists lightning fast, and then never have to compute the binomial terms because we have a lookup table:

+
+lut = [      [1],           // n=0
             [1,1],          // n=1
            [1,2,1],         // n=2
           [1,3,3,1],        // n=3
@@ -194,42 +194,110 @@ binomial(n,k):
       nextRow[i] = lut[prev][i-1] + lut[prev][i]
     nextRow[s] = 1
     lut.add(nextRow)
-  return lut[n][k]
- -So what's going on here? First, we declare a lookup table with a size that's reasonably large enough to accommodate most lookups. Then, we declare a function to get us the values we need, and we make sure that if an n/k pair is requested that isn't in the LUT yet, we expand it first. Our basis function now looks like this: - -
function Bezier(n,t):
+  return lut[n][k]
+
+
+

So what's going on here? First, we declare a lookup table with a size that's reasonably large enough to accommodate most lookups. Then, we declare a function to get us the values we need, and we make sure that if an n/k pair is requested that isn't in the LUT yet, we expand it first. Our basis function now looks like this:

+
+function Bezier(n,t):
   sum = 0
   for(k=0; k<=n; k++):
-    sum += binomial(n,k)  (1-t)^(n-k)  t^(k)
-  return sum
- -Perfect. Of course, we can optimize further. For most computer graphics purposes, we don't need arbitrary curves. We need quadratic and cubic curves (this primer actually does do arbitrary curves, so you'll find code similar to shown here), which means we can drastically simplify the code: - -
function Bezier(2,t):
-  t2 = t  t
+    sum += binomial(n,k) * (1-t)^(n-k) * t^(k)
+  return sum
+
+
+

Perfect. Of course, we can optimize further. For most computer graphics purposes, we don't need arbitrary curves. We need quadratic and cubic curves (this primer actually does do arbitrary curves, so you'll find code similar to shown here), which means we can drastically simplify the code:

+
+function Bezier(2,t):
+  t2 = t * t
   mt = 1-t
-  mt2 = mt  mt
-  return mt2 + 2mtt + t2
+  mt2 = mt * mt
+  return mt2 + 2*mt*t + t2
 
 function Bezier(3,t):
-  t2 = t  t
-  t3 = t2  t
+  t2 = t * t
+  t3 = t2 * t
   mt = 1-t
-  mt2 = mt  mt
-  mt3 = mt2  mt
-  return mt3 + 3mt2t + 3mtt2 + t3
- -And now we know how to program the basis function. Exellent. + mt2 = mt * mt + mt3 = mt2 * mt + return mt3 + 3*mt2*t + 3*mt*t2 + t3 + +
+

And now we know how to program the basis function. Exellent.

-

So, now we know what the base function(s) look(s) like, time to add in the magic that makes Bézier curves so special: control points.

; } }, "control": { - "title": "Unknown title (control)", + "title": "Controlling Bézier curvatures", "getContent": function(handler) { return
+ +

Bézier curves are (like all "splines") interpolation functions, meaning they take a set of points, and generate values somewhere "between" those points. (One of the consequences of this is that you'll never be able to generate a point that lies outside the outline for the control points, commonly called the "hull" for the curve. Useful information!). In fact, we can visualize how each point contributes to the value generated by the function, so we can see which points are important, where, in the curve.

+

The following graphs show the interpolation functions for quadratic and cubic curves, with "S" being the strength of a point's contribution to the total sum of the Bézier function. Click or click-drag to see the interpolation percentages for each curve-defining point at a specific t value.

+ +
+ + + +
+

Also shown is the interpolation function for a 15th order Bézier function. As you can see, the start and end point contribute considerably more to the curve's shape than any other point in the control point set.

+

If we want to change the curve, we need to change the weights of each point, effectively changing the interpolations. The way to do this is about as straight forward as possible: just multiply each point with a value that changes its strength. These values are conventionally called "Weights", and we can add them to our original Bézier function:

+ +\[ + Bézier(n,t) = \sum_{i=0}^{n} + \underset{binomial\ term}{\underbrace{\binom{n}{i}}} + \cdot\ + \underset{polynomial\ term}{\underbrace{(1-t)^{n-i} \cdot t^{i}}} + \cdot\ + \underset{weight}{\underbrace{w_i}} +\] + +

That looks complicated, but as it so happens, the "weights" are actually just the coordinate values we want our curve to have: for an nth + order curve, w0 is our start coordinate, wn is our last coordinate, and everything in between is a controlling coordinate. Say we want a cubic curve that starts at (120,160), is controlled by (35,200) and (220,260) and ends at (220,40), we use this Bézier curve:

+ +\[ +\left \{ \begin{matrix} + x = BLUE[120] \cdot (1-t)^3 + BLUE[35] \cdot 3 \cdot (1-t)^2 \cdot t + BLUE[220] \cdot 3 \cdot (1-t) \cdot t^2 + BLUE[220] \cdot t^3 \\ + y = BLUE[160] \cdot (1-t)^3 + BLUE[200] \cdot 3 \cdot (1-t)^2 \cdot t + BLUE[260] \cdot 3 \cdot (1-t) \cdot t^2 + BLUE[40] \cdot t^3 +\end{matrix} \right. +\] + +

Which gives us the curve we saw at the top of the article:

+ + +

What else can we do with Bézier curves? Quite a lot, actually. The rest of this article covers a multitude of possible operations and algorithms that we can apply, and the tasks they achieve.

+ +
+

How to implement the weighted basis function

+

Given that we already know how to implement basis function, adding in the control points is remarkably easy:

+
+function Bezier(n,t,w[]):
+  sum = 0
+  for(k=0; k<n; k++):
+    sum += w[k] * binomial(n,k) * (1-t)^(n-k) * t^(k)
+  return sum
+
+
+

And for the extremely optimized versions:

+
+function Bezier(2,t,w[]):
+  t2 = t * t
+  mt = 1-t
+  mt2 = mt * mt
+  return w[0]*mt2 + w[1]*2*mt*t + w[2]*t2
+
+function Bezier(3,t,w[]):
+  t2 = t * t
+  t3 = t2 * t
+  mt = 1-t
+  mt2 = mt * mt
+  mt3 = mt2 * mt
+  return w[0]*mt3 + 3*w[1]*mt2*t + 3*w[2]*mt*t2 + w[3]*t3
+
+
+

And now we know how to program the weighted basis function.

+
; } }, diff --git a/make-locales.js b/make-locales.js index cc7148d4..da3e4556 100644 --- a/make-locales.js +++ b/make-locales.js @@ -1,4 +1,3 @@ -const glue = '
'; var marked = require("marked"); var fs = require("fs-extra"); @@ -17,34 +16,99 @@ Module.prototype.require = function() { }; -/** - * Split data up into "markdown" and "latex" - */ -function chunk(data) { - var p = 0, e = 0, chunks = []; +function chunkDivs(data, chunks, chunkMore) { + var p = 0, + next = chunkMore ? chunkMore[0] : false, + otherChunkers = chunkMore ? chunkMore.slice(1) : false, + divMatch = '\n
\n', + divClosingTag = '
\n'; + while (p !== -1) { - let s = data.indexOf('\n\\[', p); - if (s === -1) { - chunks.push({ - latex: false, - data: data.substring(p) - }); + // Let's check for a
tag + let div = data.indexOf(divMatch, p); + if (div === -1) { + // No div tags found: we're done here. Parse the remaining + // data for whatever else might be in there. + performChunking(data.substring(p), chunks, next, otherChunkers); break; } - chunks.push({ - latex: false, - data: data.substring(p, s) - }); + // First parse the non-div data for whatever else might be in there. + performChunking(data.substring(p, div), chunks, next, otherChunkers); - e = data.indexOf('\\]\n\n', s) + 4; - chunks.push({ - latex: true, - data: data.substring(s, e) - }); + // Now, if we have a div, there's a few options: + // + // - "figure" contains HTML content, not to be converted + // - "note" contains markdown content, to be converted + // - "howtocode" contains markdown content, to be converted + let className = data.substring(div).match(/className="([^"]+)"/); + if (className !== null) { className = className[1]; } - p = e; + let eod, type="div"; + if (className === "figure") { + eod = data.indexOf(divClosingTag, div) + divClosingTag.length; + type += ".figure"; + } else { + eod = data.indexOf(divEnd, div) + divEnd.length; + } + + chunks.push({ convert: false, type: type, s:div, e:eod, data: data.substring(div, eod) }); + p = eod; } +} + +/** + * Split data up into "latex" and "not latex". + * Anything that is not latex might still be "not markdown" + * though, so we hand that data off to additional chunkers + */ +function chunkLatex(data, chunks, chunkMore) { + var p = 0, + next = chunkMore ? chunkMore[0] : false, + otherChunkers = chunkMore ? chunkMore.slice(1) : false, + latexEnd = '\\]\n\n'; + + while (p !== -1) { + // Let's check a LaTeX block + let latex = data.indexOf('\n\\[', p); + if (latex === -1) { + // No LaTeX block found: we're done here. Parse the remaining + // data for whatever else might be in there. + performChunking(data.substring(p), chunks, next, otherChunkers); + break; + } + + // First parse the non-LaTeX data for whatever else might be in there. + performChunking(data.substring(p, latex), chunks, next, otherChunkers); + + // Then capture the LaTeX block and mark it as "don't convert" + let eol = data.indexOf(latexEnd, latex) + latexEnd.length; + chunks.push({ convert: false, type: "latex", s:latex, e:eol, data: data.substring(latex, eol) }); + p = eol; + } +} + +// in-place chunking +function performChunking(data, chunks, chunker, moreChunkers) { + // If there's no further chunking function to run, just + // record this data as a chunk of convertible data. + if (!chunker) { + chunks.push({ convert: true, data: data }); + return "early"; + } + + // otherwise, perform more chunking. + chunker(data, chunks, moreChunkers); +} + +/** + * Split data up into "markdown" and "not markdown" parts. + * We'll only run markdown conversion on the markdown parts. + */ +function chunk(data) { + var chunks = []; + performChunking(data, chunks, chunkLatex, [chunkDivs]); return chunks; } @@ -59,26 +123,23 @@ sections.forEach((cname, number) => { var data, title; try { data = fs.readFileSync(loc).toString(); - - // convert all non-latex data - data = data.split(glue).map(content => { - return chunk(content).map(chunk => { - if (chunk.latex) return chunk.data; - let d = marked(chunk.data) - // And then some post-processing... - d = d.replace(/]+>([^<]+)<\/h1>/,function(_,t) { - title = t; - return ``; - }); - d = d.replace('

', '
') - // serious can we fucking not, please. - .replace(/'/g, "'") - .replace(/&/g, '&') - .replace(/"/g, '"') - return d; - }).join(''); - }).join(glue); - + data = chunk(data).map(block => { + // preserver is simple + if (!block.convert) return block.data; + // markdown conversion is a little more work + let d = marked(block.data.trim()); + // And then some post-processing... + d = d.replace(/]+>([^<]+)<\/h1>/,function(_,t) { + title = t; + return ``; + }); + d = d.replace('

', '') + // serious can we fucking not, please. + .replace(/'/g, "'") + .replace(/&/g, '&') + .replace(/"/g, '"') + return d; + }).join(''); } catch (e) { data = ''; title = `Unknown title (${cname})`; diff --git a/package.json b/package.json index 6da2fc3c..718e27b8 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "react-dom": "^15.0.0", "react-router": "^1.0.3", "sha1": "^1.1.1", + "showdown": "^1.6.4", "style-loader": "^0.13.0", "svgo": "^0.6.6", "uglify-loader": "^1.3.0",