Skip to content

Commit d97e2f4

Browse files
author
Shrike
committedJul 30, 2023
adding mandelbrot calculator
1 parent fa67490 commit d97e2f4

File tree

5 files changed

+366
-1
lines changed

5 files changed

+366
-1
lines changed
 

‎README.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ $ cd c-wasm
2020
$ ./build.sh
2121
```
2222

23+
### Browser
24+
2325
Start a local webserver:
2426

2527
```console
@@ -30,7 +32,9 @@ Point your browser to http://localhost:8000/index.html
3032

3133
![Screenshot](c_wasm_screenshot.png)
3234

33-
Using NodeJS: (Tested with v16.20.1)
35+
### NodeJS
36+
37+
Tested with _v16.20.1_
3438

3539
```console
3640
$ node node-app.js

‎main.c

+51
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,54 @@ char* reverse(char* msg, int len)
6161

6262
return result;
6363
}
64+
65+
/*********************************************/
66+
/* Mandelbrot ********************************/
67+
68+
double minR;
69+
double maxR;
70+
double minI;
71+
double maxI;
72+
int maxIterations;
73+
int canvasWidth;
74+
int canvasHeight;
75+
76+
double map(double value, double minin, double maxin, double minout, double maxout)
77+
{
78+
return minout + (value - minin) / (maxin - minin) * (maxout - minout);
79+
}
80+
81+
void initMandel(double minr, double maxr, double mini, double maxi, int maxiterations, int width, int height)
82+
{
83+
minR = minr;
84+
maxR = maxr;
85+
minI = mini;
86+
maxI = maxi;
87+
maxIterations = maxiterations;
88+
canvasWidth = width;
89+
canvasHeight = height;
90+
}
91+
92+
int calcPixel(int x, int y)
93+
{
94+
double x0 = map((double)x, 0.0, (double)canvasWidth - 1, minR, maxR);
95+
double y0 = map((double)y, 0.0, (double)canvasHeight - 1, minI, maxI);
96+
97+
double a = 0;
98+
double b = 0;
99+
double rx = 0;
100+
double ry = 0;
101+
102+
int iterations = 0;
103+
while (iterations < maxIterations && (rx * rx + ry * ry <= 4.0))
104+
{
105+
rx = a * a - b * b + x0;
106+
ry = 2 * a * b + y0;
107+
108+
a = rx;
109+
b = ry;
110+
iterations++;
111+
}
112+
113+
return iterations;
114+
}

‎mandel-app.js

+233
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
let w = null;
2+
let memory = null;
3+
4+
function make_environment(...envs) {
5+
return new Proxy(envs, {
6+
get(target, prop, receiver) {
7+
for (let env of envs) {
8+
if (env.hasOwnProperty(prop)) {
9+
return env[prop];
10+
}
11+
}
12+
13+
return (...args) => { console.error("NOT IMPLEMENTED: " + prop, args); }
14+
}
15+
});
16+
}
17+
18+
const imports = {
19+
"sqrt": Math.sqrt,
20+
"print": print,
21+
};
22+
23+
function print(value)
24+
{
25+
//const s = extract_string(value);
26+
//console.log("print:", s);
27+
console.log("Print:");
28+
}
29+
30+
function run_wasm(obj) {
31+
w = obj.instance;
32+
memory = new Uint8Array(w.exports.memory.buffer);
33+
34+
let m = new Mandel(w);
35+
}
36+
37+
async function load_wasm() {
38+
fetch("main.wasm").then(response =>
39+
response.arrayBuffer()
40+
).then(bytes =>
41+
WebAssembly.instantiate(bytes, { "env": make_environment(imports) })
42+
).then(run_wasm);
43+
}
44+
45+
function init() {
46+
load_wasm();
47+
48+
49+
}
50+
51+
class Mandel
52+
{
53+
constructor(w) {
54+
this.buttonStartC = document.getElementById("btnStartC");
55+
this.buttonStartJS = document.getElementById("btnStartJS");
56+
this.buttonStopC = document.getElementById("btnStopC");
57+
this.buttonStopJS = document.getElementById("btnStopJS");
58+
59+
this.canvasJS = document.getElementById("canvasJS");
60+
this.canvasC = document.getElementById("canvasC");
61+
this.w = w;
62+
63+
this.ctxC = this.canvasC.getContext("2d");
64+
this.ctxJS = this.canvasJS.getContext("2d");
65+
66+
this.canvasWidth = this.canvasC.width;
67+
this.canvasHeight = this.canvasC.height;
68+
69+
this.boundHandleClickStartC = this.startC.bind(this);
70+
this.boundHandleClickStartJS = this.startJS.bind(this);
71+
this.boundHandleClickStopC = this.stopC.bind(this);
72+
this.boundHandleClickStopJS = this.stopJS.bind(this);
73+
74+
this.boundLoopJS = this.loopJS.bind(this);
75+
this.boundLoopC = this.loopC.bind(this);
76+
77+
this.buttonStartC.addEventListener("click", this.boundHandleClickStartC);
78+
this.buttonStopC.addEventListener("click", this.boundHandleClickStopC);
79+
this.buttonStartJS.addEventListener("click", this.boundHandleClickStartJS);
80+
this.buttonStopJS.addEventListener("click", this.boundHandleClickStopJS);
81+
82+
this.buttonStopC.style.display = "none";
83+
this.buttonStopJS.style.display = "none";
84+
85+
this.runJS = false;
86+
this.runC = false;
87+
}
88+
89+
startC()
90+
{
91+
this.buttonStartC.style.display = "none";
92+
this.buttonStopC.style.display = "block";
93+
this.buttonStartJS.style.display = "none";
94+
this.initMandel();
95+
96+
this.ctxC.fillStyle = "rgba(10,10,10,255)";
97+
this.ctxC.fillRect( 0, 0, this.canvasWidth, this.canvasHeight );
98+
99+
this.runC = true;
100+
requestAnimationFrame(this.boundLoopC);
101+
}
102+
103+
stopC()
104+
{
105+
this.buttonStartC.style.display = "block";
106+
this.buttonStopC.style.display = "none";
107+
this.buttonStartJS.style.display = "block";
108+
this.runC = false;
109+
}
110+
111+
startJS()
112+
{
113+
this.buttonStartJS.style.display = "none";
114+
this.buttonStopJS.style.display = "block";
115+
this.buttonStartC.style.display = "none";
116+
this.initMandel();
117+
118+
this.ctxJS.fillStyle = "rgba(10,10,10,255)";
119+
this.ctxJS.fillRect( 0, 0, this.canvasWidth, this.canvasHeight );
120+
121+
this.runJS = true;
122+
requestAnimationFrame(this.boundLoopJS);
123+
}
124+
125+
stopJS()
126+
{
127+
this.buttonStartJS.style.display = "block";
128+
this.buttonStopJS.style.display = "none";
129+
this.buttonStartC.style.display = "block";
130+
this.runJS = false;
131+
}
132+
133+
initMandel()
134+
{
135+
this.minR = -1.016104875794718741911;
136+
this.maxR = -1.016069839835083325286;
137+
this.minI = 0.277341371563625000140;
138+
this.maxI = 0.277367718605270833442;
139+
140+
// this.minR = -1.016097461503191826601;
141+
// this.maxR = -1.016097461213187951076;
142+
// this.minI = 0.277356844127615701971;
143+
// this.maxI = 0.277356844344781394806;
144+
145+
this.minR = -2;
146+
this.maxR = 1;
147+
this.minI = -1;
148+
this.maxI = 1;
149+
150+
this.minR = -1.293019999999999985104;
151+
this.maxR = -1.193859999999999987472;
152+
this.minI = 0.12808;
153+
this.maxI = 0.20245;
154+
155+
this.maxIterations = 5000;
156+
this.x = 0;
157+
this.y = 0;
158+
159+
this.colors = new Array(16).fill(0).map((_, i) => i === 0 ? '#000' : `#${((1 << 24) * Math.random() | 0).toString(16)}`);
160+
161+
this.w.exports.initMandel(this.minR, this.maxR, this.minI, this.maxI, this.maxIterations, this.canvasWidth, this.canvasHeight);
162+
}
163+
164+
loopJS()
165+
{
166+
for (let x = 0; x < this.canvasWidth; ++x)
167+
{
168+
const iterations = this.calcPixel(x, this.y);
169+
this.ctxJS.fillStyle = this.colors[iterations >= this.maxIterations ? 0 : (iterations % this.colors.length - 1) + 1];
170+
this.ctxJS.fillRect( x, this.y, 1, 1 );
171+
}
172+
173+
this.y += 1;
174+
if (this.y >= this.canvasHeight)
175+
{
176+
this.stopJS();
177+
}
178+
179+
if (this.runJS)
180+
{
181+
requestAnimationFrame(this.boundLoopJS);
182+
}
183+
}
184+
185+
loopC()
186+
{
187+
for (let x = 0; x < this.canvasWidth; ++x)
188+
{
189+
const iterations = this.w.exports.calcPixel(x, this.y);
190+
this.ctxC.fillStyle = this.colors[iterations >= this.maxIterations ? 0 : (iterations % this.colors.length - 1) + 1];
191+
this.ctxC.fillRect( x, this.y, 1, 1 );
192+
}
193+
194+
this.y += 1;
195+
if (this.y >= this.canvasHeight)
196+
{
197+
this.stopC();
198+
}
199+
200+
if (this.runC)
201+
{
202+
requestAnimationFrame(this.boundLoopC);
203+
}
204+
}
205+
206+
map(value, minin, maxin, minout, maxout)
207+
{
208+
return minout + (value - minin) / (maxin - minin) * (maxout - minout);
209+
}
210+
211+
calcPixel(x, y)
212+
{
213+
const x0 = this.map(x, 0, this.canvasWidth - 1, this.minR, this.maxR);
214+
const y0 = this.map(y, 0, this.canvasHeight - 1, this.minI, this.maxI);
215+
216+
let a = 0;
217+
let b = 0;
218+
let rx = 0;
219+
let ry = 0;
220+
221+
let iterations = 0;
222+
while (iterations < this.maxIterations && (rx * rx + ry * ry <= 4)) {
223+
rx = a * a - b * b + x0;
224+
ry = 2 * a * b + y0;
225+
226+
a = rx;
227+
b = ry;
228+
iterations++;
229+
}
230+
231+
return iterations;
232+
}
233+
}

