需求:
将WPF程序中一段调用Cgal编译的dll的treemap算法 改成 调用d3.js中的packing Circle算法
重要程度
⭐⭐⭐⭐
人物性格:
非常懒
时限
10小时
思路
一.将d3.js打包成dll,然后WPF调用(因为C#中有现成的调用dll代码,因此修改的非常少)
a) 尝试JScript.NET
在VS开发人员命令行中输入
jsc /t:library d3.js
报错
D://1.js(2,323) : error JS1160: 特性列表不适用于当前上下文
D://1.js(2,334) : error JS1193: 应为“,”或“)”
D://1.js(2,449) : error JS1018: “return”语句在函数范围外
D://1.js(2,479) : error JS1195: 应为表达式
D://1.js(2,513) : error JS1195: 应为表达式
D://1.js(2,527) : error JS1004: 应为“;”
D://1.js(2,543) : error JS1004: 应为“;”
D://1.js(2,552) : error JS1010: 应为标识符
D://1.js(2,661) : error JS1195: 应为表达式
D://1.js(2,676) : error JS1010: 应为标识符
D://1.js(2,677) : warning JS1111: 已经定义了“t”
D://1.js(2,780) : error JS1002: 语法错误
D://1.js(2,825) : error JS1111: 已经定义了“i”
D://1.js(2,881) : error JS1160: 特性列表不适用于当前上下文
D://1.js(2,911) : error JS1004: 应为“;”
D://1.js(2,913) : error JS1004: 应为“;”
D://1.js(2,916) : error JS1006: 应为“)”
D://1.js(2,917) : error JS1004: 应为“;”
D://1.js(2,947) : error JS1160: 特性列表不适用于当前上下文
D://1.js(2,964) : error JS1004: 应为“;”
D://1.js(2,966) : error JS1004: 应为“;”
D://1.js(2,969) : error JS1006: 应为“)”
D://1.js(2,970) : error JS1004: 应为“;”
D://1.js(2,978) : error JS1197: 错误太多。该文件可能不是 JScript 文件
放弃
二.WPF直接调用js代码
a) 尝试MSScriptControl.ScriptControl
//string _MD5_js = "MD5.js";
private object ExecuteScript(string funcName, string argument, string jsPath)
{
string js = System.IO.File.ReadAllText(jsPath);
object o = ExecuteScript(string.Format("{0}('{1}')", funcName, argument), js);
return o;
}
/// <summary>
/// 执行JS
/// </summary>
/// <param name="sExpression">参数体</param>
/// <param name="sCode">JavaScript代码的字符串</param>
/// <returns></returns>
private object ExecuteScript(string sExpression, string sCode)
{
MSScriptControl.ScriptControl scriptControl = new MSScriptControl.ScriptControl();
scriptControl.UseSafeSubset = true;
scriptControl.Language = "JScript";
scriptControl.AddCode(sCode);
try
{
return scriptControl.Eval(sExpression);
}
catch (Exception ex)
{
}
return null;
}
报错
放弃
三.Asp.net后台调用js代码,然后WPF通过post获取数据(因为Asp.net有现成的post函数)
a) 尝试ValuesController.cs中在Post函数中调用JS代码
对MVC不是特别熟,没有余力去思考ValuesController对应的view在哪儿了
放弃
四.直接写一个post服务器,然后WPF通过post获取数据
服务器
// postserver.js
// 获取客户端post来的title、text参数,然后返回给客户端
var http = require('http');
var querystring = require('querystring');
var server = http.createServer(function(req, res) {
var post = '';
req.on('data', function(chunk) {
post += chunk;
});
req.on('end', function() {
post = querystring.parse(post);
res.write(post.title);
res.write(post.text);
res.end();
});
}).listen(3000);
在控制台中执行node server.js,开启http服务,监听3000端口。
客户端
var http = require('http');
var querystring = require('querystring');
var contents = querystring.stringify({
title: 'Hello ',
text: 'Arthur',
});
var options = {
host: '127.0.0.1',
port: '3000', //非80端口需要指定,如果是80端口则可以省略。
path: '/',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': contents.length
}
};
var req = http.request(options, function(res) {
res.setEncoding('utf8');
res.on('data', function(data) {
console.log('request begin:');
console.log(data);
console.log('request end.');
console.log('--------------------');
});
});
req.write(contents);
req.end();
在另一个控制台中执行node client.js
a) 安装Nodejs
b) 写一个简单的post服务器和客户端测试
成功
c) 在post服务器中调用d3.js
var d3 = require("./d3.js");
报错
ReferenceError: XMLHttpRequest is not defined
at nu (C:\Users\lunhui\Desktop\d3.js:2:63462)
at Object.json (C:\Users\lunhui\Desktop\d3.js:2:65084)
at d33 (C:\Users\lunhui\Desktop\test.js:41:8)
at IncomingMessage.<anonymous> (C:\Users\lunhui\Desktop\test.js:21:18)
[90m at IncomingMessage.emit (node:events:406:35)[39m
[90m at endReadableNT (node:internal/streams/readable:1329:12)[39m
[90m at processTicksAndRejections (node:internal/process/task_queues:83:21)[39m
α)尝试1:
原因:xmlhttprequest 不是Node 内置的,需要单独安装:
安装命令:npm install xmlhttprequest
失败
β)尝试2:
搜索nodejs调用d3.js
报错
安装express
npm install express
然后不报错了,但是脚本也没用
失败
γ)尝试3:
找到一份代码node.js中用d3.js
npm install d3
报错
尝试解决方案1
Check for 2 things:
Node version >= 14. It only works with latest version of node.
Make sure your package.json includes a line for “type”: “module”, Without this line node assumes you want to use Common JS modules rather than ESM.
报错
尝试解决方案2
在你要require的代码前引入如下代码即可:
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
报错
尝试解决方案3
在 Node.js 中使用 import
Node 版本需在 9.0 及以上
不加 loader 时候,使用 import/export 的文件后缀名必须为 .mjs
import d3 from 'd3'
修改为
import * as d3 from 'd3';
其他的require也都要改成这个亚子
成功!
d) 尝试连接
报错
ReferenceError: fetch is not defined
在node_module/d3_fectch/src/json.js开头添加以下两句
import { fetch as fetchPolyfill } from 'whatwg-fetch'
global.fetch = fetchPolyfill
报错
把var转换成字符串
报错
node_modules\whatwg-fetch\dist\fetch.umd.js
添加
import * as XMLHttpRequest from 'xmlhttprequest';
报错
这是个终极大错!
退无可退!
放弃不了了!
现在已经22点.明天记续
给明天留个希望: d3.json()和d3.csv()出了问题,也就是读取文件出了问题
五.中期总结
我好像没有时间详细记录问题和解决方案了,只能草草描述.
果然是读取文件出了问题
换成了fs.readFileSync就再也没有报错了
然而接下来的第二天时间却浪费在了d3.js的返回数据格式上
这件事给我的教训是 一定要先去看说明文档,不要自己想当然的测试
https://observablehq.com/@d3/d3-hierarchy1
第三天的时间浪费在了fs.readFileSync之后没有转化成Json格式.
var root = JSON.parse(fs.readFileSync('./flare-2.json', 'utf8'));
成功输出正确格式的root;
终于,马上要成功了.
然而好像并没有那个简单,除了根节点的x,y被计算出来,其他的所有属性都没有记录.
六.神奇的转折-C#
结果在github找到一个C#的circle packing程序
Circle Packing Algorithm for C# (in a Windows Universal project)
兜兜转转又回到最初的起点
正在下载相关组件,测试是否work
发现Visual Studio 2017 和Visual Studio 2019 都打不开UWP项目
期间测试了一波下载Visual Studio 2015
失败
于是直接从源代码里摘出来放到WPF项目中,然而,发现并不是自己需要的效果
失败
七.重回Html
又回到Github,找到了Html
Github:balakrishna-k/CirclePack
这个能运行
并且可以获取到数据的返回值,很是成功.
var circle = g.selectAll("circle")
.data(nodes)
.enter().append("circle")
.attr("class", function(d) { return d.parent ? d.children ? "node" : "node node--leaf" : "node node--root"; })
;
alert(circle._groups[0][0].__data__.x);
八.Html的Socket通信
html的websocket与我已有的C#的TCP socket连接失败
Websocket错误:ERR_INVALID_HTTP_RESPONSE
原因是:
因此实现了一个C#的WebSocket的服务器,连接成功.
最终我的伞兵架构为下图:
1.把服务端放在服务器而不是本地上是因为快速解决跨域问题(后来放弃了,因为外网IP的问题,最后放在了本地IIS服务器上)
2.写两个socket是因为WPF和服务端的Tcp socket是现成的,最简单的修改是直接websocket与tcp socket之间互传字符串
九.最终调试
经过了几个小bug,最终三个程序联调成功.(C#服务器调用UI组件一定要用Dispatcher.BeginInvoke,否则会出各种奇怪的bug)
剩下的就是规范数据传输格式,封装函数等工作了.
十.总结
看似初衷是为了省事,实则花费了更多时间和精力.
究竟是变通了,不撞南墙了?还是浮躁了,静不下来了呢?
十一.番外
如同之前很多次一样,在一切都结束的时候,我才突然意识到,d3.js中的packing Circle算法不能计算非矩形内的packing circle.
因此,这些工作好像都没有用上.
之前是想得太多,做的太少.现在是想的太少,直接去莽.
都是教训