@sohale/implisolid
Version:
A Solid Modelling Kernel in Javascript based on Implicit Surfaces, for MP5 file format
442 lines (346 loc) • 16.4 kB
JavaScript
;
/******************************************************
* Low level: Level 1
******************************************************/
var wapi1 = {}; // todo: rename
wapi1.worker = new Worker('../js_iteration_2/js/worker_api.js');
wapi1.worker_response_callbacks_table = {};
const WORKER_DEBUG = true;
wapi1.w_api_debug_info = {};
wapi1.w_api_info2 = {}; // w_api_info2.READABLE_COMPILE_TIME_NAME = ... // such as function name or a name that is similar to a funciton name
wapi1.call_counter = 0; // a clobal counter that assigns a unique id to each individual call, to make it easy to trace correspondence of call and callback.
var wcodes1 = {};
wcodes1.__wapi_get_latest_vf = 6;
wcodes1.__wapi_make_geometry = 5;
var callstamps = {};
wapi1.worker.addEventListener('message', function(event) {
if (WORKER_DEBUG)
console.info("Message received from worker:", event, "data=", event.data);
var _callbackId = event.data.return_callback_id;
var mycallback = wapi1.worker_response_callbacks_table[ _callbackId ];
if (mycallback) {
// delete wapi1.worker_response_callbacks_table[ _callbackId ];
// don’t delete array elements. It would make the array transition to a slower internal representation.
wapi1.worker_response_callbacks_table[ _callbackId ] = undefined; // per-call
if (WORKER_DEBUG) {
// tracking calls, measuring the time, detecting orphan calls (calls with no return), etc.
var call_id = event.data.call_id;
var start_time = callstamps[call_id];
console.info("TIME: ", new Date() - start_time , "msec");
}
}
//if (_callbackId === 4) {
// console.info("The result of 'query_implicit_values()' was: ", event.data.returned_data); //event.data.result_allpositive
//}
if (!mycallback) {
console.error("Returned data from worked did not match any registered callbackId: ", event.data, event);
return;
}
if (WORKER_DEBUG)
console.info("calling the callback ", mycallback.name);
var call_identification = event.data.call_id;
var shape_id = event.data.shape_id;
mycallback(event.data.returned_data, call_identification, shape_id);
// todo: mycallback(event.data.returned_data, call_identification, event.data.shape_id);
});
function worker_call_preparation(_callbackId, result_callback) {
wapi1.call_counter ++ ;
if (WORKER_DEBUG) {
var temp = wapi1.worker_response_callbacks_table[_callbackId];
if (temp) {
console.warn("callback already set.");
if (!(temp === result_callback))
console.error("Warning: and callback is different!: ", temp, "!==", result_callback);
}
}
wapi1.worker_response_callbacks_table[_callbackId] = result_callback;
if (WORKER_DEBUG) {
wapi1.w_api_debug_info[_callbackId] = result_callback;
// also you can store call timestamp, etc
}
//console.info(callstamps, wapi1.call_counter);
callstamps[wapi1.call_counter] = new Date();
//console.info(callstamps);
return _callbackId;
}
/*******************************************************************************
* API Level 2
*******************************************************************************/
// Level 2
var wapi2 = {};
wapi2.wapi_make_geometry = function (shape_id0, mp5_str, polygonization_settings_json, result_callback) {
// based on implisolid_main.js
//var startTime = new Date();
my_assert(typeof result_callback === 'function');
var _callbackId = worker_call_preparation(wcodes1.__wapi_make_geometry, result_callback);
var wreq = {
funcName: 'make_geometry',
callbackId: _callbackId,
call_id: wapi1.call_counter,
// call_timestampe: new Date(),
mp5_str: null, polygonization_settings: null, obj_req_id: 0
};
wreq.mp5_str = mp5_str;
wreq.polygonization_settings = polygonization_settings_json; // mc_params;
wreq.obj_req_id = shape_id0; //wapi1.call_counter;
wapi1.worker.postMessage(wreq);
/*
const _FLOAT_SIZE = Float32Array.BYTES_PER_ELEMENT;
const _INT_SIZE = Uint32Array.BYTES_PER_ELEMENT
if(impli1.needs_deallocation) {
impli1.finish_geometry();
impli1.needs_deallocation = false;
}
impli1.build_geometry(mp5_str, mc_params_json);
impli1.needs_deallocation = true;
var nverts = impli1.get_v_size();
var nfaces = impli1.get_f_size();
var verts_address = impli1.get_v_ptr();
var faces_address = impli1.get_f_ptr();
var verts = Module.HEAPF32.subarray(verts_address/_FLOAT_SIZE, verts_address/_FLOAT_SIZE + 3*nverts);
var faces = Module.HEAPU32.subarray(faces_address/_INT_SIZE, faces_address/_INT_SIZE + 3*nfaces);
// first iteration: the callback
var geom = result_callback(verts, faces);
var endTime = new Date();
var timeDiff = endTime - startTime;
//report_time(timeDiff, function(){hist();});
return geom;
*/
}
// w_impli2.query_implicit_values_old = function(mp5_str, points, reduce_callback, call_id, shape_index)
/**
@param client_result_callback is needed for all worker API functions. client_result_callback is a fuction that receives the and called asynchroniously. Even if it sdoesn't have eany argument, it notifies completion f the worker's job.
*/
wapi2.wapi_query_implicit_values_old = function(shape_index, shape_json, points, reduce_type_str, epsilon, client_result_callback) {
my_assert(typeof reduce_type_str === 'string');
if(shape_index === undefined ) shape_index = "test(undefiend)";
// registers the function client_result_callback to listen to the messages posted from the webworker
var _callbackId = worker_call_preparation(wcodes1.__wapi_query_implicit_values_old, client_result_callback);
var wreq = {
funcName: 'query_implicit_values_old',
callbackId: _callbackId,
call_id: wapi1.call_counter,
mp5_str: null, points: null, reduce_type: "", epsilon: 0.0000,
obj_req_id: -1
};
wreq.mp5_str = shape_json;
wreq.points = points; // the points are set to the worker via the postMessage, as the Float32Array they are.
wreq.reduce_type = reduce_type_str;
wreq.epsilon = epsilon;
wreq.shape_id = shape_index;
wapi1.worker.postMessage(wreq);
}
/** called by query_a_normal()
@param: clientside_result_callback = function((normals, call_id, shape_id)){output_vect3.set(-r[0], -r[1], -r[2]);}
*/
wapi2.wapi_query_a_normal = function() {
//static
var pointbuffer = new Float32Array(3);
return function wapi_query_a_normal(shape_index, mp5_shape_json, point, clientside_result_callback) {
my_assert(typeof mp5_shape_json === 'string');
// registers the worker callback
var _callbackId = worker_call_preparation(wcodes1.__wapi_query_a_normal, clientside_result_callback);
if (point instanceof Float32Array) {
pointbuffer.set(point);
} else {
pointbuffer[0] = point.x;
pointbuffer[1] = point.y;
pointbuffer[2] = point.z;
}
var wreq = {
funcName: 'query_normals',
callbackId: _callbackId,
call_id: wapi1.call_counter,
mp5_str: null, points: null,
obj_req_id: -1
};
wreq.mp5_str = mp5_shape_json;
wreq.points = pointbuffer;
wreq.shape_id = shape_index;
wapi1.worker.postMessage(wreq);
};
}();
wapi2.wapi_get_latest_vf = function (shape_id, result_callback) {
my_assert(typeof result_callback === 'function');
var _callbackId = worker_call_preparation(wcodes1.__wapi_get_latest_vf, result_callback);
var wreq = {
funcName: 'get_latest_vf',
callbackId: _callbackId,
call_id: wapi1.call_counter,
obj_req_id: 0
};
wreq.shape_id = shape_id, // wreq.obj_req_id = obj_id, //wapi1.call_counter;
wapi1.worker.postMessage(wreq);
}
/*******************************************************************************
* API Level 3
******************************************************************************/
var wapi3 = {}; // not worker side, but uses worker
// wapi3.custom_mc_settings = {};
// Copied from implisolid_main.js
var SET_ME_TO_TRUE = false;
/*
wapi3.getLiveGeometry = function(shape_properties, bbox, ignore_root_matrix, geom_callback) {
var mc_properties = IMPLICIT.make_polygonization_settings(bbox, ignore_root_matrix);
var shape_json_str = JSON.stringify(shape_properties);
var polygonization_properties_json = JSON.stringify(mc_properties);
this.getLiveGeometry_from_json(shape_json_str, polygonization_properties_json, geom_callback);
}
*/
wapi3.getLiveGeometry_from_json = function(shape_id00, shape_json_str, polygonization_setttings_json_str, geom_callback) {
assert(typeof shape_id00 === "number");
assert(typeof shape_json_str === "string");
assert(typeof polygonization_setttings_json_str === "string");
assert(typeof geom_callback === "function");
// wapi2.make_geometry
//var geom = // no geom returned here anymore
var shape_id0 = shape_id00;
wapi2.wapi_make_geometry(shape_id0, shape_json_str, polygonization_setttings_json_str,
function (vf_dict, call_id, shapeid_) {
var verts = vf_dict.verts;
var faces = vf_dict.faces;
assert(shapeid_ == shape_id0);
// ThreeJS-specific code
// var ignore_root_matrix: Does not need other (MC-related) arguments.
var allocate_buffers = true;
var geom = new LiveBufferGeometry79(verts, faces, allocate_buffers);
// Set the normals
// var ignore_root_matrix = mc_params.ignore_root_matrix; // Does not need other (MC-related) arguments.
//geom.update_normals(this_, verts, shape_json_str, ignore_root_matrix); // Evaluates the implicit function and sets the goemetry's normals based on it.
//
if (!shape_json_str)
console.error(shape_json_str);
if (SET_ME_TO_TRUE)
wapi3.make_normals_into_geometry(geom, shapeid_, shape_json_str, verts, ignore_root_matrix); // Evaluates the implicit function and sets the goemetry's normals based on it.
//this_.aaaaaaaaaA(verts);
// no geom anymore
// return geom;
//var shape_id = 99; // vf_dict.shape_id
geom_callback(geom, shape_id0);
}
);
// return geom;
};
wapi3.wapi_query_implicit_values = function (obj_id, mp5_str, points, result_callback) {
var _callbackId = worker_call_preparation(4, result_callback);
var wreq = {
funcName: 'query_implicit_values',
callbackId: _callbackId,
call_id: wapi1.call_counter,
//args: {
mp5_str: null, points: null, reduce_callback: "", obj_id: 0
//},
};
wreq.mp5_str = mp5_str;
wreq.points = points;
wreq.reduce_callback = 'any-positive'; // all outside or on
wreq.obj_id = obj_id;
wapi1.worker.postMessage(wreq);
}
wapi3.receive_mesh = function(shape_id_, result_callback) {
var vf = {faces: null, verts: null};
/*
var nonempty = wapi2.wapi_get_latest_vf(vf);
if (nonempty) {
// vf already contains the output of get_latest_vf()
} else{
console.log("empty implicit. Using a default shape.");
vf['verts'] = new Float32Array([0,0,0, 1,0,0, 1,1,0, 0,1,0, 0,0,1, 1,0,1, 1,1,1, 0,1,1 ]);
vf['faces'] = new Uint32Array([0,1,2, 0,2,3, 0,4,5, 0,5,1, 1,5,6, 1,6,2, 2,6,3, 3,6,7, 4,5,6, 5,6,7]);
}
assert(vf['verts']);
assert(vf['faces']);
*/
// should be no logic here
wapi2.wapi_get_latest_vf(shape_id_, function c3(data_received_from_worker, call_id, shape_id){
data_received_from_worker.verts;
data_received_from_worker.faces;
data_received_from_worker.nonempty;
var vf = data_received_from_worker;
var nonempty = data_received_from_worker.nonempty;
if (nonempty) {
// vf already contains the output of get_latest_vf()
} else{
console.log("empty implicit. Using a default shape.");
vf['verts'] = new Float32Array([0,0,0, 1,0,0, 1,1,0, 0,1,0, 0,0,1, 1,0,1, 1,1,1, 0,1,1 ]);
vf['faces'] = new Uint32Array([0,1,2, 0,2,3, 0,4,5, 0,5,1, 1,5,6, 1,6,2, 2,6,3, 3,6,7, 4,5,6, 5,6,7]);
}
assert(vf['verts']);
assert(vf['faces']);
//return vf;
result_callback(shape_id_, vf);
});
}
/**
ver 1 is a test for a recursive - like function in side another function (function composition; promise chaining).
*/
wapi3.update_geometry_from_json_ver1 = function(geometry, shape_id, shape_json, polygonization_json, done_callback) {
//var startTime = new Date();
if (typeof shape_json !== "string") {
shape_json = JSON.stringify(shape_json);
}
assert(typeof done_callback !== "number" && typeof done_callback !== "boolean");
assert(typeof done_callback === "function");
// polygonization_json should not be jsonified.
if (typeof polygonization_json !== "string") {
polygonization_json = JSON.stringify(polygonization_json);
}
wapi2.wapi_make_geometry (shape_id, shape_json, polygonization_json, //result_callback,
//function (verts, faces) {
function c3(vf_result, call_id1, shape_id1) {
//var allocate_buffer=false;
vf_result.faces;
vf_result.verts;
// vf_result already contains it!!
// todo: make it the same query. dont call get_vf again.
wapi3.receive_mesh(shape_id1, function(shape_id__, vf) {
vf.faces;
vf.verts;
vf.nonempty;
// todo: match the object_id to the request call_id, etc
var ignoreDefaultNormals = true;
var bool_reallocated = geometry.update_geometry1(vf['verts'], vf['faces'], ignoreDefaultNormals, false);
geometry.use_default_normals_from_vertices();
/*
var startTime = callstamps[call_id];
var endTime = new Date();
var timeDiff = endTime - startTime;
console.log(nv3 + " , " + nf3);
console.log("Time: "+timeDiff+ " msec.");
report_time(timeDiff);
*/
//geometry is now updated
//var call_id = 10000;
//done_callback(null, call_id, shape_id__);
done_callback(shape_id__, null);
});
}
);
};
/**
ver 2 is faster (simpler), doing it in a single request.
*/
wapi3.update_geometry_from_json_ver2 = function(geometry, shape_id, shape_json, polygonization_json, done_callback) {
if (typeof shape_json !== "string") shape_json = JSON.stringify(shape_json);
assert(typeof done_callback !== "number" && typeof done_callback !== "boolean");
assert(typeof done_callback === "function");
if (typeof polygonization_json !== "string") polygonization_json = JSON.stringify(polygonization_json);
wapi2.wapi_make_geometry (shape_id, shape_json, polygonization_json, //result_callback,
function c3(vf_result, call_id1, shape_id1) {
vf_result.faces; vf_result.verts;
assert(shape_id1 === shape_id);
var ignoreDefaultNormals = true;
var bool_reallocated = geometry.update_geometry1(vf_result.verts, vf_result.faces, ignoreDefaultNormals, false);
geometry.use_default_normals_from_vertices();
done_callback(shape_id1, null);
}
);
};
wapi3.query_implicit_values_old = wapi2.wapi_query_implicit_values_old;
wapi3.wapi_query_a_normal = wapi2.wapi_query_a_normal;
// End of Worker-based API
// ******************************************************************************
var IMPLICIT_WORKER = wapi3;
// wapi_*
console.log("IMPLICIT_WORKER ---------------");
for(var a in IMPLICIT_WORKER) console.log("IMPLICIT_WORKER.",a);