‎mandel.html

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width">
6+
<meta name="description" content="Experiment with C to WebAssembly">
7+
<meta name="color-scheme" content="dark light" />
8+
9+
<title>C to WebAssembly</title>
10+
11+
<link rel="stylesheet" href="style.css" />
12+
<script src="mandel-app.js" type="application/javascript"></script>
13+
</head>
14+
<body>
15+
16+
<h1>Running C functions in WebAssembly</h1>
17+
<hr/>
18+
<div class="wrapper">
19+
<div class="cnvJS">
20+
<h2>Pure Javascript</h2>
21+
<div style="margin:0; padding:0;"><canvas id="canvasJS" width="320" height="240"></canvas></div>
22+
<button type="button" id="btnStartJS">Start</button>
23+
<button type="button" id="btnStopJS" style="display:none;">Stop</button>
24+
</div>
25+
<div class="cnvC">
26+
<h2>C-WebAssembly</h2>
27+
<div style="margin:0; padding:0;"><canvas id="canvasC" width="320" height="240"></canvas></div>
28+
<button type="button" id="btnStartC">Start</button>
29+
<button type="button" id="btnStopC" style="display:none;">Stop</button>
30+
</div>
31+
</div>
32+
33+
<script type="module">
34+
init();
35+
</script>
36+
37+
</body>
38+
</html>

‎style.css

+39
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,45 @@ h2 {
1212
color: #909020;
1313
}
1414

15+
button {
16+
margin: 10px;
17+
padding: 10px;
18+
}
19+
20+
canvas {
21+
padding: 0;
22+
margin: 0;
23+
}
24+
1525
.result {
1626
color: blue;
1727
}
28+
29+
.wrapper {
30+
margin-top: 40px;
31+
display: flex;
32+
flex-direction: row;
33+
justify-content: flex-start;
34+
gap: 20px;
35+
}
36+
37+
.cnvJS {
38+
//border: 1px solid #444444;
39+
background-color: #111111;
40+
padding: 20px;
41+
}
42+
43+
.cnvJS h2 {
44+
background-color: #111111;
45+
}
46+
47+
.cnvC {
48+
//border-right: 1px solid #444444;
49+
//border-bottom: 1px solid #444444;
50+
background-color: #111111;
51+
padding: 20px;
52+
}
53+
54+
.cnvC h2 {
55+
background-color: #111111;
56+
}

0 commit comments

Comments
 (0)