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

visualization blog edits #66

Merged
merged 10 commits into from
Sep 24, 2020
Merged
Show file tree
Hide file tree
Changes from 8 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
File renamed without changes.
74 changes: 28 additions & 46 deletions js-plot/README.md → show-me-the-visualization/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Running C++ code in a web browser is all nice, but we really want to grab someones attention by visualizing something. In this blog we are going to make a plot from the results coming from our C++ code.

To make a plot we need some data. In a [previous](TODO fix url ../js-webapp) post we found the root of an equation using the Newton-Raphson algorithm implemented in C++ and compiled to a WebAsssembly module.
To make a plot we need some data. In the [previous post](../run-your-c%2B%2B-code-on-the-web) we found the root of an equation using the Newton-Raphson algorithm implemented in C++ and compiled to a WebAsssembly module.
A single root value makes for a depressing plot. The Newton-Raphson algorithm uses iterations to find the root so we will capture the data of each iteration and plot those.

![Image](root-plot.png)
Expand All @@ -14,12 +14,12 @@ Let's make changes to the C++ code to store the data from the iterations.

To store data of an iteration we will use a structure with the following variables:

* x, x value, starts with initial guess and ends with root result
* y, result of passing x through equation
* slope, slope or derivative at value x
* delta_x, y divided by slope
* `x`: x value, starting with the value of `initial_guess` and ending with the estimate of the `equation`'s root
* `y`: result of passing `x` through `equation`
* `slope`: result of passing `x` through `derivative`
* `delta_x`: `y` divided by `slope`

We will add `iterations` public property to the NewtonRaphson which is a vector of iteration structs. So the `newtonraphson.hpp` becomes
We will add a vector of `Iteration` `structs` named `iterations` as a public property to the `NewtonRaphson` class. So `newtonraphson.hpp` becomes:

```cpp
#ifndef H_NEWTONRAPHSON_HPP
Expand Down Expand Up @@ -47,7 +47,7 @@ class NewtonRaphson {
```
File: _newtonraphson.hpp_

The `newtonraphson.cpp` is rewritten from a while loop to a do while loop like with a push to the iterations vector each cycle.
The `do` loop in `newtonraphson.cpp` is updated to include a `push_back` to the `iterations` vector. This way, we can record the value of relevant variables in each cycle, as follows:

```cpp
#include "newtonraphson.hpp"
Expand All @@ -72,7 +72,7 @@ float NewtonRaphson::solve(float initial_guess) {
```
File: _newtonraphson.cpp_.

Before we go into Emscripten world, lets first test our c++ code. We will check if the iteration property is actually populated correctly by wrapping the code in a main function, adding some print statements, compiling it and running it.
Before we go into the Emscripten world, let's first test our C++ code. We can check if the iteration property is populated correctly, by extending the command line interface we made in the [previous blog](../run-your-c%2B%2B-code-on-the-web) as follows:

```cpp
#include <iostream>
Expand Down Expand Up @@ -101,13 +101,13 @@ int main() {
```
File: _cli.cpp_.

Compile it with
Compile it with:

```shell
g++ -o cli.exe newtonraphson.cpp cli.cpp
```

Run with
Run with:

```shell
./cli.exe
Expand All @@ -119,14 +119,14 @@ index = 4 x = -1.02 y = -0.28 slope = 14.40 delta_x = -0.02
index = 5 x = -1.00 y = -0.00 slope = 14.01 delta_x = -0.00
```

The last iteration has `-1.00` as x, which is what we expected.
The last iteration has `x = -1.00`, which is what we expected.

## Bindings

