vtkModel.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. import * as d3 from "d3-scale";
  2. import { formatDefaultLocale } from "d3-format";
  3. import "@kitware/vtk.js/Rendering/Profiles/Geometry";
  4. import vtkFullScreenRenderWindow from "@kitware/vtk.js/Rendering/Misc/FullScreenRenderWindow";
  5. import vtkActor from "@kitware/vtk.js/Rendering/Core/Actor";
  6. import vtkMapper from "@kitware/vtk.js/Rendering/Core/Mapper";
  7. import vtkScalarBarActor from "@kitware/vtk.js/Rendering/Core/ScalarBarActor";
  8. import vtkColorTransferFunction from "@kitware/vtk.js/Rendering/Core/ColorTransferFunction";
  9. import { Representation } from "@kitware/vtk.js/Rendering/Core/Property/Constants";
  10. import vtkPolyData from "@kitware/vtk.js/Common/DataModel/PolyData";
  11. import { throttle } from '@kitware/vtk.js/macros';
  12. import {
  13. FieldDataTypes,
  14. FieldAssociations,
  15. } from '@kitware/vtk.js/Common/DataModel/DataSet/Constants';
  16. import vtkSphereSource from '@kitware/vtk.js/Filters/Sources/SphereSource';
  17. import vtkLineSource from '@kitware/vtk.js/Filters/Sources/LineSource';
  18. import vtkSphereMapper from '@kitware/vtk.js/Rendering/Core/SphereMapper';
  19. export class VtkModel {
  20. constructor() {
  21. //当前显示 模版 还是结果
  22. this.isJg = false;
  23. /**
  24. * 选择点线
  25. */
  26. this.isSelectNode = false;
  27. /**
  28. * 选择管道
  29. */
  30. this.isSelectPipe = false;
  31. //管道节点选择对象
  32. this.selectObj = null;
  33. //结果选择的点数据
  34. this.selectJgPointId=0;
  35. this.validNodes = [];//节点数据
  36. this.pipes = [];//管道数据
  37. this.renderWindowWith = vtkFullScreenRenderWindow.newInstance();
  38. this.renderer = this.renderWindowWith.getRenderer();
  39. this.renderWindow = this.renderWindowWith.getRenderWindow();
  40. //模板数据
  41. this.polyData = vtkPolyData.newInstance();
  42. this.mapper = vtkMapper.newInstance();
  43. this.actor = vtkActor.newInstance();
  44. this.actor.getProperty().setRepresentation(Representation.SURFACE); //线
  45. this.actor.getProperty().setColor(WHITE);
  46. this.mapper.setInputData(this.polyData);
  47. this.actor.setMapper(this.mapper);
  48. //点数据
  49. this.actor2 = vtkActor.newInstance();
  50. this.actor2.getProperty().setRepresentation(Representation.POINTS); //点
  51. this.actor2.getProperty().setColor(WHITE);
  52. this.actor2.getProperty().setPointSize(2);
  53. this.actor2.setMapper(this.mapper);
  54. //选取
  55. this.lastActor = null;
  56. this.nodeActors = []; //节点actor 数据
  57. this.pipeActors = []; //管道actor 数据
  58. this.interactor = this.renderer.getRenderWindow().getInteractor();
  59. this.apiSpecificRenderWindow = this.interactor.getView();
  60. this.hardwareSelector = this.apiSpecificRenderWindow.getSelector();
  61. this.hardwareSelector.setCaptureZValues(true);
  62. this.hardwareSelector.setFieldAssociation(FieldAssociations.FIELD_ASSOCIATION_POINTS);
  63. document.addEventListener("mousemove", throttleMouseHandler);
  64. //结果数据
  65. this.jgActor = vtkActor.newInstance();
  66. this.jgMapper = vtkMapper.newInstance();
  67. this.jgActor.getProperty().setRepresentation(Representation.SURFACE); //面
  68. this.scalarBarActor = vtkScalarBarActor.newInstance();
  69. this.scalarBarActor.setGenerateTicks(this.generateTicks(5));
  70. this.scalarBarActor.setDrawAboveRangeSwatch(true);
  71. this.scalarBarActor.setDrawNanAnnotation(false);
  72. this.scalarBarActor.setBoxPosition([1, 0]);
  73. // 修改设条颜色
  74. const ctf = vtkColorTransferFunction.newInstance();
  75. ctf.addRGBPoint(0.0, 0.0, 0.0, 1.0);
  76. ctf.addRGBPoint(1.0, 0.0, 1.0, 0.5);
  77. ctf.addRGBPoint(2.0, 0.0, 1.0, 0.0);
  78. ctf.addRGBPoint(3.0, 1.0, 0.5, 0.0);
  79. ctf.addRGBPoint(4.0, 1.0, 0.0, 0.0);
  80. this.jgMapper.setLookupTable(ctf);
  81. const lut = this.jgMapper.getLookupTable();
  82. this.scalarBarActor.setScalarsToColors(lut);
  83. this.jgActor.setMapper(this.jgMapper);
  84. }
  85. generateTicks(numberOfTicks) {
  86. return (helper) => {
  87. const lastTickBounds = helper.getLastTickBounds();
  88. // compute tick marks for axes
  89. const scale = d3
  90. .scaleLinear()
  91. .domain([0.0, 1.0])
  92. .range([lastTickBounds[0], lastTickBounds[1]]);
  93. const samples = scale.ticks(numberOfTicks);
  94. const ticks = samples.map((tick) => scale(tick));
  95. // Replace minus "\u2212" with hyphen-minus "\u002D" so that parseFloat() works
  96. formatDefaultLocale({ minus: "\u002D" });
  97. const format = scale.tickFormat(ticks[0], ticks[ticks.length - 1], numberOfTicks);
  98. const tickStrings = ticks
  99. .map(format)
  100. .map((tick) => Number(parseFloat(tick).toPrecision(12)).toPrecision()); // d3 sometimes adds unwanted whitespace
  101. helper.setTicks(ticks);
  102. helper.setTickStrings(tickStrings);
  103. };
  104. }
  105. modelInit(validNodes, pipes) {
  106. this.validNodes = validNodes;
  107. this.pipes = pipes;
  108. this.modelCreate();
  109. }
  110. modelCreate() {
  111. console.log("modelInit..");
  112. const points = this.polyData.getPoints();
  113. const lines = this.polyData.getLines();
  114. //无节点actor
  115. this.validNodes.forEach((node) => {
  116. points.insertNextPoint(parseFloat(node.x), parseFloat(node.y), parseFloat(node.z));
  117. });
  118. //无管道actor
  119. this.pipes.forEach((pipe) => {
  120. let sid = this.indexIdByPipeNodeId(pipe.snId);
  121. let eid = this.indexIdByPipeNodeId(pipe.enId);
  122. lines.insertNextCell([sid, eid]);
  123. });
  124. //有节点actors
  125. const sphereSource = vtkSphereSource.newInstance({
  126. center: [0, 0, 0],
  127. radius: 4.0,
  128. });
  129. const mapper = vtkMapper.newInstance();
  130. mapper.setInputConnection(sphereSource.getOutputPort());
  131. this.validNodes.forEach((node) => {
  132. const actor = vtkActor.newInstance();
  133. actor.setMapper(mapper);
  134. actor.setPosition(parseFloat(node.x), parseFloat(node.y), parseFloat(node.z));
  135. actor.getProperty().setColor(WHITE);
  136. const nodeActor = {};
  137. nodeActor.node = node;
  138. nodeActor.actor = actor;
  139. this.nodeActors.push(nodeActor);
  140. });
  141. //有管道actors
  142. this.pipes.forEach((pipe) => {
  143. const lineSource = vtkLineSource.newInstance();
  144. let point1 = this.pointByPipeNodeId(pipe.snId);
  145. let point2 = this.pointByPipeNodeId(pipe.enId);
  146. lineSource.setPoint1(point1);
  147. lineSource.setPoint2(point2);
  148. lineSource.setResolution(12);
  149. const actor = vtkActor.newInstance();
  150. const mapper = vtkMapper.newInstance();
  151. actor.setMapper(mapper);
  152. actor.getProperty().setLineWidth(3);//设置线宽
  153. actor.getProperty().setColor(WHITE);
  154. mapper.setInputConnection(lineSource.getOutputPort());
  155. const pipeActor = {};
  156. pipeActor.pipe = pipe;
  157. pipeActor.actor = actor;
  158. this.pipeActors.push(pipeActor);
  159. });
  160. this.modelShow();
  161. this.renderer.resetCamera();
  162. this.renderWindow.render();
  163. }
  164. modelClearShow() {
  165. this.renderer.removeActor(this.actor);
  166. this.renderer.removeActor(this.actor2);
  167. this.nodeActors.forEach((nodeActor) => {
  168. this.renderer.removeActor(nodeActor.actor);
  169. });
  170. this.pipeActors.forEach((pipeActor) => {
  171. this.renderer.removeActor(pipeActor.actor);
  172. });
  173. }
  174. modelShow() {
  175. this.modelClearShow();
  176. if (!this.isSelectNode) {//不选择节点
  177. this.renderer.addActor(this.actor2);
  178. }
  179. if (!this.isSelectPipe) {//不选择管道
  180. this.renderer.addActor(this.actor);
  181. }
  182. if (this.isSelectNode) {//选择节点
  183. this.nodeActors.forEach((nodeActor) => {
  184. this.renderer.addActor(nodeActor.actor);
  185. });
  186. }
  187. if (this.isSelectPipe) {//选择管道
  188. this.pipeActors.forEach((pipeActor) => {
  189. this.renderer.addActor(pipeActor.actor);
  190. });
  191. }
  192. }
  193. selectNodes() {
  194. this.isSelectNode = true;
  195. this.isSelectPipe = false;
  196. this.modelShow();
  197. this.renderWindow.render();
  198. }
  199. selectPipes() {
  200. this.isSelectNode = false;
  201. this.isSelectPipe = true;
  202. this.modelShow();
  203. this.renderWindow.render();
  204. }
  205. selectNoting(){
  206. this.isSelectNode = false;
  207. this.isSelectPipe = false;
  208. this.modelShow();
  209. this.renderWindow.render();
  210. }
  211. /**
  212. * 根据id 获取对应的编号
  213. */
  214. indexIdByPipeNodeId(nid) {
  215. for (let index = 0; index < this.validNodes.length; index++) {
  216. const node = this.validNodes[index];
  217. if (node.id == nid) {
  218. return index;
  219. }
  220. }
  221. return 0;
  222. }
  223. pointByPipeNodeId(nid) {
  224. for (let index = 0; index < this.validNodes.length; index++) {
  225. const node = this.validNodes[index];
  226. if (node.id == nid) {
  227. return [parseFloat(node.x), parseFloat(node.y), parseFloat(node.z)];
  228. }
  229. }
  230. return 0;
  231. }
  232. clearModeAddJg() {
  233. this.renderer.addActor(this.jgActor);
  234. this.renderer.addActor(this.scalarBarActor);
  235. this.modelClearShow();
  236. this.isJg = true; //显示结果
  237. }
  238. clearJgAddMode() {
  239. this.renderer.removeActor(this.scalarBarActor);
  240. this.renderer.removeActor(this.jgActor);
  241. this.modelShow();
  242. this.isJg = false; //不显示结果
  243. }
  244. }
  245. function pickOnMouseEvent(event) {
  246. if (vtkmodel.interactor.isAnimating()) {
  247. // We should not do picking when interacting with the scene
  248. return;
  249. }
  250. const [x, y] = eventToWindowXY(event);
  251. // console.log([x,y]);
  252. // vtkmodel.pointerActor.setVisibility(false);
  253. vtkmodel.hardwareSelector
  254. .getSourceDataAsync(vtkmodel.renderer, x, y, x, y)
  255. .then((result) => {
  256. if (result) {
  257. processSelections(result.generateSelection(x, y, x, y));
  258. // processSelections(result.generateSelection(x, y, x, y));
  259. } else {
  260. // processSelections(null);
  261. }
  262. });
  263. }
  264. function eventToWindowXY(event) {
  265. // We know we are full screen => window.innerXXX
  266. // Otherwise we can use pixel device ratio or else...
  267. const { clientX, clientY } = event;
  268. const [width, height] = vtkmodel.apiSpecificRenderWindow.getSize();
  269. const x = Math.round((width * clientX) / window.innerWidth);
  270. const y = Math.round(height * (1 - clientY / window.innerHeight)); // Need to flip Y
  271. return [x, y];
  272. }
  273. function processSelections(selections) {
  274. if (!selections || selections.length === 0) {//没有选择
  275. if (vtkmodel.lastActor == null) {
  276. } else {
  277. // vtkmodel.lastActor.getProperty().setColor(WHITE);
  278. // vtkmodel.renderWindow.render();
  279. }
  280. // vtkmodel.lastActor = null;
  281. return;
  282. }
  283. const {
  284. worldPosition: rayHitWorldPosition,
  285. compositeID,
  286. prop,
  287. propID,
  288. attributeID,
  289. } = selections[0].getProperties();
  290. if (vtkmodel.lastActor != null) {
  291. vtkmodel.lastActor.getProperty().setColor(WHITE);
  292. vtkmodel.renderWindow.render();
  293. }
  294. vtkmodel.nodeActors.forEach((nodeActor) => {
  295. if (prop == nodeActor.actor) {
  296. vtkmodel.selectObj = nodeActor.node;
  297. prop.getProperty().setColor(GREEN);
  298. vtkmodel.lastActor = prop;
  299. console.log(vtkmodel.selectObj );
  300. }
  301. });
  302. vtkmodel.pipeActors.forEach((pipeActor) => {
  303. if (prop == pipeActor.actor) {
  304. vtkmodel.selectObj = pipeActor.pipe;
  305. prop.getProperty().setColor(GREEN);
  306. vtkmodel.lastActor = prop;
  307. console.log(vtkmodel.selectObj );
  308. }
  309. });
  310. if (vtkmodel.isJg) {//结果数据选择cell
  311. //选择Cell
  312. const input = prop.getMapper().getInputData();
  313. if (!input.getCells()) {
  314. input.buildCells();
  315. }
  316. const cellPoints = input.getCellPoints(attributeID);
  317. if (cellPoints) {
  318. const pointIds = cellPoints.cellPointIds;
  319. // Find the closest cell point, and use that as cursor position
  320. const points = Array.from(pointIds).map((pointId) =>
  321. input.getPoints().getPoint(pointId)
  322. );
  323. // const scalarDataArray=input.getPointData().getScalars().getData();
  324. // pointIds.forEach(pointId=>{
  325. // console.log(input.getPointData().getScalars().getName());
  326. // console.log(scalarDataArray[pointId]);//节点标量数据
  327. // });
  328. vtkmodel.selectJgPointId =pointIds[0];
  329. }
  330. }
  331. vtkmodel.renderWindow.render();
  332. }
  333. const throttleMouseHandler = throttle(pickOnMouseEvent, 20);
  334. const WHITE = [0, 0, 1];
  335. const GREEN = [0.1, 0.8, 0.1];
  336. const vtkmodel = new VtkModel();
  337. export { vtkmodel };