wasm凸包测试
This commit is contained in:
parent
e811c04eeb
commit
75c3e31bc4
@ -1,42 +1,76 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
// 定义一个点结构体
|
// 定义点结构
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int x, y;
|
int x;
|
||||||
|
int y;
|
||||||
} Point;
|
} Point;
|
||||||
|
|
||||||
// 计算两点的极角
|
// 计算叉积
|
||||||
int orientation(Point p, Point q, Point r) {
|
double crossProduct(Point p0, Point p1, Point p2) {
|
||||||
int val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
|
return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);
|
||||||
if (val == 0) return 0; // 共线
|
|
||||||
return (val > 0) ? 1 : 2; // 顺时针或逆时针
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 对点的极角排序
|
// 计算两点间距离的平方
|
||||||
int compare(const void *vp1, const void *vp2) {
|
double distanceSquare(Point p1, Point p2) {
|
||||||
Point *p1 = (Point *)vp1;
|
return (p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y);
|
||||||
Point *p2 = (Point *)vp2;
|
|
||||||
return (p1->y < p2->y) || (p1->y == p2->y && p1->x < p2->x);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 凸包主函数
|
// 比较函数
|
||||||
void convexHull(Point points[], int n, Point result[], int *result_size) {
|
Point p0;
|
||||||
// 如果点数小于 3,无法构成凸包
|
int comparePoints(const void* vp1, const void* vp2) {
|
||||||
|
Point* p1 = (Point*)vp1;
|
||||||
|
Point* p2 = (Point*)vp2;
|
||||||
|
double cross = crossProduct(p0, *p1, *p2);
|
||||||
|
if (cross == 0) {
|
||||||
|
return (distanceSquare(p0, *p1) - distanceSquare(p0, *p2));
|
||||||
|
}
|
||||||
|
return (cross < 0) ? 1 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出的凸包计算函数
|
||||||
|
void convexHull(Point* points, int n, Point* result, int* resultSize) {
|
||||||
if (n < 3) {
|
if (n < 3) {
|
||||||
*result_size = 0;
|
// 点数小于3,直接复制所有点
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
result[i] = points[i];
|
||||||
|
}
|
||||||
|
*resultSize = n;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 按 y 坐标排序,如果 y 坐标相同则按 x 坐标排序
|
// 找到y坐标最小的点
|
||||||
qsort(points, n, sizeof(Point), compare);
|
int min = 0;
|
||||||
|
for (int i = 1; i < n; i++) {
|
||||||
|
if (points[i].y < points[min].y ||
|
||||||
|
(points[i].y == points[min].y && points[i].x < points[min].x)) {
|
||||||
|
min = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将最下方的点交换到points[0]
|
||||||
|
Point temp = points[0];
|
||||||
|
points[0] = points[min];
|
||||||
|
points[min] = temp;
|
||||||
|
|
||||||
|
// 保存最下方的点用于排序
|
||||||
|
p0 = points[0];
|
||||||
|
|
||||||
|
// 按极角排序
|
||||||
|
qsort(&points[1], n-1, sizeof(Point), comparePoints);
|
||||||
|
|
||||||
// 构建凸包
|
// 构建凸包
|
||||||
int m = 0; // 凸包的点数
|
int m = 0;
|
||||||
for (int i = 0; i < n; i++) {
|
result[m++] = points[0];
|
||||||
while (m >= 2 && orientation(result[m-2], result[m-1], points[i]) != 2)
|
result[m++] = points[1];
|
||||||
|
result[m++] = points[2];
|
||||||
|
|
||||||
|
for (int i = 3; i < n; i++) {
|
||||||
|
while (m > 1 && crossProduct(result[m-2], result[m-1], points[i]) <= 0) {
|
||||||
m--;
|
m--;
|
||||||
|
}
|
||||||
result[m++] = points[i];
|
result[m++] = points[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
*result_size = m;
|
*resultSize = m;
|
||||||
}
|
}
|
227
convex_hull.html
Normal file
227
convex_hull.html
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>凸包算法</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<textarea style="width: 100%;height: 100px;" id="points" placeholder="输入点,格式:x1,y1;x2,y2;x3,y3;..."></textarea>
|
||||||
|
<button onclick="convex_hull()">计算凸包</button>
|
||||||
|
<button onclick="randomPointsToTextarea(30)">随机30点</button>
|
||||||
|
<button onclick="randomPointsToTextarea(3000)">随机3000点</button>
|
||||||
|
<button onclick="randomPointsToTextarea(10000)">随机10000点</button>
|
||||||
|
<button onclick="randomPointsToTextarea(30000)">随机30000点</button>
|
||||||
|
<div id="result"></div>
|
||||||
|
<canvas id="canvas" width="800" height="800"></canvas>
|
||||||
|
</body>
|
||||||
|
<script src="./convex_hull.js"></script>
|
||||||
|
<script>
|
||||||
|
function callWasmConvexHull(points) {
|
||||||
|
//长度
|
||||||
|
var n = points.length;
|
||||||
|
var pointsPtr = Module._malloc(n * 8); //8字节一个点
|
||||||
|
|
||||||
|
for (let i = 0; i < n; i++) {
|
||||||
|
Module.setValue(pointsPtr + i * 8, points[i].x, 'i32');
|
||||||
|
Module.setValue(pointsPtr + i * 8 + 4, points[i].y, 'i32');
|
||||||
|
}
|
||||||
|
//多少点
|
||||||
|
var resultPtr = Module._malloc(n * 8);
|
||||||
|
var resultSizePtr = Module._malloc(4);
|
||||||
|
console.time('wasm耗时')
|
||||||
|
Module._convexHull(pointsPtr, n, resultPtr, resultSizePtr);
|
||||||
|
console.timeEnd('wasm耗时')
|
||||||
|
var resultSize = Module.getValue(resultSizePtr, 'i32');
|
||||||
|
var resultPoints = [];
|
||||||
|
//读
|
||||||
|
for (let i = 0; i < resultSize; i++) {
|
||||||
|
var x = Module.getValue(resultPtr + i * 8, 'i32');
|
||||||
|
var y = Module.getValue(resultPtr + i * 8 + 4, 'i32');
|
||||||
|
resultPoints.push({ x: x, y: y });
|
||||||
|
}
|
||||||
|
// 释放
|
||||||
|
Module._free(pointsPtr);
|
||||||
|
Module._free(resultPtr);
|
||||||
|
Module._free(resultSizePtr);
|
||||||
|
return resultPoints
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function randomPointsToTextarea(n) {
|
||||||
|
var points = Array.from({ length: n }, () => ({
|
||||||
|
x: Math.floor(Math.random() * 100),
|
||||||
|
y: Math.floor(Math.random() * 100)
|
||||||
|
}));
|
||||||
|
document.getElementById('points').value = points.map(p => `${p.x},${p.y}`).join(';');
|
||||||
|
draw(points)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//计算凸包
|
||||||
|
function convex_hull(points) {
|
||||||
|
var points = document.getElementById('points').value.split(';').map(item => {
|
||||||
|
var [x, y] = item.split(',');
|
||||||
|
return { x: parseInt(x), y: parseInt(y) };
|
||||||
|
});
|
||||||
|
console.log('--------------------------------')
|
||||||
|
//wasm整体调用计时
|
||||||
|
console.time('wasm整体耗时,包含指针传递')
|
||||||
|
var resultPoints = callWasmConvexHull(points)
|
||||||
|
console.timeEnd('wasm整体耗时,包含指针传递')
|
||||||
|
document.getElementById('result').innerHTML = JSON.stringify(resultPoints)
|
||||||
|
drawConvexHull(resultPoints)
|
||||||
|
|
||||||
|
//js实现凸包
|
||||||
|
console.time('js调用')
|
||||||
|
var resultPoints = jsConvexHull(points)
|
||||||
|
console.timeEnd('js调用')
|
||||||
|
drawConvexHull(resultPoints)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let padding = 100; // 增加内边距,让点不贴边
|
||||||
|
let minX = 0;
|
||||||
|
let minY = 0;
|
||||||
|
let maxX = 0;
|
||||||
|
let maxY = 0;
|
||||||
|
let scale = 1;
|
||||||
|
// 转换坐标
|
||||||
|
function transformPoint(p, canvas) {
|
||||||
|
return {
|
||||||
|
x: padding + (p.x - minX) * scale,
|
||||||
|
y: canvas.height - padding - (p.y - minY) * scale
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* canvas画点
|
||||||
|
*/
|
||||||
|
function draw(points) {
|
||||||
|
var canvas = document.getElementById('canvas');
|
||||||
|
var ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
// 清空画布
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
// 找出所有点的最大最小值
|
||||||
|
minX = Math.min(...points.map(p => p.x));
|
||||||
|
maxX = Math.max(...points.map(p => p.x));
|
||||||
|
minY = Math.min(...points.map(p => p.y));
|
||||||
|
maxY = Math.max(...points.map(p => p.y));
|
||||||
|
|
||||||
|
// 计算缩放比例
|
||||||
|
var scaleX = (canvas.width - 2 * padding) / (maxX - minX || 1);
|
||||||
|
var scaleY = (canvas.height - 2 * padding) / (maxY - minY || 1);
|
||||||
|
scale = Math.min(scaleX, scaleY);
|
||||||
|
|
||||||
|
// 绘制点
|
||||||
|
var transformedPoints = points.map(p => transformPoint(p, canvas));
|
||||||
|
ctx.fillStyle = 'blue';
|
||||||
|
|
||||||
|
transformedPoints.forEach(point => {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(point.x, point.y, 4, 0, Math.PI * 2); // 绘制半径为4的圆点
|
||||||
|
ctx.fill();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 画凸包
|
||||||
|
*/
|
||||||
|
function drawConvexHull(points) {
|
||||||
|
// 使用相同的绘制函数,但使用不同的颜色
|
||||||
|
var canvas = document.getElementById('canvas');
|
||||||
|
var ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
// 绘制凸包
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.strokeStyle = 'red';
|
||||||
|
|
||||||
|
|
||||||
|
var transformedPoints = points.map(p => transformPoint(p, canvas));
|
||||||
|
|
||||||
|
ctx.moveTo(transformedPoints[0].x, transformedPoints[0].y);
|
||||||
|
for (let i = 1; i < transformedPoints.length; i++) {
|
||||||
|
ctx.lineTo(transformedPoints[i].x, transformedPoints[i].y);
|
||||||
|
}
|
||||||
|
// 闭合凸包
|
||||||
|
ctx.lineTo(transformedPoints[0].x, transformedPoints[0].y);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//loadwasm
|
||||||
|
function loadwasm() {
|
||||||
|
var wasm_url = "./convex_hull.js";
|
||||||
|
var script = document.createElement('script');
|
||||||
|
script.src = wasm_url;
|
||||||
|
script.onload = function () {
|
||||||
|
//loadwasm
|
||||||
|
console.log(Module)
|
||||||
|
Module.onRuntimeInitialized = function () {
|
||||||
|
// convex_hull()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.body.appendChild(script);
|
||||||
|
}
|
||||||
|
loadwasm()
|
||||||
|
randomPointsToTextarea(30)
|
||||||
|
|
||||||
|
|
||||||
|
//js实现凸包 不使用wasm
|
||||||
|
function jsConvexHull(points) {
|
||||||
|
// 如果点少于3个,直接返回原始点集
|
||||||
|
if (points.length < 3) return points;
|
||||||
|
|
||||||
|
// 找到最低且最左的点作为起始点
|
||||||
|
let start = points.reduce((lowest, point) => {
|
||||||
|
if (point.y < lowest.y || (point.y === lowest.y && point.x < lowest.x)) {
|
||||||
|
return point;
|
||||||
|
}
|
||||||
|
return lowest;
|
||||||
|
}, points[0]);
|
||||||
|
|
||||||
|
// 计算其他点相对于起始点的极角,并排序
|
||||||
|
let sorted = points
|
||||||
|
.filter(point => point !== start)
|
||||||
|
.map(point => ({
|
||||||
|
point: point,
|
||||||
|
angle: Math.atan2(point.y - start.y, point.x - start.x),
|
||||||
|
distance: Math.sqrt(Math.pow(point.x - start.x, 2) + Math.pow(point.y - start.y, 2))
|
||||||
|
}))
|
||||||
|
.sort((a, b) => {
|
||||||
|
if (a.angle === b.angle) {
|
||||||
|
return a.distance - b.distance;
|
||||||
|
}
|
||||||
|
return a.angle - b.angle;
|
||||||
|
})
|
||||||
|
.map(item => item.point);
|
||||||
|
|
||||||
|
// 将起始点加入结果数组
|
||||||
|
sorted.unshift(start);
|
||||||
|
|
||||||
|
// Graham扫描
|
||||||
|
let stack = [sorted[0], sorted[1]];
|
||||||
|
for (let i = 2; i < sorted.length; i++) {
|
||||||
|
while (stack.length >= 2 && !isCounterClockwise(
|
||||||
|
stack[stack.length - 2],
|
||||||
|
stack[stack.length - 1],
|
||||||
|
sorted[i]
|
||||||
|
)) {
|
||||||
|
stack.pop();
|
||||||
|
}
|
||||||
|
stack.push(sorted[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断三个点是否形成逆时针方向
|
||||||
|
function isCounterClockwise(p1, p2, p3) {
|
||||||
|
return (p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x) > 0;
|
||||||
|
}
|
||||||
|
</script>
|
2615
convex_hull.js
2615
convex_hull.js
File diff suppressed because one or more lines are too long
BIN
convex_hull.wasm
BIN
convex_hull.wasm
Binary file not shown.
@ -1,196 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright 2015 The Emscripten Authors
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Pthread Web Worker startup routine:
|
|
||||||
// This is the entry point file that is loaded first by each Web Worker
|
|
||||||
// that executes pthreads on the Emscripten application.
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var Module = {};
|
|
||||||
|
|
||||||
// Node.js support
|
|
||||||
var ENVIRONMENT_IS_NODE = typeof process == 'object' && typeof process.versions == 'object' && typeof process.versions.node == 'string';
|
|
||||||
if (ENVIRONMENT_IS_NODE) {
|
|
||||||
// Create as web-worker-like an environment as we can.
|
|
||||||
|
|
||||||
// See the parallel code in shell.js, but here we don't need the condition on
|
|
||||||
// multi-environment builds, as we do not have the need to interact with the
|
|
||||||
// modularization logic as shell.js must (see link.py:node_es6_imports and
|
|
||||||
// how that is used in link.py).
|
|
||||||
|
|
||||||
var nodeWorkerThreads = require('worker_threads');
|
|
||||||
|
|
||||||
var parentPort = nodeWorkerThreads.parentPort;
|
|
||||||
|
|
||||||
parentPort.on('message', (data) => onmessage({ data: data }));
|
|
||||||
|
|
||||||
var fs = require('fs');
|
|
||||||
var vm = require('vm');
|
|
||||||
|
|
||||||
Object.assign(global, {
|
|
||||||
self: global,
|
|
||||||
require,
|
|
||||||
Module,
|
|
||||||
location: {
|
|
||||||
// __filename is undefined in ES6 modules, and import.meta.url only in ES6
|
|
||||||
// modules.
|
|
||||||
href: __filename
|
|
||||||
},
|
|
||||||
Worker: nodeWorkerThreads.Worker,
|
|
||||||
importScripts: (f) => vm.runInThisContext(fs.readFileSync(f, 'utf8'), {filename: f}),
|
|
||||||
postMessage: (msg) => parentPort.postMessage(msg),
|
|
||||||
performance: global.performance || { now: Date.now },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Thread-local guard variable for one-time init of the JS state
|
|
||||||
var initializedJS = false;
|
|
||||||
|
|
||||||
function assert(condition, text) {
|
|
||||||
if (!condition) abort('Assertion failed: ' + text);
|
|
||||||
}
|
|
||||||
|
|
||||||
function threadPrintErr(...args) {
|
|
||||||
var text = args.join(' ');
|
|
||||||
// See https://github.com/emscripten-core/emscripten/issues/14804
|
|
||||||
if (ENVIRONMENT_IS_NODE) {
|
|
||||||
fs.writeSync(2, text + '\n');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.error(text);
|
|
||||||
}
|
|
||||||
function threadAlert(...args) {
|
|
||||||
var text = args.join(' ');
|
|
||||||
postMessage({cmd: 'alert', text, threadId: Module['_pthread_self']()});
|
|
||||||
}
|
|
||||||
// We don't need out() for now, but may need to add it if we want to use it
|
|
||||||
// here. Or, if this code all moves into the main JS, that problem will go
|
|
||||||
// away. (For now, adding it here increases code size for no benefit.)
|
|
||||||
var out = () => { throw 'out() is not defined in worker.js.'; }
|
|
||||||
var err = threadPrintErr;
|
|
||||||
self.alert = threadAlert;
|
|
||||||
var dbg = threadPrintErr;
|
|
||||||
|
|
||||||
Module['instantiateWasm'] = (info, receiveInstance) => {
|
|
||||||
// Instantiate from the module posted from the main thread.
|
|
||||||
// We can just use sync instantiation in the worker.
|
|
||||||
var module = Module['wasmModule'];
|
|
||||||
// We don't need the module anymore; new threads will be spawned from the main thread.
|
|
||||||
Module['wasmModule'] = null;
|
|
||||||
var instance = new WebAssembly.Instance(module, info);
|
|
||||||
// TODO: Due to Closure regression https://github.com/google/closure-compiler/issues/3193,
|
|
||||||
// the above line no longer optimizes out down to the following line.
|
|
||||||
// When the regression is fixed, we can remove this if/else.
|
|
||||||
return receiveInstance(instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Turn unhandled rejected promises into errors so that the main thread will be
|
|
||||||
// notified about them.
|
|
||||||
self.onunhandledrejection = (e) => {
|
|
||||||
throw e.reason || e;
|
|
||||||
};
|
|
||||||
|
|
||||||
function handleMessage(e) {
|
|
||||||
try {
|
|
||||||
if (e.data.cmd === 'load') { // Preload command that is called once per worker to parse and load the Emscripten code.
|
|
||||||
|
|
||||||
// Until we initialize the runtime, queue up any further incoming messages.
|
|
||||||
let messageQueue = [];
|
|
||||||
self.onmessage = (e) => messageQueue.push(e);
|
|
||||||
|
|
||||||
// And add a callback for when the runtime is initialized.
|
|
||||||
self.startWorker = (instance) => {
|
|
||||||
// Notify the main thread that this thread has loaded.
|
|
||||||
postMessage({ 'cmd': 'loaded' });
|
|
||||||
// Process any messages that were queued before the thread was ready.
|
|
||||||
for (let msg of messageQueue) {
|
|
||||||
handleMessage(msg);
|
|
||||||
}
|
|
||||||
// Restore the real message handler.
|
|
||||||
self.onmessage = handleMessage;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Module and memory were sent from main thread
|
|
||||||
Module['wasmModule'] = e.data.wasmModule;
|
|
||||||
|
|
||||||
// Use `const` here to ensure that the variable is scoped only to
|
|
||||||
// that iteration, allowing safe reference from a closure.
|
|
||||||
for (const handler of e.data.handlers) {
|
|
||||||
Module[handler] = (...args) => {
|
|
||||||
postMessage({ cmd: 'callHandler', handler, args: args });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Module['wasmMemory'] = e.data.wasmMemory;
|
|
||||||
|
|
||||||
Module['buffer'] = Module['wasmMemory'].buffer;
|
|
||||||
|
|
||||||
Module['workerID'] = e.data.workerID;
|
|
||||||
|
|
||||||
Module['ENVIRONMENT_IS_PTHREAD'] = true;
|
|
||||||
|
|
||||||
if (typeof e.data.urlOrBlob == 'string') {
|
|
||||||
importScripts(e.data.urlOrBlob);
|
|
||||||
} else {
|
|
||||||
var objectUrl = URL.createObjectURL(e.data.urlOrBlob);
|
|
||||||
importScripts(objectUrl);
|
|
||||||
URL.revokeObjectURL(objectUrl);
|
|
||||||
}
|
|
||||||
} else if (e.data.cmd === 'run') {
|
|
||||||
// Pass the thread address to wasm to store it for fast access.
|
|
||||||
Module['__emscripten_thread_init'](e.data.pthread_ptr, /*is_main=*/0, /*is_runtime=*/0, /*can_block=*/1);
|
|
||||||
|
|
||||||
// Await mailbox notifications with `Atomics.waitAsync` so we can start
|
|
||||||
// using the fast `Atomics.notify` notification path.
|
|
||||||
Module['__emscripten_thread_mailbox_await'](e.data.pthread_ptr);
|
|
||||||
|
|
||||||
assert(e.data.pthread_ptr);
|
|
||||||
// Also call inside JS module to set up the stack frame for this pthread in JS module scope
|
|
||||||
Module['establishStackSpace']();
|
|
||||||
Module['PThread'].receiveObjectTransfer(e.data);
|
|
||||||
Module['PThread'].threadInitTLS();
|
|
||||||
|
|
||||||
if (!initializedJS) {
|
|
||||||
initializedJS = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
Module['invokeEntryPoint'](e.data.start_routine, e.data.arg);
|
|
||||||
} catch(ex) {
|
|
||||||
if (ex != 'unwind') {
|
|
||||||
// The pthread "crashed". Do not call `_emscripten_thread_exit` (which
|
|
||||||
// would make this thread joinable). Instead, re-throw the exception
|
|
||||||
// and let the top level handler propagate it back to the main thread.
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (e.data.cmd === 'cancel') { // Main thread is asking for a pthread_cancel() on this thread.
|
|
||||||
if (Module['_pthread_self']()) {
|
|
||||||
Module['__emscripten_thread_exit'](-1);
|
|
||||||
}
|
|
||||||
} else if (e.data.target === 'setimmediate') {
|
|
||||||
// no-op
|
|
||||||
} else if (e.data.cmd === 'checkMailbox') {
|
|
||||||
if (initializedJS) {
|
|
||||||
Module['checkMailbox']();
|
|
||||||
}
|
|
||||||
} else if (e.data.cmd) {
|
|
||||||
// The received message looks like something that should be handled by this message
|
|
||||||
// handler, (since there is a e.data.cmd field present), but is not one of the
|
|
||||||
// recognized commands:
|
|
||||||
err(`worker.js received unknown command ${e.data.cmd}`);
|
|
||||||
err(e.data);
|
|
||||||
}
|
|
||||||
} catch(ex) {
|
|
||||||
err(`worker.js onmessage() captured an uncaught exception: ${ex}`);
|
|
||||||
if (ex?.stack) err(ex.stack);
|
|
||||||
Module['__emscripten_thread_crashed']?.();
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
self.onmessage = handleMessage;
|
|
33
readme.md
33
readme.md
@ -1 +1,32 @@
|
|||||||
凸包算法的wasm实现
|
# 凸包算法的wasm实现
|
||||||
|
[在线测试地址](https://kuraa.cc/upload/convex_hull.html)
|
||||||
|
```js
|
||||||
|
function callWasmConvexHull(points) {
|
||||||
|
//长度
|
||||||
|
var n = points.length;
|
||||||
|
var pointsPtr = Module._malloc(n * 8); //8字节一个点
|
||||||
|
|
||||||
|
for (let i = 0; i < n; i++) {
|
||||||
|
Module.setValue(pointsPtr + i * 8, points[i].x, 'i32');
|
||||||
|
Module.setValue(pointsPtr + i * 8 + 4, points[i].y, 'i32');
|
||||||
|
}
|
||||||
|
//多少点
|
||||||
|
var resultPtr = Module._malloc(n * 8);
|
||||||
|
var resultSizePtr = Module._malloc(4);
|
||||||
|
//调用wasm
|
||||||
|
Module._convexHull(pointsPtr, n, resultPtr, resultSizePtr);
|
||||||
|
var resultSize = Module.getValue(resultSizePtr, 'i32');
|
||||||
|
var resultPoints = [];
|
||||||
|
//读
|
||||||
|
for (let i = 0; i < resultSize; i++) {
|
||||||
|
var x = Module.getValue(resultPtr + i * 8, 'i32');
|
||||||
|
var y = Module.getValue(resultPtr + i * 8 + 4, 'i32');
|
||||||
|
resultPoints.push({ x: x, y: y });
|
||||||
|
}
|
||||||
|
// 释放
|
||||||
|
Module._free(pointsPtr);
|
||||||
|
Module._free(resultPtr);
|
||||||
|
Module._free(resultSizePtr);
|
||||||
|
return resultPoints
|
||||||
|
}
|
||||||
|
```
|
54
test.html
54
test.html
@ -1,54 +0,0 @@
|
|||||||
<body></body>
|
|
||||||
<script>
|
|
||||||
//计算凸包
|
|
||||||
function convex_hull(points) {
|
|
||||||
var points = [
|
|
||||||
{ x: 0, y: 0 }, { x: 1, y: 1 }, { x: 2, y: 2 },
|
|
||||||
{ x: 0, y: 3 }, { x: 3, y: 0 }, { x: 3, y: 3 },
|
|
||||||
{ x: 2, y: 1 }, { x: 1, y: 2 }
|
|
||||||
];
|
|
||||||
//长度
|
|
||||||
var n = points.length;
|
|
||||||
var pointsPtr = Module._malloc(n * 8); //8字节一个点
|
|
||||||
|
|
||||||
for (let i = 0; i < n; i++) {
|
|
||||||
Module.setValue(pointsPtr + i * 8, points[i].x, 'i32');
|
|
||||||
Module.setValue(pointsPtr + i * 8 + 4, points[i].y, 'i32');
|
|
||||||
}
|
|
||||||
//多少点
|
|
||||||
var resultPtr = Module._malloc(n * 8);
|
|
||||||
var resultSizePtr = Module._malloc(4);
|
|
||||||
Module._convexHull(pointsPtr, n, resultPtr, resultSizePtr);
|
|
||||||
var resultSize = Module.getValue(resultSizePtr, 'i32');
|
|
||||||
var resultPoints = [];
|
|
||||||
//读
|
|
||||||
for (let i = 0; i < resultSize; i++) {
|
|
||||||
var x = Module.getValue(resultPtr + i * 8, 'i32');
|
|
||||||
var y = Module.getValue(resultPtr + i * 8 + 4, 'i32');
|
|
||||||
resultPoints.push({ x: x, y: y });
|
|
||||||
}
|
|
||||||
console.log(resultPoints);
|
|
||||||
|
|
||||||
// 释放
|
|
||||||
Module._free(pointsPtr);
|
|
||||||
Module._free(resultPtr);
|
|
||||||
Module._free(resultSizePtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//loadwasm
|
|
||||||
function loadwasm() {
|
|
||||||
var wasm_url = "./convex_hull.js";
|
|
||||||
var script = document.createElement('script');
|
|
||||||
script.src = wasm_url;
|
|
||||||
script.onload = function () {
|
|
||||||
//loadwasm
|
|
||||||
console.log(Module)
|
|
||||||
Module.onRuntimeInitialized = function () {
|
|
||||||
convex_hull()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
document.body.appendChild(script);
|
|
||||||
}
|
|
||||||
loadwasm()
|
|
||||||
</script>
|
|
3
生成
3
生成
@ -1,3 +1,4 @@
|
|||||||
docker run --rm -v $(pwd):/src emscripten/emsdk emcc convex_hull.c -o convex_hull.js -s IMPORTED_MEMORY=1 -s INITIAL_MEMORY=104857600 -s EXPORTED_FUNCTIONS="['_convexHull','_malloc','_free','setValue','getValue']" -s EXTRA_EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]'
|
docker run --rm -v $(pwd):/src emscripten/emsdk emcc convex_hull.c -o convex_hull.js -s IMPORTED_MEMORY=1 -s INITIAL_MEMORY=104857600 -s EXPORTED_FUNCTIONS="['_convexHull','_malloc','_free','setValue','getValue']" -s EXTRA_EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]'
|
||||||
|
|
||||||
docker run --rm -v $(pwd):/src emscripten/emsdk emcc convex_hull.c -o convex_hull.js -s USE_PTHREADS=1 -s IMPORTED_MEMORY=1 -s EXPORTED_FUNCTIONS="['_convexHull','_malloc','_free','setValue','getValue']" -s EXTRA_EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]'
|
# 优化指令
|
||||||
|
docker run --rm -v $(pwd):/src emscripten/emsdk emcc convex_hull.c -o convex_hull.js -s IMPORTED_MEMORY=1 -s EXPORTED_FUNCTIONS="['_convexHull','_malloc','_free','setValue','getValue']" -s EXTRA_EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]' -O2
|
Loading…
Reference in New Issue
Block a user