Emscripten can handle simple types like float and int, but needs help exposing more complex types to JavaScript like the iterations property.
We need to use [value_object](https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html#value-types) to expose the Iteration struct and [register_vector](https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html#built-in-type-conversions) as the iterations property type.
Emscripten can handle simple types like `float` and `int`, but needs help exposing more complex types to JavaScript like the `iterations` property.
We need to use [`value_object`](https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html#value-types) to expose the `Iteration` `struct` and [`register_vector`](https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html#built-in-type-conversions) as the `iterations` property type.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Links are already rendered differently, they do not need more highlighting

Suggested change
We need to use [`value_object`](https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html#value-types) to expose the `Iteration` `struct` and [`register_vector`](https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html#built-in-type-conversions) as the `iterations` property type.
We need to use [value_object](https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html#value-types) to expose the `Iteration` `struct` and [register_vector](https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html#built-in-type-conversions) as the `iterations` property type.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

strictly speaking I don't care to highlight them, but I want to emphasize that I'm referring to something in the code. It's your call though.


So the bindings look like
So the bindings look like this:

```cpp
#include <emscripten/bind.h>
Expand Down Expand Up @@ -154,41 +154,23 @@ EMSCRIPTEN_BINDINGS(newtonraphson) {
```
File: _bindings.cpp_.

Same as in [previous blog](TODO) we can compile to a WebAssembly module with Emscripten using `emcc` command
We can now compile our C++ code to a WebAssembly module with Emscripten using `emcc` command, exactly [like we did before](../run-your-c%2B%2B-code-on-the-web):

```shell
emcc -I. -o newtonraphson.js -Oz -s MODULARIZE=1 \
-s EXPORT_NAME=createModule --bind newtonraphson.cpp bindings.cpp
```

<!--
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I forgot about that one, nice to see #65

TODO do we want to explain how we can run snippet below?

To run test the WebAssembly module in a web browser we can use it's build-in console

1. startup a web server to host newtonraphson.js and newtonraphson.wasm with `python3 -m http.server 8000`
2. goto webserver adress [http://localhost:8000](http://localhost:8080) in browser
3. open the console in the web browsers DevTools (press F12 to open)

Import the WebAssembly JavaScript binding file with

```javascript
const response = await fetch('http://localhost:8000/newtonraphson.js');
const text = await response.text();
eval(text);
const {NewtonRaphson} = await createModule();
```
-->

To get the iteration data in JavaScript we use the following code

```javascript
const initial_guess = -4;
const tolerance = 0.001;
const newtonraphson = new NewtonRaphson(tolerance);
newtonraphson.solve(initial_guess);
// newtonraphson.iterations is a vector object which not consumeable by Vega
// So convert Emscripten vector of objects to JavaScript array of objects
// newtonraphson.iterations is a vector object, which is not
// consumeable by Vega, so we need to convert Emscripten's
// vector of objects to a JavaScript array of objects first
const iterations = new Array(
newtonraphson.iterations.size()
).fill().map(
Expand All @@ -198,7 +180,7 @@ const iterations = new Array(
);
```

Let's have a look at the data we want to plot, by logging it to the with `console.log(JSON.stringify(iterations, null, 2))` to get
Let's have a look at the data we want to plot, by logging it to the console with `console.log(JSON.stringify(iterations, null, 2))`, which should return the following data:

```json
[
Expand Down Expand Up @@ -259,9 +241,9 @@ So let's plot the iteration index against the y found in each iteration to see h

The Vega-Lite specification is constructed out of the following blocks

* data, the iterations we want to plot as an array of iteration objects
* mark, for line plot use [line](https://vega.github.io/vega-lite/docs/line.html) marker
* encoding, which field should go on which axis
* `"data"`, the iterations we want to plot as an array of iteration objects
* `"encoding"`, which field should go on which axis
* `"mark"`, for line plot use [line](https://vega.github.io/vega-lite/docs/line.html) marker

```js
const spec = {
Expand All @@ -286,7 +268,7 @@ const spec = {
};
```

To render a specification we need to use the `vegaEmbed(element, spec)` method which accepts a HTML element and a Vega-Lite specification.
To render a specification we need to use the `vegaEmbed(element, spec)` method which accepts an HTML element and a Vega-Lite specification.

```html
<html>
Expand All @@ -306,7 +288,7 @@ To render a specification we need to use the `vegaEmbed(element, spec)` method w
</html>
```

The complete HTML pages looks like
The complete HTML page looks like this:

```html
<html>
Expand Down Expand Up @@ -360,15 +342,15 @@ The complete HTML pages looks like
</body>
</html>
```
File: _scatter.html.
File: _scatter.html_.

We'll need a web server to display the HTML page in a web browser. For this, we'll use the http.server module from Python 3 to host all files on port 8000, like so:
We'll need a web server to display the HTML page in a web browser. For this, we'll use the `http.server` module from Python 3 to host all files on port 8000, like so:

```shell
python3 -m http.server 8000
```

When we visit the web page at [http://localhost:8000/scatter.html](http://localhost:8000/scatter.html), we will be greeted by the following plot. We can zoom with mouse wheel and pan by dragging. Also when we hover over a point we get a tooltip with all iteration data.
When we visit the web page at [http://localhost:8000/scatter.html](http://localhost:8000/scatter.html), we will be greeted by the following plot. We can zoom with the mouse wheel and pan by dragging. Hovering over a point shows a tooltip with relevant data at that point.

[![Image](scatter.png)](https://nlesc-jcer.github.io/run-cpp-on-web/js-plot/scatter.html)
(Click on image to get interactive version)
Expand All @@ -377,7 +359,7 @@ When we visit the web page at [http://localhost:8000/scatter.html](http://localh

In the first blog of this series we plotted the equation and root as

![equation.png](https://nlesc-jcer.github.io/run-cpp-on-web/run-your-c++-code-on-the-web/equation.png)
![equation.png](https://nlesc-jcer.github.io/run-cpp-on-web/run-your-c%2B%2B-code-on-the-web/equation.png)

It would be nice to write a specification of this plot together with the iterations the root finding algorithm went through.
Vega-Lite can superimpose one chart on top of another with [layers](https://vega.github.io/vega-lite/docs/layer.html) keyword.
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ using namespace emscripten;

EMSCRIPTEN_BINDINGS(newtonraphson) {
class_<NewtonRaphson>("NewtonRaphson")
.constructor<float>()
.constructor<float>()
.function("solve", &NewtonRaphson::solve)
.property("iterations", &NewtonRaphson::iterations)
;
Expand Down
4 changes: 2 additions & 2 deletions js-plot/cli.cpp → show-me-the-visualization/cli.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
#include "newtonraphson.hpp"

int main() {
double initial_guess = -4;
double tolerance = 0.001;
float initial_guess = -4;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice inconsistency find

float tolerance = 0.001;
NewtonRaphson newtonraphson(tolerance);
newtonraphson.solve(initial_guess);

Expand Down
File renamed without changes
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes
File renamed without changes.
File renamed without changes