update feed generation

This commit is contained in:
Tom 2025-01-11 19:13:45 +00:00
parent 5461fb9082
commit 7626386952
7 changed files with 521 additions and 12 deletions

View File

@ -1,8 +1,11 @@
title: Tom Hodson
description: Physics, Programming and Baking.
url: https://thomashodson.com
author:
twitter: T_Hodson
feed:
posts_limit: 100
update_period: monthly
update_frequency: 1
exclude: ["env/", ]
host: 0.0.0.0
@ -29,12 +32,10 @@ include:
- node_modules
plugins:
- jekyll-feed
# - jekyll-feed
- jekyll-redirect-from
- flexible_include
- kramdown-syntax-coderay
- jekyll-last-modified-at
feed:
posts_limit: 100

View File

@ -59,6 +59,7 @@
{% if page.mathjax %}
<script>
console.log("MathJax is enabled for this page. Enjoy the math!");
MathJax = {
tex: {
inlineMath: [['$', '$'], ['\\(', '\\)']]

View File

@ -2,16 +2,49 @@
title:
layout: post
excerpt: A one sentence summary.
draft: true
# Just a helper for the path if the page has lots of images.
images: /assets/images/2024
assets: # I often use this for model paths
# The thumbnail that goes on the blog or posts page. SVG if possible.
thumbnail: /assets/blog/template/thumbnail.svg
# The social media preview image, must be png or other raster.
social_image: /assets/blog/template/thumbnail.png
# The alt text for both images.
alt: An image of the text "{...}" to suggest the idea of a template.
image_class: invertable # For images that look good when inverted in dark modes by default they're dimmed
mathjax: true
head: |
<script async src="/node_modules/es-module-shims/dist/es-module-shims.js"></script>
<script type="importmap">
{
"imports": {
"three": "/node_modules/three/build/three.module.min.js",
"three/addons/": "/node_modules/three/examples/jsm/",
"lil-gui": "/node_modules/lil-gui/dist/lil-gui.esm.min.js"
}
}
</script>
<script src="/assets/js/projects.js" type="module"></script>
images:
thumbnail:
social_image:
assets:
alt:
---
This page acts as both a reminder of how I do various things on this blog and also serves as a canary to see if I've broken the layout inadvertently.
See [this kramdown cheatsheet](https://kramdown.gettalong.org/quickref.html)
## Subtitle
The first big project of the year was repainting this ladder up to our mezzanine bed. This ended up being so much more work than we expected, they say it's all in the surface prep and the surface prep here took ages with all the awkward corners.
@ -20,16 +53,312 @@ There was one aspect that was fun with this which was that I made non-slip pads
<figure>
<img src="{{page.images}}/train_view.jpeg">
<figcaption>Crossing the alps by train provided some good views and also some good gaming time on my then new Steam Deck.</figcaption>
<figcaption> A single large image. </figcaption>
</figure>
<figure class="two-wide">
<img src="{{page.images}}/ladder/late_into_the_night.jpeg">
<img src="{{page.images}}/ladder/finished.jpeg">
<figcaption> Two images side by side. </figcaption>
</figure>
<figure class="multiple">
<img src="{{page.images}}/chop_saw_dust_collector/print_preview.jpeg">
<img src="{{page.images}}/chop_saw_dust_collector/printing.jpeg">
<img src="{{page.images}}/chop_saw_dust_collector/installed.jpeg">
<figcaption> More than two images layed out nicely. </figcaption>
</figure>
Four images:
<figure class="multiple">
<img src="{{page.images}}/lasercutting/boxes.jpeg">
<img src="{{page.images}}/usbc_psu/case_proto_1.jpeg">
<img src="{{page.images}}/usbc_psu/case_proto_2.jpeg">
<img src="{{page.images}}/usbc_psu/case_proto_3.jpeg">
</figure>
A very long image:
<figure>
<img src="{{page.images}}/logos.jpeg">
<figcaption>Played around with some logo designs that I could stamp into ceramics.</figcaption>
</figure>
1. Item one
* sub item one
* sub item two
* sub item three
2. Item two
A table:
| Power | Voltage | Current |
|-------|---------|---------|
| 15W | 5 V | 3A |
| 27W | 9 V | 3A |
| 45W | 15 V | 3A |
| 60W | 20 V | 3A |
| 100W* | 20V | 5A |
## Line Element
So the setup is this: Imagine we draw a very short line vector $\vec{v}$ and let it flow along in a fluid with velocity field $u(\vec{x}, t)$.
<figure style="max-width: 250px;">
<img src="/assets/blog/astrophysical_fluids/line_element.svg" class="invertable">
<figcaption>A line element $\delta \vec{v}$ being dragged aloung in a fluid with velocity field $u(\vec{x}, t)$</figcaption>
</figure>
Three things will happen, the vector will be translated along, it will change length and it will change direction. If we ignore the translation, we can ask what the equation would be for the change in length and direction of $\vec{v}$. I'll drop the vector symbols on $v$, $u$ and $x$ from now on.
$$ D_t \; v = ? $$
If we assume $v$ is very small we can think about expanding $u$ to first order along $v$
$$ u(x + v, t) = u(x, t) + v \cdot \nabla u $$
where $v \cdot \nabla$ is the directional derivative $v_x \partial_x + v_y \partial_y + v_y \partial_y$ and when $v$ is infinitesimal it just directly tells us how $u$ will change if we move from point $x$ to point $x + v$.
So from this we can see that one end of our vector $v$ is moving along at $u(x, t)$ while the other end will move at $u(x, t) + v \cdot \nabla u$ hence:
$$ D_t \; v = v \cdot \nabla u $$
Below is a more “indexbyindex” look at how one carries out **Step 3** in detail. We start from
**math with color**:
$${\color{red} x} + {\color{blue} y}$$
$$
\frac{D}{Dt}\,\delta S_i
\;=\;
\varepsilon_{i j k}\,\bigl(\tfrac{D}{Dt}\delta x_j^{(1)}\bigr)\,\delta x_k^{(2)}
\;+\;
\varepsilon_{i j k}\,\delta x_j^{(1)}\,\bigl(\tfrac{D}{Dt}\delta x_k^{(2)}\bigr),
$$
and then substitute
$$
\frac{D}{Dt}\,\delta x_j^{(1)}
\;=\;
\delta x_\ell^{(1)}\,\frac{\partial u_j}{\partial x_\ell},
\quad
\frac{D}{Dt}\,\delta x_k^{(2)}
\;=\;
\delta x_\ell^{(2)}\,\frac{\partial u_k}{\partial x_\ell}.
$$
I like these underbraces:
$$
\frac{D}{Dt}\,\delta S_i
\;=\;
\underbrace{\varepsilon_{i j k}\,\delta x_\ell^{(1)}\,\frac{\partial u_j}{\partial x_\ell}\,\delta x_k^{(2)}}
_{T_{1}}
\;+\;
\underbrace{\varepsilon_{i j k}\,\delta x_j^{(1)}\,\delta x_\ell^{(2)}\,\frac{\partial u_k}{\partial x_\ell}}
_{T_{2}}.
$$
References:
[This is a link to the subtitle heading at the top of the page](#subtitle)
A [link][kramdown hp]
to the homepage.
[kramdown hp]: http://kramdown.gettalong.org "hp"
This is a text with a
footnote[^1].
[^1]: And here is the definition.
This is a text with a
footnote[^2].
[^2]:
And here is the definition.
> With a quote!
and some math
$$ x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a} $$
* * *
<canvas style ="width: 100%;" id="myCanvas"></canvas>
<script type="module">
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import { DragControls } from "three/addons/controls/DragControls.js";
let scene, camera, renderer;
let orbitControls, dragControls;
let sphereA, sphereB;
let arrowA, arrowB, arrowCross;
let objectsToDrag = [];
const d = 1;
init();
animate();
function init() {
const canvas = document.getElementById('myCanvas');
// --- Scene ---
scene = new THREE.Scene();
const aspect = canvas.clientWidth / canvas.clientHeight;
camera = new THREE.OrthographicCamera(
-d * aspect, // left
d * aspect, // right
d, // top
-d, // bottom
-100, // near
100 // far
);
camera.position.set(5, 5, 5);
camera.lookAt(0, 0, 0);
// --- Renderer (use the existing canvas) ---
renderer = new THREE.WebGLRenderer({ alpha: true, canvas: canvas, antialias: true });
renderer.setSize(canvas.clientWidth, canvas.clientHeight,);
// --- OrbitControls ---
orbitControls = new OrbitControls(camera, renderer.domElement);
// orbitControls.enableRotate = false; // Keep isometric
orbitControls.enablePan = false;
orbitControls.enableDamping = true;
orbitControls.dampingFactor = 0.05;
// const gridHelper = new THREE.GridHelper(5, 25, 0x444444, 0x888888);
// scene.add(gridHelper);
// --- Spheres for vector endpoints (draggable) ---
const sphereGeom = new THREE.SphereGeometry(0.08, 16, 16);
const sphereMat = new THREE.MeshStandardMaterial({ color: 0x000000 });
sphereA = new THREE.Mesh(sphereGeom, sphereMat);
sphereB = new THREE.Mesh(sphereGeom, sphereMat);
// Initial positions
sphereA.position.set(0, 0, 1);
sphereB.position.set(1, 0, 0);
scene.add(sphereA);
scene.add(sphereB);
objectsToDrag.push(sphereA, sphereB);
// --- Lights ---
const ambientLight = new THREE.AmbientLight(0xffffff, 0.7);
scene.add(ambientLight);
const dirLight = new THREE.DirectionalLight(0xffffff, 0.7);
dirLight.position.set(5, 5, 10);
scene.add(dirLight);
// --- Arrows for A, B, and A x B ---
const headLength = 0.1;
const headWidth = 0.1;
arrowA = new THREE.ArrowHelper(
new THREE.Vector3(1, 0, 0).normalize(),
new THREE.Vector3(0, 0, 0),
1,
0x000000, headLength, headWidth
);
arrowB = new THREE.ArrowHelper(
new THREE.Vector3(0, 1, 0).normalize(),
new THREE.Vector3(0, 0, 0),
1,
0x000000, headLength, headWidth
);
// Cross product arrow in red
arrowCross = new THREE.ArrowHelper(
new THREE.Vector3(0, 0, 1).normalize(),
new THREE.Vector3(0, 0, 0),
1,
0xff0000, headLength, headWidth
);
scene.add(arrowA);
scene.add(arrowB);
scene.add(arrowCross);
// --- DragControls ---
dragControls = new DragControls(objectsToDrag, camera, renderer.domElement);
// Disable orbiting during drag
dragControls.addEventListener('dragstart', function () {
orbitControls.enabled = false;
});
dragControls.addEventListener('dragend', function () {
orbitControls.enabled = true;
});
// Keep spheres in XZ plane, update arrows
dragControls.addEventListener('drag', (event) => {
event.object.position.y = 0;
updateArrows();
});
updateArrows();
window.addEventListener('resize', onWindowResize, false);
}
// Update arrow directions and lengths based on sphere positions
function updateArrows() {
const A = new THREE.Vector3().copy(sphereA.position);
const B = new THREE.Vector3().copy(sphereB.position);
// Update arrow A
const lengthA = A.length();
arrowA.setLength(lengthA, 0.2 * lengthA, 0.2 * lengthA);
arrowA.setDirection(A.clone().normalize());
arrowA.position.set(0, 0, 0);
// Update arrow B
const lengthB = B.length();
arrowB.setLength(lengthB, 0.2 * lengthB, 0.2 * lengthB);
arrowB.setDirection(B.clone().normalize());
arrowB.position.set(0, 0, 0);
// A x B
const cross = new THREE.Vector3().crossVectors(A, B);
const lengthCross = cross.length();
let directionCross = cross.clone().normalize();
// If cross is zero (or close to zero), set a default
if (isNaN(directionCross.x)) {
directionCross.set(0, 0, 1);
}
arrowCross.setDirection(directionCross);
arrowCross.setLength(lengthCross, 0.2 * lengthCross, 0.2 * lengthCross);
arrowCross.position.set(0, 0, 0);
}
function onWindowResize() {
const aspect = canvas.clientWidth / canvas.clientHeight;
camera.left = -d * aspect;
camera.right = d * aspect;
camera.top = d;
camera.bottom = -d;
camera.updateProjectionMatrix();
renderer.setSize(canvas.clientWidth, canvas.clientHeight);
}
function animate() {
requestAnimationFrame(animate);
orbitControls.update();
renderer.render(scene, camera);
}
</script>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@ -0,0 +1,105 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="200"
height="200"
viewBox="0 -750 11049.043 11052.166"
aria-hidden="true"
version="1.1"
id="svg161"
sodipodi:docname="thumbnail.svg"
inkscape:version="1.2.2 (b0a84865, 2022-12-01)"
inkscape:export-filename="thumbnail.png"
inkscape:export-xdpi="150"
inkscape:export-ydpi="150"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview163"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="1.3222267"
inkscape:cx="2.6470498"
inkscape:cy="63.907346"
inkscape:window-width="1390"
inkscape:window-height="1205"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="0"
inkscape:current-layer="svg161" />
<defs
id="defs135">
<path
id="MJX-24-TEX-N-7B"
d="m 434,-231 q 0,-13 -6,-19 h -18 q -129,0 -180,66 -5,7 -8,12 -3,5 -5,11 -2,6 -4,13 -2,7 -2,15 0,8 -1,22 -1,14 -1,27 0,13 0,37 0,24 0,47 0,21 0,53 -1,89 -5,100 -1,1 -1,2 -14,36 -50,56 -36,20 -71,20 -11,0 -14,3 -3,3 -3,16 0,13 3,16 3,3 14,3 34,0 70,20 36,20 51,56 5,11 5,32 0,21 1,152 v 50 q 0,55 6,77 6,22 29,42 26,26 80,42 37,8 53,9 2,0 13,0 11,0 18,1 h 20 q 6,-6 6,-18 0,-13 -3,-16 -2,-3 -16,-3 -53,-3 -83,-24 -30,-21 -36,-42 -5,-13 -5,-148 v -82 q 0,-47 -3,-64 -3,-17 -17,-39 -31,-43 -87,-59 l -14,-5 14,-5 q 18,-6 36,-15 18,-9 42,-34 24,-25 28,-59 1,-6 1,-136 0,-135 5,-148 10,-27 43,-45 33,-18 76,-21 14,0 16,-3 3,-3 3,-15 z" />
<path
id="MJX-24-TEX-N-2E"
d="m 78,60 q 0,24 17,42 17,18 43,18 24,0 42,-16 Q 198,88 199,61 199,36 182,18 165,0 139,0 113,0 96,17 79,34 78,60 Z" />
<path
id="MJX-24-TEX-N-7D"
d="m 65,731 q 0,14 3,16 3,2 20,3 83,0 128,-25 45,-25 63,-55 9,-21 10,-35 1,-14 2,-134 1,-139 2,-144 13,-45 52,-66 39,-21 72,-22 11,0 14,-3 3,-3 3,-16 0,-13 -3,-16 -3,-3 -14,-3 -37,0 -72,-21 -35,-21 -47,-53 -5,-14 -6,-36 -1,-22 -1,-149 v -51 q 0,-55 -6,-77 -6,-22 -29,-42 -54,-52 -167,-52 -18,0 -21,3 -3,3 -3,17 0,6 0,7 0,1 1,5 1,4 3,4 2,0 8,1 14,0 31,3 17,3 38,10 21,7 37,23 16,16 24,38 1,5 2,142 l 1,136 q 13,57 70,91 35,17 50,20 -25,7 -50,20 -55,34 -68,82 l -2,10 -1,136 q -1,137 -2,142 -12,40 -53,56 -41,16 -77,17 -9,0 -10,3 -1,3 -2,15 z" />
</defs>
<g
stroke="#000000"
fill="#000000"
stroke-width="0"
transform="matrix(3.9192269,0,0,-3.9192269,952.74332,5755.8897)"
id="g159">
<g
data-mml-node="math"
id="g157">
<g
data-mml-node="mo"
id="g139">
<use
data-c="7B"
xlink:href="#MJX-24-TEX-N-7B"
id="use137" />
</g>
<g
data-mml-node="mo"
transform="translate(500)"
id="g143">
<use
data-c="2E"
xlink:href="#MJX-24-TEX-N-2E"
id="use141" />
</g>
<g
data-mml-node="mo"
transform="translate(944.7)"
id="g147">
<use
data-c="2E"
xlink:href="#MJX-24-TEX-N-2E"
id="use145" />
</g>
<g
data-mml-node="mo"
transform="translate(1389.3)"
id="g151">
<use
data-c="2E"
xlink:href="#MJX-24-TEX-N-2E"
id="use149" />
</g>
<g
data-mml-node="mo"
transform="translate(1834)"
id="g155">
<use
data-c="7D"
xlink:href="#MJX-24-TEX-N-7D"
id="use153" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

28
feed.json Normal file
View File

@ -0,0 +1,28 @@
---
layout: none
---
{
"version": "https://jsonfeed.org/version/1",
"title": {{ site.title | jsonify }},
"description": {% if site.description %}{{ site.description | jsonify }}{% endif %},
"home_page_url": "{{ site.url }}",
"feed_url": "{{ site.url }}/feed.json",
"favicon": "{{ site.url}}/favicon.ico",
"author": {
"name": "Tom Hodson"
},
"items": [
{% for post in site.posts limit:10 %}
{% unless post.draft %}
{
"id": "{{ post.url | prepend: site.baseurl | prepend: site.url }}",
"url": "{{ post.url | prepend: site.baseurl | prepend: site.url }}",
"title": {{ post.title | jsonify }},
"content_html": {{ post.content | jsonify }},
"date_published": "{{ post.date | date_to_rfc822 }}"
}{% unless forloop.last %},{% endunless %}
{% endunless %}
{% endfor %}
]
}

45
feed.xml Normal file
View File

@ -0,0 +1,45 @@
---
layout: none
---
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
>
<channel>
<title>{{ site.name | xml_escape }}</title>
<description>{% if site.description %}{{ site.description | xml_escape }}{% endif %}</description>
<sy:updatePeriod>{{ site.feed.update_period | default: "daily" | xml_escape }}</sy:updatePeriod>
<sy:updateFrequency>{{ site.feed.update_frequency | default: 1 | xml_escape }}</sy:updateFrequency>
<link>{{ site.url }}</link>
<atom:link href="{{ site.url }}/{{ page.path }}" rel="self" type="application/rss+xml" />
<lastBuildDate>{{ site.time | date_to_rfc822 }}</lastBuildDate>
{% assign feed_items = site.feed.post_limit | default: 10 %}
{% for post in site.posts limit:feed_items %}
{% unless post.draft %}
<item>
<title>{{ post.title | xml_escape }}</title>
<dc:creator>Tom Hodson</dc:creator>
{% if post.excerpt %}
<description>
{% if post.social_image %}
<![CDATA[<img src="{{ site.url }}{{ post.social_image }}" alt="{{ post.alt }}"/>]]>
{% endif %}
{{ post.excerpt | xml_escape }}
</description>
{% else %}
<description>
Work a little, play a little, dream into summer, celebrate a great winter gone by, keep movin'... <br />]]>
{{ post.excerpt | xml_escape }}
</description>
{% endif %}
<pubDate>{{ post.date | date_to_rfc822 }}</pubDate>
<link>{{ site.url }}{{ post.url }}</link>
<guid isPermaLink="true">{{ site.url }}{{ post.url }}</guid>
</item>
{% endunless %}
{% endfor %}
</channel>
</rss>