Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Incorporate video link with headings #985

Merged
merged 9 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions content/01_vectors.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ <h3 id="marshall-islands-stick-chart-on-display-at-the-berkeley-art-museum-photo
<p>A vector is typically drawn as an arrow, as in Figure 1.1. The vector’s direction is indicated by where the arrow is pointing, and its magnitude by the length of the arrow.</p>
<p>The vector in Figure 1.1 is drawn as an arrow from point A to point B. It serves as an instruction for how to travel from A to B.</p>
<h2 id="the-point-of-vectors">The Point of Vectors</h2>
<div data-type="video-link" data-title="Video 1.1: What is a Vector?" href="https://thecodingtrain.com/tracks/the-nature-of-code-2/noc/1-vectors/1-what-is-a-vector"></div>
<div data-type="video-link" data-title="What is a Vector?" href="https://www.youtube.com/watch?v=bKEaK7WNLzM&#x26;embeds_referring_euri=https://thecodingtrain.com/&#x26;embeds_referring_origin=https://thecodingtrain.com&#x26;source_ve_path=OTY3MTQ&#x26;feature=emb_imp_woyt"></div>
<p>Before diving into more details about vectors, I’d like to create a p5.js example that demonstrates why you should care about vectors in the first place. If you’ve watched any beginner p5.js tutorials, read any introductory p5.js textbooks, or taken an introduction to creative coding course (and hopefully you’ve done one of these things to help prepare you for this book!), you probably, at one point or another, learned how to write a bouncing ball sketch.</p>
<div data-type="example">
<h3 id="example-11-bouncing-ball-with-no-vectors">Example 1.1: Bouncing Ball with No Vectors</h3>
Expand Down Expand Up @@ -210,7 +210,7 @@ <h2 id="vectors-in-p5js">Vectors in p5.js</h2>
position = position + velocity;</pre>
<p>In JavaScript, however, the addition operator <code>+</code> is reserved for primitive values (integers, floats, and the like). JavaScript doesn’t know how to add two <code>p5.Vector</code> objects together any more than it knows how to add two <code>p5.Font</code> objects or <code>p5.Image</code> objects. Fortunately, the <code>p5.Vector</code> class includes methods for common mathematical operations.</p>
<h2 id="vector-addition">Vector Addition</h2>
<div data-type="video-link" data-title="Video 1.2: Vector Math" href="https://thecodingtrain.com/tracks/the-nature-of-code-2/noc/1-vectors/2-vector-math"></div>
<div data-type="video-link" data-title="Vector Math" href="https://www.youtube.com/watch?v=Rob0pbE7kks"></div>
<p>Before I continue working with the <code>p5.Vector</code> class and the <code>add()</code> method, let’s examine vector addition by using the notation found in math and physics textbooks. Vectors are typically written either in boldface type or with an arrow on top. For the purposes of this book, to distinguish a <strong>vector</strong> (with magnitude and direction) from a <strong>scalar</strong> (a single value, such as an integer or a floating-point number), I’ll use the arrow notation:</p>
<ul>
<li>Vector: <span data-type="equation">\vec{v}</span></li>
Expand Down Expand Up @@ -885,7 +885,7 @@ <h3 id="exercise-15">Exercise 1.5</h3>
<p>Create a simulation of an object (think about a vehicle) that accelerates when you press the up arrow and brakes when you press the down arrow.</p>
</div>
<h3 id="algorithm-2-random-acceleration">Algorithm 2: Random Acceleration</h3>
<div data-type="video-link" data-title="Video 1.3: Random Vectors" href="https://thecodingtrain.com/tracks/the-nature-of-code-2/noc/1-vectors/3-random-vectors"></div>
<div data-type="video-link" data-title="Random Vectors" href="https://www.youtube.com/watch?v=jupjuq9Jl-M"></div>
<p>Now on to Acceleration Algorithm 2, a random acceleration. In this case, instead of initializing <code>acceleration</code> in the object’s constructor, I want to randomly set its value inside the <code>update()</code> method. This way, the object will get a different acceleration vector for every frame of the animation:</p>
<pre class="codesplit" data-code-language="javascript">update() {
// The <code>random2D()</code> method returns a unit vector pointing in a random direction.
Expand Down Expand Up @@ -916,7 +916,7 @@ <h3 id="exercise-16">Exercise 1.6</h3>
<p>Referring back to Exercise 0.6, implement an acceleration calculated with Perlin noise.</p>
</div>
<h3 id="static-vs-nonstatic-methods">Static vs. Nonstatic Methods</h3>
<div data-type="video-link" data-title="Video 1.4: Static Functions" href="https://thecodingtrain.com/tracks/the-nature-of-code-2/noc/1-vectors/4-static-functions"></div>
<div data-type="video-link" data-title="Static Functions" href="https://www.youtube.com/watch?v=YN8Q-QEmQ8Y&#x26;embeds_referring_euri=https://thecodingtrain.com/&#x26;embeds_referring_origin=https://thecodingtrain.com&#x26;source_ve_path=OTY3MTQ&#x26;feature=emb_imp_woyt"></div>
<p>You might have noticed something a bit odd and unfamiliar in the previous example. The<br><code>random2D()</code> method used to create a random unit vector was called on the class name, as in <code>p5.Vector.random2D()</code>, rather than on the current instance of the class, as in <code>this.random2D()</code>.<br>This is because <code>random2D()</code> is a <strong>static method</strong>, meaning it’s associated with the class as a whole rather than the individual objects (that is, the instances of that class).</p>
<p>Static methods are rarely needed when you’re writing your own classes (like <code>Walker</code> or <code>Mover</code>), so you may not have encountered them before. They sometimes form an important part of prewritten classes like <code>p5.Vector</code>, however. In fact, Acceleration Algorithm 3 (accelerate toward the mouse) requires further use of this concept, so let’s take a step back and consider the difference between static and nonstatic methods.</p>
<p>Setting aside vectors for a second, take a look at the following code:</p>
Expand Down Expand Up @@ -996,7 +996,7 @@ <h3 id="algorithm-3-interactive-motion">Algorithm 3: Interactive Motion</h3>
let direction = p5.Vector.sub(mouse, this.position);</pre>
<p>I’ve used the static version of <code>sub()</code> to create a new vector <code>direction</code> that points from the mover’s position to the mouse. If the object were to actually accelerate using that vector, however, it would appear instantaneously at the mouse position, since the magnitude of <code>direction</code> is equal to the distance between the object and the mouse. This wouldn’t make for a smooth animation, of course. The next step, therefore, is to decide how quickly the object should accelerate toward the mouse by changing the vector’s magnitude.</p>
<p>To set the magnitude (whatever it may be) of the acceleration vector, I must first ______ the vector. That’s right, you said it: <em>normalize</em>! If I can shrink the vector to its unit vector (of length 1), I can easily scale it to any other value, because 1 multiplied by anything equals anything:</p>
<div data-type="video-link" data-title="Video 1.5: A Unit Vector" href="https://thecodingtrain.com/tracks/the-nature-of-code-2/noc/1-vectors/5-unit-vector"></div>
<div data-type="video-link" data-title="A Unit Vector" href="https://thecodingtrain.com/tracks/the-nature-of-code-2/noc/1-vectors/5-unit-vector"></div>
<pre class="codesplit" data-code-language="javascript">//{!1} Any number!
let anything = __________________;
direction.normalize();
Expand All @@ -1014,7 +1014,7 @@ <h3 id="algorithm-3-interactive-motion">Algorithm 3: Interactive Motion</h3>
dir.setMag(anything);</pre>
</div>
<p>In this next example, to emphasize the math, I’m going to write the code using <code>normalize()</code> and <code>mult()</code>, but this is likely the last time I’ll do that. You’ll find <code>setMag()</code> in examples going forward.</p>
<div data-type="video-link" data-title="Video 1.6: Acceleration Vector" href="https://thecodingtrain.com/tracks/the-nature-of-code-2/noc/1-vectors/6-acceleration-vector"></div>
<div data-type="video-link" data-title="Acceleration Vector" href="https://thecodingtrain.com/tracks/the-nature-of-code-2/noc/1-vectors/6-acceleration-vector"></div>
<div data-type="example">
<h3 id="example-110-accelerating-toward-the-mouse">Example 1.10: Accelerating Toward the Mouse</h3>
<figure>
Expand Down
2 changes: 1 addition & 1 deletion content/09_ga.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ <h1 id="chapter-9-evolutionary-computing">Chapter 9. Evolutionary Computing</h1>
</div>
<div class="chapter-opening-figure">
<figure>
<img src="images/09_ga/09_ga_1.png" alt="">
<img src="images/09_ga/09_ga_1.jpg" alt="">
<figcaption></figcaption>
</figure>
<h3 id="pueblo-pottery-photo-courtesy-of-the-national-park-service">Pueblo pottery (photo courtesy of the National Park Service)</h3>
Expand Down
Binary file modified content/images/01_vectors/01_vectors_1.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added content/images/09_ga/09_ga_1.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed content/images/09_ga/09_ga_1.png
Binary file not shown.
29 changes: 23 additions & 6 deletions gatsby/lib/parse-content.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,13 @@ import { toString } from 'hast-util-to-string';

import { rehypeCodesplit } from './codesplit.mjs';

function isHeading(node) {
return node.type === 'element' && /^h[1-6]$/i.test(node.tagName);
}

export function parseContent(html) {
const replaceMedia = () => (tree) => {
visit(tree, { tagName: 'div' }, (node) => {
visit(tree, { tagName: 'div' }, (node, index, parent) => {
if (
node.properties.className &&
Array.isArray(node.properties.className) &&
Expand Down Expand Up @@ -44,6 +48,19 @@ export function parseContent(html) {
node.tagName = 'embed-example';
}

if (
node.properties.dataType === 'video-link' &&
node.properties.dataTitle &&
index > 2 &&
isHeading(parent.children[index - 2])
) {
node.tagName = 'video-link';

// move the video-link node inside the last adjacent heading
parent.children[index - 2].children.push(node);
parent.children.splice(index, 1);
}

if (
node.properties.dataType === 'note' ||
node.properties.dataType === 'exercise' ||
Expand Down Expand Up @@ -151,18 +168,18 @@ export function parseContent(html) {
const description = paragraphs.join(' ').trim().substring(0, 150);

const transformedAst = unified()
.use(replaceMedia)
.use(externalLinkInNewTab)
.use(rehypeCodesplit)
.use(rehypeHighlight)
.use(rehypeSlug)
.use(rehypeAutolinkHeadings, {
behavior: 'wrap',
test: ['h2', 'h3'],
properties: {
class: 'heading-link',
},
})
.use(replaceMedia)
.use(externalLinkInNewTab)
.use(rehypeCodesplit)
.use(rehypeHighlight)
.use(rehypeSlug)
.use(rehypeKatex)
.runSync(ast);

Expand Down
4 changes: 4 additions & 0 deletions magicbook/stylesheets/components/callout.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
div[data-type='video-link'] {
display: none;
}

span.highlight,
div[data-type='exercise'],
div[data-type='note'],
Expand Down
64 changes: 64 additions & 0 deletions src/components/VideoLink.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React from 'react';
import { FaYoutube, FaPlay } from 'react-icons/fa';

function getVideoIdFromYoutubeUrl(url) {
try {
const parsedUrl = new URL(url);
if (
parsedUrl.hostname === 'www.youtube.com' ||
parsedUrl.hostname === 'youtube.com'
) {
return parsedUrl.searchParams.get('v');
} else if (parsedUrl.hostname === 'youtu.be') {
return parsedUrl.pathname.slice(1);
}
return null;
} catch (e) {
return null;
}
}

const VideoLink = (props) => {
const videoId = getVideoIdFromYoutubeUrl(props['href']);

return (
<div className="inline-block">
<a
className="group relative flex items-center gap-2 text-base text-noc-400 no-underline"
href={props['href']}
target="_blank"
rel="noreferrer"
>
<FaYoutube />
{props['data-title']}

{videoId && (
<div className="not-prose absolute top-0 hidden w-80 pt-10 group-hover:block">
<div className="relative rounded-lg bg-noc-400 bg-opacity-50 p-4">
<div className="flex aspect-video items-center overflow-hidden rounded object-cover">
<picture>
<source
type="image/webp"
srcSet={`https://img.youtube.com/vi_webp/${videoId}/sddefault.webp`}
/>
<source
type="image/jpeg"
srcSet={`https://i.ytimg.com/vi/${videoId}/sddefault.jpg`}
/>
<img
src={`https://i.ytimg.com/vi/${videoId}/sddefault.jpg`}
alt="youtube video thumbnail"
/>
</picture>
</div>

<FaPlay className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 text-4xl text-noc-400" />
</div>
</div>
)}
</a>
</div>
);
};

export default VideoLink;
2 changes: 2 additions & 0 deletions src/layouts/ChapterLayout.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import SideNavLayout from './SideNavLayout';
import PrevNextButtons from '../components/PrevNextButtons';
import Image from '../components/Image';
import Example from '../components/Example';
import VideoLink from '../components/VideoLink';

const renderAst = ({ ast, images }) => {
visit(ast, { tagName: 'img' }, (node) => {
Expand All @@ -29,6 +30,7 @@ const renderAst = ({ ast, images }) => {
components: {
'gatsby-image': Image,
'embed-example': Example,
'video-link': VideoLink,
},
});

Expand Down
2 changes: 1 addition & 1 deletion src/styles/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
}

.heading-link {
@apply relative font-inherit no-underline;
@apply relative mr-6 font-inherit no-underline;
}

.heading-link::before {
Expand Down