Merge branch 'main' into CollaborationGit

This commit is contained in:
Tom van Woudenberg 2024-04-11 09:13:10 +02:00
commit 32e66601e2
21 changed files with 27665 additions and 63 deletions

View file

@ -0,0 +1,11 @@
# Interactive HTML/JavaScript elements
In this section, we will discuss how to [create](creating_basic_HTML_elements.md) and [embed](adding_HTML_elements.md) interactive HTML/Javascript elements created in HTML/JavaScript in a TeachBook. These HTML/Javascript element work very fluently and don't require a python kernel running in the background.
This could be for example interactive 3d figures:
<iframe src="../_static/element_render_box.html" width="600" height="400" frameborder="0"></iframe>
Or other interactive graphs:
<iframe src="../_static/element_pdf_and_cdf.html" width="600" height="300" frameborder="0"></iframe>

View file

@ -0,0 +1,197 @@
# Adding interactive HTML/JavaScript elements
In this section, we will discuss how to embed interactive elements created in HTML/JavaScript in a TeachBook. If you are interested in learning how to create these elements yourself, we will discuss an example in the [next article](creating_basic_HTML_elements.md). As an example, let us discuss how to embed an interactive 3D model of a subsurface environment I created:
<iframe src="../_static/element_render_box.html" width="600" height="400" frameborder="0"></iframe>
## Storing the HTML file
TeachBooks like this one are built from a Git project, comprising of different files and folders that are assembled into a webpage. It is generally not possible to paste the code of your HTML element directly into one of the project's Markdown files. Instead, you will have to store the files separately somewhere in the folder structure, then embed them later on. For this project, the book's root folder contains a ´_static´ folder which we can use to store our HTML/JS file. Some elemenets - such as the one above - may require additional files. The HTML element above, for instance, specifies a renderer using [THREE.js](https://threejs.org/) that displays a digital model. This model is specified as an .obj file (which defines its geometry) and a .mtl file (which defines its materials - its colors, reflectivity, transparency, and so on). We can store these files in the same ´_static´ folder.
## Embedding HTML files via iframes
If we want to embed our HTML/JS element in a Markdown page, we must use an inline frame, also known as an iframe. In code, an iframe looks like this:
```html
<iframe src="../_static/element_render box.html" width="600" height="300" frameborder="0"></iframe>
```
What does this code do? In HTML, certain active parts of the code are defined as tags surrounded by `<` and `>` symbols. The tag `<iframe>` opens the iframe environment, the `<iframe/>` tag closes it again. Inside the opening tag, we can further specify properties of the iframe. These properties are usually defined as strings, that is to say, surrounded by quotation marks. Some of the more common iframe properties are:
- `src`: This specifies the **source** of the iframe, the HTML file it will display.
- `width`: This specifies the **width** of the iframe in pixels.
- `height`: This specifies the **height** of the iframe in pixels.
- `frameborder`: This flag specifies whether a border should be drawn around the iframe (`"1"`) or not (`"0"`).
- `scrolling`: Specifies whether or not to display scrollbars for the iframe, in case the embedded content is larger than the iframe. Possible values are `"yes"`, `"no"`, and `"auto"`. I recommend either creating HTML elements of the same size, or adjusting their size dynamically to their contained - in this case, the iframe.
- `allowfullscreen`: Specifies whether or not to allow the iframe to be displayed in fullscreen mode.
- `loading`: Specifies how the browser should handle the loading of the iframe content. Possible values are eager (load immediately) and lazy (load on demand).
Let's take a closer look at the source. Since this project uses a local file structure, we can make use of relative paths such as `../_static/element_render box.html`. What does this path do? In this project, the markdown file for this article is located in the directory `book/features/adding_HTML_elements.md`. The HTML element we want to embed is located in the directory `book/_static/element_render_box.html`. The relative path thus works as follows:
- We start in the markdown file's directory `book/features/adding_HTML_elements.md`
- The string `"../"` tells the iframe to move up one folder to `book`
- The following string `"_static/"` tells the iframe to navigate into the `_static` folder. We are now in `book/_static/`.
- Finally, the string `"element_render_box.html"` selects the HTML file we want to embed. We are now in `book/_static/element_render_box.html`.
## Viewing result at local build
To view the final result locally, it's required to setup a [local python webserver](setup-local-server).
## HTML code for the 3D render element
To conclude, I provide the HTML/JavaScript code for the 3D render element above. To understand what the code below does in more detail, please refer to the [next article](creating_basic_HTML_elements.md). One thing I want to direct your attention to are the paths specified in the first few lines of the `<script>` environment. Here, we do not have to navigate to a different local folder because we initially stored our `.obj` and `.mtl` files in the same folder as the `element_render_box.html` file. If they would be stored in a different folder, we have to specify either an absolute path (e.g., via a URL) or a relative path, as explained above.
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Subsurface</title>
<style>
html, body {
margin: 0;
height: 100%;
}
#c {
width: 100%;
height: 100%;
display: block;
}
</style>
</head>
<body>
<canvas id="c"></canvas>
<script type="module">
// Path to object
var path_obj = "bimodal_field.obj";
var path_mtl = "bimodal_field.mtl";
// Three.js - Load .OBJ and .MTL file - Windmill2
// from https://threejsfundamentals.org/threejs/threejs-load-obj-materials-windmill2.html
import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r127/build/three.module.js';
import { OrbitControls } from 'https://threejsfundamentals.org/threejs/resources/threejs/r127/examples/jsm/controls/OrbitControls.js';
import { OBJLoader } from 'https://threejsfundamentals.org/threejs/resources/threejs/r127/examples/jsm/loaders/OBJLoader.js';
import { MTLLoader } from 'https://threejsfundamentals.org/threejs/resources/threejs/r127/examples/jsm/loaders/MTLLoader.js';
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({ canvas });
const fov = 45;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 100;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(0, 10, 20);
const controls = new OrbitControls(camera, canvas);
controls.target.set(0, 5, 0);
controls.update();
const scene = new THREE.Scene();
scene.background = new THREE.Color('white');
{
const skyColor = 0xB1E1FF; // light blue
const groundColor = 0xB97A20; // brownish orange
const intensity = 1;
const light = new THREE.HemisphereLight(skyColor, groundColor, intensity);
scene.add(light);
}
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(5, 10, 2);
scene.add(light);
scene.add(light.target);
}
function frameArea(sizeToFitOnScreen, boxSize, boxCenter, camera) {
const halfSizeToFitOnScreen = sizeToFitOnScreen * 0.5;
const halfFovY = THREE.MathUtils.degToRad(camera.fov * .5);
const distance = halfSizeToFitOnScreen / Math.tan(halfFovY);
// compute a unit vector that points in the direction the camera is now
// in the xz plane from the center of the box
const direction = (new THREE.Vector3())
.subVectors(camera.position, boxCenter)
.multiply(new THREE.Vector3(1, 0, 1))
.normalize();
// move the camera to a position distance units way from the center
// in whatever direction the camera was from the center already
camera.position.copy(direction.multiplyScalar(distance).add(boxCenter));
// pick some near and far values for the frustum that
// will contain the box.
camera.near = boxSize / 100;
camera.far = boxSize * 100;
camera.updateProjectionMatrix();
// point the camera to look at the center of the box
camera.lookAt(boxCenter.x, boxCenter.y, boxCenter.z);
}
{
const mtlLoader = new MTLLoader();
mtlLoader.load(path_mtl, (mtl) => {
mtl.preload();
const objLoader = new OBJLoader();
objLoader.setMaterials(mtl);
objLoader.load(path_obj, (root) => {
scene.add(root);
// compute the box that contains all the stuff
// from root and below
const box = new THREE.Box3().setFromObject(root);
const boxSize = box.getSize(new THREE.Vector3()).length();
const boxCenter = box.getCenter(new THREE.Vector3());
// set the camera to frame the box
frameArea(boxSize * 1.2, boxSize, boxCenter, camera);
// update the Trackball controls to handle the new size
controls.maxDistance = boxSize * 10;
controls.target.copy(boxCenter);
controls.update();
});
});
}
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render() {
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
</script>
</body>
</html>
```

View file

@ -1,4 +1,4 @@
# Adding basic interactive HTML/JavaScript elements
# Creating basic interactive HTML/JavaScript elements
## Embedding HTML files
A straightforward way to create interactive web-based elements is to code them directly using HTML with JavaScript. This allows us to build elements such as the one below, which shows the connection between a probability density function (pdf) and the associated cumulative density function (cdf):

View file

@ -15,9 +15,8 @@
" - JupyterQuiz: code-based implementation of multiple-choiche questions and numerical answer questions. This is a basic open course tool which requires you to write your quiz questions in Python language as a `dict` or to a `.json` file.\n",
" - H5p: TU-Delft-licensed tool to create questions (and many other interactive elements) using an [online GUI](https://tudelft.h5p.com/). These elements are embedded into the book using an iframe which requires very little coding. This elements are easier to implement than the JupyterQuiz and have more features (although proper numerical answer questions are missing). More information on this is provided in [this subchapter](h5p.md)\n",
" - Grasple-exercises: TU-Delft-licensed tool to creating math-based quiz questions. Again, these elements are embedded into the book using an iframe which requiries very little coding. These elements are very well suited to math-based problems as it allows analytical evaluation of numerical values and formulas. Furthermore, Grasple allows parameterization (not required for formative exercises). More information on this is provided in [this subchapter](grasple.md)\n",
" - Non-kernel-based widgets: You can create interactive widgets with Python which doesn't need a kernel to run online:\n",
" - You can create html/javascript widgets, an example is shown in [this subchapter](adding_basic_HTML_elements.md)\n",
" - The use of the [plotly](https://plotly.com/python/getting-started/) package is an example which allows this use.\n",
" - Custom HTML/Javascript widgets: You can create and embed html/javascript widgets, an example is shown in [this subchapter](HTML_javascript.md)\n",
" - HTML/Javascript widgets using Plotly. The use of the [plotly](https://plotly.com/python/getting-started/) package is an example which allows you to create HTML/Javascript widgets from python code without any knowledge on HTML/Javascript.\n",
"\n",
"Examples of each type is shown below based on the following problem:\n",
"\n",
@ -310,7 +309,18 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## Interactive widget without kernel\n",
"## HTML/Javascript\n",
"\n",
"The example below shows a custom HTML/Javascript element\n",
"\n",
"<iframe src=\"../_static/element_pdf_and_cdf.html\" width=\"600\" height=\"300\" frameborder=\"0\"></iframe>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Plotly graph\n",
"\n",
"The example below is taken from the FEM-module by Frans van der Meer.\n",
"When using Plotly graphs, make sure the notebook is executed in Jupyter Lab / Jupyter Notebook for the figures to be shown in the book. Running the code in VS code might break the result in the book, although the output is visible in VS code. Alternatively, add the following lines of code to your notebook to have a valid output in the book as well:\n",

View file

@ -1,4 +1,4 @@
# JupyterLite-Sphinx: Interactive elements
# JupyterLite-Sphinx
By popular demand: how to include interactive pieces of code in your Jupyter Book. We can do this by using the [JupyterLite](https://github.com/jupyterlite) app, which allows you to run a Jupyter Lab session and execute Python code entirely in-browser. Jupyterlite can be embedded in a Jupyter Book with the [`JupyterLite-Sphinx`](https://github.com/jupyterlite/jupyterlite-sphinx) extension.

View file

@ -4,7 +4,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# Python-enabled interactivity\n",
"# Sphinx-thebe\n",
"\n",
"Our book has been enable to run Python code live in the browser (thanks Max!). This page contains some installation instructions and the other sections show how to use this functionality to create interactive figures and feedback on code\n",
"\n",
@ -74,11 +74,7 @@
"\n",
"## Instructions: local build\n",
"\n",
"To test the fuctionality, you have to run a local server, otherwise the interactivity doens't work. So, you cannot just open the `index.html` file any more. This can be done with the following steps:\n",
"1. build the book by running ` jupyter book build <directory of your book>`. If you are using Windows you should use the Git Bash terminal. Set this as your default in VS Code, then open it using \"New Terminal.\" If you have not already done so, you will need to allow Git Bash to use your Python libraries by running the command `conda init bash`.\n",
"2. Set up a server by running `python -m http.server 8000`.\n",
"3. Open the book by going to [http://localhost:8000/](http://localhost:8000/) in your browser. You may need to reload the page once the rebuild is complete. You can do this on Chrome by right-clicking somewhere on the page, select \"Inspect\", open the \"Network\" tab, then reload with `CTRL+R`. Or try `ctrl`+`F5`\n",
"4. Go to any page that is created from a Jupyter notebook\n",
"To test the fuctionality, you have to run a local server, otherwise the interactivity doens't work. So, you cannot just open the `index.html` file any more. This can be done with the steps shown in {ref}`setup-local-server`.\n",
"\n",
"If you push to main, you can test the interactivity on the book-draft website as well.\n",
"\n",
@ -115,11 +111,6 @@
"%pip install package_name\n",
"```\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
}
],
"metadata": {