第5讲:建立自己的C函数库,js调用自己写的C/C++函数,并包含依赖C/C++第三方静态库。
在javascript中,Array有很多内置的功能,比如Array.map,Array.filter,Array.find等等,能用内置的功能就用内置的功能,最好不要自己实现一套,因为底层调用的可能压根就不是js语言本身,底层的实现可能由C/C++实现的。如果我们要做的一些功能,需要高性能密集计算,但是JavaScript内置函数无法满足我们要求的时候,这时候我们就要自己用C/C++编写一个程序,然后封装成wasm文件给JavaScript调用了,此时wasm还包含了.a文件这样的第三方库。
我们这里有个需求,就是在地球上有两艘船,船A和船B在某个经纬度位置触发,以某个航向、速度行驶,求它们间最小距离是多少,达到最小距离的时候,经过时间是多少秒?
首先这个功能用C/C++来编写,并且还要用到开源第三方库。
下图的红圈注释里面有几个参数,分别表示经度、纬度、速度、航向,当然getCPA最后一个参数6.5表示6.5分钟的时间长度。表示计算6.5分钟以内,两船最小距离是多少,并且到达最小距离时,经过时间是多少。
打开Visual Studio 2022,新建一个cmake工程,项目名称为GeoCompute。这仅仅是一个测试项目,如果测试通过,没有问题了,就把该代码交给emcc或者em++去编译。
CMakeLists.txt文件内容如下:
在这里,我采用vcpkg来安装GeographicLib库
可以执行如下命令安装。
vcpkg install GeographicLib:x64-windows
# CMakeList.txt: GeoCompute 的 CMake 项目,在此处包括源代码并定义 # 项目特定的逻辑。 # cmake_minimum_required (VERSION 3.8) set(VCPKG_ROOT "D:/CppPkg/WinVcpkg/vcpkg" CACHE PATH "") set(CMAKE_TOOLCHAIN_FILE "${VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake") # Enable Hot Reload for MSVC compilers if supported. if (POLICY CMP0141) cmake_policy(SET CMP0141 NEW) set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$,$,$>") endif() project ("GeoCompute") # 将源代码添加到此项目的可执行文件。 add_executable (GeoCompute "GeoCompute.cpp" ) find_package (GeographicLib CONFIG REQUIRED) target_link_libraries (GeoCompute PRIVATE ${GeographicLib_LIBRARIES}) if (CMAKE_VERSION VERSION_GREATER 3.12) set_property(TARGET GeoCompute PROPERTY CXX_STANDARD 20) endif() # TODO: 如有需要,请添加测试并安装目标。
然后编写一个GeoCompute.cpp文件
#include #include #include #include #include const double EARTH_RADIUS = 6377830.0; // 地球的平均半径,单位为千米 const double M_PI = 3.14159265359; struct LatLon { double first; double second; }; double deg2rad(double deg) { return deg * M_PI / 180.0; } double haversine_distance(double lat1, double lon1, double lat2, double lon2) { double dlat = deg2rad(lat2 - lat1); double dlon = deg2rad(lon2 - lon1); double a = std::sin(dlat / 2) * std::sin(dlat / 2) + std::cos(deg2rad(lat1)) * std::cos(deg2rad(lat2)) * std::sin(dlon / 2) * std::sin(dlon / 2); double c = 2 * std::atan2(std::sqrt(a), std::sqrt(1 - a)); return EARTH_RADIUS * c; } LatLon new_position_with_geolib(double lat, double lon, double speed, double cog, double T) { const GeographicLib::Geodesic& geod = GeographicLib::Geodesic::WGS84(); double s12 = speed * T; double lat2, lon2; // Direct method gives the destination point given start point, initial azimuth, and distance geod.Direct(lat, lon, cog, s12, lat2, lon2); return { lat2, lon2 }; } double new_distance(double T, double latA, double lonA, double speedA, double cogA, double latB, double lonB, double speedB, double cogB) { auto resA = new_position_with_geolib(latA, lonA, speedA, cogA, T); auto resB = new_position_with_geolib(latB, lonB, speedB, cogB, T); return haversine_distance(resA.first, resA.second, resB.first, resB.second); } LatLon getCPA(double latA, double lonA, double speedA, double cogA, double latB, double lonB, double speedB, double cogB, double tcpa) { double RES_TCPA = INFINITY; double RES_DCPA = INFINITY; double prev_dist = INFINITY; double cur_dist = INFINITY; std::vector status; int t_lim = tcpa * 60; int step = 1; if (t_lim > 600) { step = int(double(t_lim) / 300.0); } for (int t = 0;t > 3]; // 读取double指针的第0个元素 const res_dcpa = instance.HEAPF64[(resultPtr >> 3) + 1]; // 读取地址偏移量+1 console.log('TCPA:', res_tcpa); console.log('DCPA:', res_dcpa); console.log('Continuing after runtime initialization.'); // 继续下面的逻辑... } catch (error) { console.error('Error:', error); } } main();
index.html的内容:
Blank Window
package.json的内容:
{ "name": "nodedevtest", "version": "1.0.0", "description": "A minimal Electron application", "main": "main.js", "scripts": { "start": "node main.js" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "axios": "^1.6.8", "electron": "^30.0.1", "pixi.js": "^8.1.0", "request": "^2.88.2" } }
得出的结果:
得出的结果是154和1519.5687501879786
表示经过154秒以后,两船达到最小距离,并且最小距离为1519米多。