DiagramEditor.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  1. import {Diagram, Template, EquipmentNode, Line, Comp, Container, MainPipe, Legend, Anchor, Point4Anchor} from './DiagramModel';
  2. import {Point, BackgroundPaniter, ViewTool} from './UIUtils';
  3. import {Editor, Selection} from './Editor';
  4. /**
  5. * 系统图编辑器,演示版本
  6. * @author zhaoyk
  7. */
  8. export class DiagramEditor extends Editor {
  9. private util: EditUtil;
  10. diagram: Diagram;
  11. templateData: Template;
  12. libertyCons: Array<Container>; //自由定位的容器
  13. tmpMainPipe:Array<Array<Point>>;
  14. currentTmpMainPipe:Array<Point>;
  15. showData(data?: any): void {
  16. this.diagram = (<Diagram>data);
  17. this.templateData = this.diagram.template;
  18. this.selection = new Selection();
  19. this.setState(this.getState());
  20. this.util = new EditUtil(this);
  21. this.redraw();
  22. }
  23. protected initCanvasSize(): Point {
  24. return new Point(3500, 3500);
  25. }
  26. addListeners():void {
  27. this.canvas.addEventListener('click', event=>{this.onClick(event)});
  28. this.container.addEventListener("mousemove", event=>{this.onMouseMove(event)});
  29. }
  30. setState(state: number): void{
  31. this.state = state;
  32. if(this.state == 9)
  33. this.tmpMainPipe = new Array<Array<Point>>();
  34. else
  35. this.tmpMainPipe = null;
  36. this.currentTmpMainPipe = null;
  37. }
  38. redraw(): void {
  39. this.beforeRedraw();
  40. if(!this.diagram.template)
  41. return;
  42. this.libertyCons = new Array<Container>();
  43. const root = this.templateData.frame;
  44. this.util.prepareParent(root);
  45. //绘制排列定位容器
  46. this.drawContainer(root);
  47. //绘制自由定位容器
  48. const p = new Point(root.location.x, root.location.y);
  49. p.y += root.size.y;
  50. this.libertyCons.forEach(con => {
  51. p.y += 20;
  52. con.location = new Point(p.x, p.y);
  53. this.drawContainer(con, true);
  54. p.y += con.size.y;
  55. });
  56. //绘制干管
  57. if(this.templateData.mainPipes) {
  58. this.templateData.mainPipes.forEach(item => {
  59. this.ctx.strokeStyle = "#000000";
  60. this.ctx.setLineDash([]);
  61. this.ctx.lineWidth = 2;
  62. if(this.getSelComp() == item)
  63. this.ctx.strokeStyle = "#5783FA";
  64. this.ctx.beginPath();
  65. for(const arr of item.locationPath){
  66. var iter = 0;
  67. for(var p of arr) {
  68. p = this.util.toCanvas(p);
  69. if(iter == 0)
  70. this.ctx.moveTo(p.x, p.y);
  71. else
  72. this.ctx.lineTo(p.x, p.y);
  73. iter++;
  74. }
  75. }
  76. this.ctx.stroke();
  77. });
  78. }
  79. //绘制连线
  80. if(this.diagram.lines) {
  81. this.diagram.lines.forEach(line => {
  82. if(line.locationPath) {
  83. this.ctx.strokeStyle = "#000000";
  84. this.ctx.setLineDash([]);
  85. this.ctx.lineWidth = 1;
  86. this.ctx.beginPath();
  87. var iter = 0;
  88. for(var p of line.locationPath){
  89. p = this.util.toCanvas(p);
  90. if(iter == 0)
  91. this.ctx.moveTo(p.x, p.y);
  92. else
  93. this.ctx.lineTo(p.x, p.y);
  94. iter++;
  95. }
  96. this.ctx.stroke();
  97. }
  98. });
  99. }
  100. }
  101. private drawContainer(con:Container, drawLiberty?:boolean) {
  102. if(!drawLiberty && con.position.absolute)
  103. return;
  104. if(con.size.x == 0 && con.size.y == 0)
  105. return;
  106. const liberty = con.position.absolute;
  107. const location = !liberty ? this.util.locationOnCanvas(con) : this.util.toCanvas(con.location);
  108. const equip = con.equipmentTypes && con.equipmentTypes.length > 0;
  109. var color = !liberty ? "#9b9ea3": "#42B983";
  110. var dash = [3, 2];
  111. if(equip) {
  112. this.ctx.lineWidth = 1;
  113. this.ctx.setLineDash(dash);
  114. this.ctx.strokeStyle = color;
  115. this.ctx.beginPath();
  116. this.ctx.rect(location.x, location.y, con.size.x, con.size.y);
  117. this.ctx.closePath();
  118. if(this.getSelComp() == con) {
  119. this.ctx.fillStyle = "rgba(0, 127, 255, 0.1)";
  120. this.ctx.fill();
  121. }
  122. this.ctx.stroke();
  123. }
  124. if(con.children)
  125. con.children.forEach(item => {
  126. if(item.compType == 'container')
  127. this.drawContainer(<Container>item);
  128. else if(item.compType == 'equipmentNode')
  129. this.drawEquipNode(<EquipmentNode>item);
  130. else if(item.compType == 'packNode')
  131. this.drawEquipNode(<EquipmentNode>item);
  132. });
  133. }
  134. private drawEquipNode(node: EquipmentNode): void {
  135. const location = this.util.locationOnCanvas(node);
  136. this.ctx.lineWidth = 1;
  137. this.ctx.setLineDash([]);
  138. this.ctx.strokeStyle = "#000000";
  139. this.ctx.beginPath();
  140. this.ctx.rect(location.x, location.y, node.size.x, node.size.y);
  141. if(this.getSelComp() == node) {
  142. this.ctx.fillStyle = "rgba(0, 127, 255, 0.1)";
  143. this.ctx.fill();
  144. }
  145. this.ctx.stroke();
  146. if(node.anchorLocations) {
  147. for(const code in node.anchorLocations) {
  148. this.ctx.beginPath();
  149. var p: Point = node.anchorLocations[code];
  150. p = new Point(p.x + location.x, p.y + location.y);
  151. ViewTool.paintPoint(this.ctx, p, '#000000', true);
  152. }
  153. }
  154. if(node.label) {
  155. const lbl = node.label;
  156. ViewTool.drawText(this.ctx, lbl.content, new Point(location.x + lbl.location.x, location.y + lbl.location.y), node.size);
  157. }
  158. }
  159. // private redrawOnScroll(_this) {
  160. // const top = _this.container.scrollTop;
  161. // const left = _this.container.scrollLeft;
  162. // _this.canvas.style.top = top + 'px';
  163. // _this.canvas.style.left = left + 'px';
  164. // _this.draw(_this.mapData);
  165. // }
  166. private onClick(event): void {
  167. if(this.state != 9) { //selection
  168. var sel:any = this.util.findMainPipe(event.layerX, event.layerY);
  169. if(sel == null)
  170. sel = this.util.findComp(event.layerX, event.layerY);
  171. if(sel != this.getSelComp)
  172. this.setSelComp(sel);
  173. } else { //paint main pipe
  174. if(this.currentTmpMainPipe == null) {
  175. this.currentTmpMainPipe = new Array<Point>();
  176. this.tmpMainPipe.push(this.currentTmpMainPipe);
  177. }
  178. const point = new Point(event.layerX, event.layerY);
  179. this.adjustPointForMp(point);
  180. this.currentTmpMainPipe.push(point);
  181. this.redraw();
  182. }
  183. }
  184. setSelComp(comp: any): void{
  185. this.selection.setSel(comp);
  186. this.redraw();
  187. this.editorPart.onSelectionChange();
  188. }
  189. getSelComp(): any {
  190. if(!this.selection)
  191. return null;
  192. return this.selection.getSel();
  193. }
  194. buildSelInfo(){
  195. var info = null;
  196. const sel = this.getSelComp();
  197. if(sel != null) {
  198. if(sel.compType == "equipmentNode") {
  199. info = '设备id: ' + sel.dataObject.id;
  200. info += ' | 设备类型: ' + sel.dataObject.classCode;
  201. info += ' | 设备名称: ' + sel.dataObject.name;
  202. } else {
  203. info = 'id: ' + sel.id;
  204. if(sel.name)
  205. info += ' | 名称: ' + sel.name;
  206. if(sel.layout) {
  207. if(sel.layout.layout == 1)
  208. info += ' | 布局方式: 行式';
  209. else if(sel.layout.layout == 2)
  210. info += ' | 布局方式: 列式';
  211. }
  212. if(sel.equipmentTypes && sel.equipmentTypes.length > 0){
  213. info += ' | 设备类型: ' + sel.equipmentTypes;
  214. }
  215. if(sel.locationPath){
  216. var str = '';
  217. const arr:Array<Array<Point>> = new Array<Array<Point>>();
  218. for(const line of sel.locationPath) {
  219. const ps:Array<Point> = new Array<Point>();
  220. arr.push(ps);
  221. line.forEach(item => {
  222. ps.push(this.util.toCanvas(item));
  223. });
  224. if(str.length > 0)
  225. str += ", ";
  226. str += ViewTool.pointsToStr(ps);
  227. }
  228. info += ' | ' + str;
  229. }
  230. }
  231. }
  232. if(info == null)
  233. info = '~';
  234. return info;
  235. }
  236. getState(): number {
  237. const sel = this.getSelComp();
  238. if(!sel)
  239. return 0;
  240. if(sel.locationPath)
  241. return 2;
  242. return 1;
  243. }
  244. getCompById(id: string): any {
  245. if(!id)
  246. return null;
  247. if(this.templateData.mainPipes) {
  248. for(const mp of this.templateData.mainPipes) {
  249. if(mp.id == id)
  250. return mp;
  251. }
  252. }
  253. return this.getConById(id);
  254. }
  255. getConById(id:string): Container {
  256. return this.findCon(id, this.templateData.frame);
  257. }
  258. private findCon(id:string, con: Container): Container {
  259. if(con.id == id)
  260. return con;
  261. for(const sub of con.children) {
  262. if(sub.compType == 'container') {
  263. const rtn = this.findCon(id, <Container>sub);
  264. if(rtn != null)
  265. return rtn;
  266. }
  267. }
  268. return null;
  269. }
  270. //干管绘制时,自动调整横平竖直
  271. private adjustPointForMp(point: Point) {
  272. if(this.currentTmpMainPipe && this.currentTmpMainPipe.length > 0) {
  273. const pre = this.currentTmpMainPipe[this.currentTmpMainPipe.length - 1];
  274. if(Math.abs(pre.x - point.x) < Math.abs(pre.y - point.y))
  275. point.x = pre.x;
  276. else
  277. point.y = pre.y;
  278. }
  279. }
  280. private onMouseMove(event): void {
  281. if(this.state == 9) {
  282. const point = new Point(event.layerX, event.layerY);
  283. this.adjustPointForMp(point);
  284. //显示当前坐标位置
  285. this.editorPart.dynInfo = ViewTool.pointToStr(point);
  286. this.redraw();
  287. }
  288. }
  289. }
  290. class EditUtil {
  291. private editor:DiagramEditor;
  292. private template:Template;
  293. constructor(editor:DiagramEditor) {
  294. this.editor = editor;
  295. this.template = editor.templateData;
  296. }
  297. prepareParent(con:Container) {
  298. if(con.children) {
  299. con.children.forEach(item => {
  300. item.parent = con;
  301. if(item.compType == 'container') {
  302. this.prepareParent(<Container>item);
  303. if(item.position.absolute)
  304. this.editor.libertyCons.push(<Container>item);
  305. }
  306. });
  307. }
  308. }
  309. clearParent(con:Container){
  310. con.parent = null;
  311. con.children.forEach(item => {
  312. item.parent = null;
  313. if(item.compType == 'container')
  314. this.clearParent(<Container>item);
  315. });
  316. }
  317. locationOnDiagram(con:Comp): Point {
  318. const p:Point = new Point(0, 0);
  319. var c = con;
  320. while (c != null){
  321. p.x += c.location.x;
  322. p.y += c.location.y;
  323. c = c.parent;
  324. }
  325. return p;
  326. }
  327. locationOnCanvas(con:Comp): Point {
  328. return this.toCanvas(this.locationOnDiagram(con));
  329. }
  330. toCanvas(point:Point): Point {
  331. return new Point(point.x + this.editor.margin, point.y + this.editor.margin);
  332. }
  333. findComp(x:number, y:number): Comp {
  334. var rtn = this.tryFindComp(x, y, this.template.frame);
  335. if(!rtn) {
  336. for(const con of this.editor.libertyCons){
  337. rtn = this.tryFindComp(x, y, con);
  338. if(rtn)
  339. break;
  340. }
  341. }
  342. return rtn;
  343. }
  344. private tryFindComp(x:number, y:number, target:Comp): Comp {
  345. if(this.containsPoint(target, x, y)){
  346. if(target.compType == 'container') {
  347. for(const item of (<Container>target).children) {
  348. const c = this.tryFindComp(x, y, item);
  349. if(c != null)
  350. return c;
  351. }
  352. }
  353. return target;
  354. }
  355. return null;
  356. }
  357. containsPoint(con:Comp, x:number, y:number):boolean{
  358. if(con.location){
  359. var point;
  360. if(con.position && con.position.absolute)
  361. point = this.toCanvas(con.location)
  362. else
  363. point = this.locationOnCanvas(con);
  364. return x >= point.x && y >= point.y && x <= point.x + con.size.x && y <= point.y + con.size.y;
  365. }
  366. return false;
  367. }
  368. findMainPipe(x:number, y:number): any {
  369. if(this.template.mainPipes) {
  370. for(const mp of this.template.mainPipes){
  371. for(const line of mp.locationPath) {
  372. var pre: Point = null;
  373. for(var point of line) {
  374. point = this.toCanvas(point);
  375. if(pre != null) {
  376. var x1:number;
  377. var y1:number;
  378. var x2:number;
  379. var y2:number;
  380. const scope = 3;
  381. if(pre.y == point.y) { //水平
  382. y1 = pre.y - scope;
  383. y2 = pre.y + scope;
  384. x1 = Math.min(pre.x, point.x);
  385. x2 = Math.max(pre.x, point.x);
  386. } else { //垂直
  387. x1 = pre.x - scope;
  388. x2 = pre.x + scope;
  389. y1 = Math.min(pre.y, point.y);
  390. y2 = Math.max(pre.y, point.y);
  391. }
  392. if(x >= x1 && y >= y1 && x <= x2 && y <= y2)
  393. return mp;
  394. }
  395. pre = point;
  396. }
  397. }
  398. }
  399. }
  400. return null;
  401. }
  402. }