1/*
2 * Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16const { Scr } = require('../engine/XDefine');
17const { XTools } = require('../engine/XTools');
18const { XButton } = require('../engine/control/XButton');
19const { XScroll } = require('../engine/control/XScroll');
20const { XSelect } = require('../engine/control/XSelect');
21const { X2DFast } = require('../engine/graphics/X2DFast');
22const { XTexture } = require('../engine/graphics/XTexture');
23const { CanvasInput } = require('./CanvasInput');
24const { IrToPicture } = require('./IrToPicture');
25const { LogParser } = require('./LogParser');
26const { NapiLog } = require('./NapiLog');
27
28const INTYPE_STR = ['state', 'depend', 'value', 'framestate', 'root', 'other'];
29
30class IrViewer {
31  constructor(fn, result) {
32    this.t1_ = Date.now();
33    this.fileName_ = fn;
34    this.parser_ = new LogParser(result);
35    this.inited_ = false;
36  }
37  InitViewer(result) {
38    this.data_ = result;
39    this.direct_ = null;
40    this.selectPoint_ = [];
41    this.visable_ = null;
42
43    this.loaded_ = false;
44
45    this.offx_ = Scr.logicw / 2;
46    this.offy_ = 30;
47
48    let tx = 10;
49    let ty = 10;
50    let files = Object.keys(this.data_);
51    this.selectFile_ = new XSelect(files, files[0]);
52    this.selectFile_.move(tx, ty, 550, 20);
53    this.selectFile_.registCallback(this.changeFile.bind(this));
54
55    tx = 10;
56    ty += 30;
57    this.selectFunc_ = new XSelect([], '');
58    this.selectFunc_.move(tx, ty, 290, 20);
59    this.selectFunc_.registCallback(this.changeFunc.bind(this));
60
61    tx += 290 + 10;
62    this.selectMethod_ = new XSelect([], '');
63    this.selectMethod_.move(tx, ty, 250, 20);
64    this.selectMethod_.registCallback(this.changeMethod.bind(this));
65
66    tx = 10;
67    ty += 30;
68    this.btnGo_ = [];
69    this.mask_ = 0xffff;
70    for (let i = 0; i < INTYPE_STR.length; i++) {
71      let bname = INTYPE_STR[i] + '✔️'; //❌
72      let bw = X2DFast.gi().getTextWidth(bname, 14) + 6;
73      let btn = new XButton(tx, ty, bw, 20, bname);
74      btn.inTypeId_ = i;
75      btn.inTypeMask_ = 1;
76      btn.onClicked_ = () => {
77        btn.inTypeMask_ = 1 - btn.inTypeMask_;
78        btn.name_ = INTYPE_STR[btn.inTypeId_] + (btn.inTypeMask_ === 1 ? '✔️' : '❌');
79        this.mask_ = (this.mask_ & ~(1 << btn.inTypeId_)) | (btn.inTypeMask_ << btn.inTypeId_);
80        this.changeVisable();
81      };
82      this.btnGo_.push(btn);
83      tx += bw + 10;
84    }
85
86    tx = 10;
87    ty += 30;
88    let bms = [['隐藏选中(Hide selected)', () => { this.hideNode(0); }],
89    ['隐藏未选中(Hide unselected)', () => { this.hideNode(1); }],
90    ['显示隐藏(Show hide)', () => { this.hideNode(2); }],
91    ['选中前继(Select pred)', () => { this.selectNode(0); }],
92    ['选中后继(Select suc)', () => { this.selectNode(1); }],
93    ['刷新(refresh)', () => { this.freshNode(); }]];
94    for (let bm of bms) {
95      let bw = X2DFast.gi().getTextWidth(bm[0], 14) + 6;
96      let btn = new XButton(tx, ty, bw, 20, bm[0]);
97      btn.onClicked_ = bm[1];
98      this.btnGo_.push(btn);
99      tx += bw + 10;
100    }
101
102    this.btnGo_.push(this.selectFile_, this.selectFunc_, this.selectMethod_);
103    this.btnGo_.sort((a, b) => {
104      return b.posY_ - a.posY_;
105    });
106
107    this.scrollY_ = new XScroll({ type: 'right' });
108    this.scrollX_ = new XScroll({ type: 'button' });
109    this.scrollY_.move(Scr.logicw - 20, 100, 20, Scr.logich - 100 - 20);
110    this.scrollX_.move(20, Scr.logich - 20, Scr.logicw - 40, 20);
111
112    this.hideNodeIds_ = [];
113    this.pointFile_ = files[0];
114  }
115  freshNode() {
116    this.scrollY_.setBarOff(0);
117    this.scrollX_.setBarOff(0.5);
118    IrToPicture.resetPicture(this.visable_.nodes, this.direct_.type.startsWith('block:'));
119  }
120  hideNode(type) {
121    if (type === 0) { //隐藏选中
122      this.hideNodeIds_ = this.hideNodeIds_.concat(this.selectPoint_);
123    }
124    else if (type === 1) { //隐藏未选中
125      let nodes = this.visable_.nodes;
126      for (let k in nodes) {
127        if (this.selectPoint_.indexOf(parseInt(nodes[k].ir.id)) >= 0) {
128          continue;
129        }
130        this.hideNodeIds_.push(parseInt(nodes[k].ir.id));
131      }
132    }
133    else { //显示所有
134      this.hideNodeIds_ = [];
135    }
136    this.changeVisable();
137  }
138  selectNode(type) {
139    let sel = new Set();
140    let nodes = this.visable_.nodes;
141    let lines = this.visable_.lines;
142    let hideChanged = false;
143    for (let l of lines) {
144      let n1 = nodes[l.fromId];
145      let n2 = nodes[l.toId];
146
147      let id1 = parseInt(n1.ir.id);
148      let id2 = parseInt(n2.ir.id);
149      let idx = -1;
150      if (type === 0 && (n1.mask & this.mask_) !== 0 && this.selectPoint_.indexOf(id2) >= 0) { //选中前继
151        idx = this.hideNodeIds_.indexOf(id1);
152        sel.add(id1);
153      }
154      if (type === 1 && (n2.mask & this.mask_) !== 0 && this.selectPoint_.indexOf(id1) >= 0) { //选中后继
155        idx = this.hideNodeIds_.indexOf(id2);
156        sel.add(id2);
157      }
158      if (idx >= 0) {
159        this.hideNodeIds_.splice(idx, 1);
160        hideChanged = true;
161      }
162    }
163    this.selectPoint_ = [...sel];
164    if (hideChanged) {
165      this.changeVisable();
166    }
167  }
168  loading() {
169    if (this.loaded_) {
170      return false;
171    }
172    if (this.parser_.parsing()) {
173      return true;
174    }
175    if (!this.inited_) {
176      this.inited_ = true;
177      this.InitViewer(this.parser_.output_);
178      return true;
179    }
180    let total = 1;
181    let procto = 1;
182    let loadonce = 1;
183    for (let file in this.data_) {
184      for (let func in this.data_[file]) {
185        for (let method of this.data_[file][func]) {
186          total++;
187          if (method.loaded) {
188            procto++;
189            continue;
190          }
191          if (loadonce <= 0) {
192            continue;
193          }
194          loadonce--;
195
196          method.irAll = IrToPicture.toPicture(method.irList, 1, method.type.startsWith('block:'));
197          method.loaded = true;
198        }
199      }
200    }
201    if (loadonce === 0) {
202      XTools.PROC_TO = 20 + procto / total * 80;
203      return true;
204    }
205    XTools.PROC_TO = 100;
206    this.loaded_ = true;
207    this.changeFile(this.pointFile_);
208    NapiLog.logInfo('load cost', Date.now() - this.t1_);
209    return true;
210  }
211  changeFile(name) {
212    this.pointFile_ = name;
213    let funcs = Object.keys(this.data_[this.pointFile_]);
214    this.selectFunc_.resetList(funcs, funcs[0]);
215    this.changeFunc(funcs[0]);
216  }
217  changeFunc(name) {
218    this.pointFunc_ = name;
219    let methods = [];
220    for (let i = 0; i < this.data_[this.pointFile_][this.pointFunc_].length; i++) {
221      methods.push((i + 1) + ',' + this.data_[this.pointFile_][this.pointFunc_][i].type);
222    }
223    this.selectMethod_.resetList(methods, methods[0]);
224    this.changeMethod(methods[0]);
225  }
226  changeMethod(name) {
227    this.pointMethod_ = name;
228    let p = parseInt(name.split(',')[0]) - 1;
229    this.direct_ = this.data_[this.pointFile_][this.pointFunc_][p];
230    this.changeVisable();
231  }
232  changeVisable() {
233    this.visable_ = this.direct_.irAll;
234    let nodes = this.visable_.nodes;
235    let lines = this.visable_.lines;
236
237    let showNodes = [];
238    for (let k in nodes) {
239      let n = nodes[k];
240      if (this.hideNodeIds_.indexOf(parseInt(n.ir.id)) >= 0) {
241        n.hide = true;
242      }
243      else {
244        n.hide = (n.mask & this.mask_) === 0;
245        if (!n.hide) {
246          showNodes.push(k);
247        }
248      }
249    }
250    for (let k of showNodes) {
251      let n = nodes[k];
252      for (let i = 0; i < 5; i++) {
253        if ((this.mask_ & (1 << i) !== 0) && (n.mask & (1 << i) !== 0)) { //进入点也加进来
254          for (let id of n.ir.in[i]) {
255            nodes[id].hide = false;
256          }
257        }
258      }
259    }
260    for (let k in nodes) {
261      let n = nodes[k];
262      if (this.hideNodeIds_.indexOf(parseInt(n.ir.id)) >= 0) {
263        n.hide = true;
264      }
265    }
266    this.scrollY_.setBarOff(0);
267    this.scrollX_.setBarOff(0.5);
268  }
269  makeLevely(nodes) {
270    let levely = new Set();
271    for (let k in nodes) {
272      let n = nodes[k];
273      if (n.hide) {
274        continue;
275      }
276      if (n.deep !== IrToPicture.INVALID_DEEP) {
277        levely.add(n.pos.y);
278      }
279    }
280    return Array.from(levely).sort((a, b) => { return parseInt(a) - parseInt(b) });
281  }
282  drawSmallMap(nodes, x1, x2, y1, y2) {
283    if (x1 === x2 || y2 === y1) {
284      return;
285    }
286    let [tx, ty, w, h] = this.smallMapRect;
287    X2DFast.gi().fillRect(tx, ty, w, h, 0x80000000);
288
289    let sw = w / (x2 - x1);
290    let sh = h / (y2 - y1);
291
292    let dh = Math.max(20 * sh, 1);
293    for (let k in nodes) { //画节点
294      let n = nodes[k];
295      if (n.hide) {
296        continue;
297      }
298      let dx = n.pos.x - x1;
299      let dy = n.pos.y - y1;
300      let dw = Math.max((n.nameWidth + 6) * sw, 1);
301      if (this.selectPoint_.indexOf(parseInt(k)) >= 0) {
302        if (this.drapSelect_) {
303          dx += this.drapSelect_.dx;
304          dy += this.drapSelect_.dy;
305        }
306        X2DFast.gi().fillRect(tx + (dx - 3) * sw, ty + (dy - 10) * sh, dw, dh, 0xff000000);
307      }
308      else {
309        let selectWith = false;
310        for (let inl of n.in) {
311          if (this.selectPoint_.indexOf(parseInt(inl.fromId)) >= 0) {
312            selectWith = true;
313            break;
314          }
315        }
316        if (!selectWith) {
317          for (let outl of n.out) {
318            if (this.selectPoint_.indexOf(parseInt(outl.toId)) >= 0) {
319              selectWith = true;
320              break;
321            }
322          }
323        }
324        if (selectWith) {
325          X2DFast.gi().fillRect(tx + (dx - 3) * sw, ty + (dy - 10) * sh, dw, dh, 0xff000000);
326        }
327        else {
328          X2DFast.gi().fillRect(tx + (dx - 3) * sw, ty + (dy - 10) * sh, dw, dh, XTools.CONFIG.NodeColor[n.type]);
329        }
330      }
331    }
332    X2DFast.gi().drawRect(tx - (this.offx_ + x1) * sw, ty - (this.offy_ + y1) * sh, Math.min(Scr.logicw * sw, w), Math.min(Scr.logich * sh, h), 0xff00ff00, 1);
333  }
334  onDraw() {
335    if (this.loading()) {
336      X2DFast.gi().drawText('Loading ' + XTools.PROC_TO.toFixed(1) + '%', 20, Scr.logicw / 2, Scr.logich / 2, 1, 1, 0, -2, -2, 0xff000000);
337      return;
338    }
339    let smallMapSize = parseInt(Math.min(Scr.logicw / 3, Scr.logich / 3));
340    this.smallMapRect = [Scr.logicw - 50 - smallMapSize, 50, smallMapSize, smallMapSize];
341    let nodes = this.visable_.nodes;
342    let lines = this.visable_.lines;
343    let levely = this.makeLevely(nodes);
344    let maxx = -9999;
345    let minx = 9999;
346    let mouseOn = -1;
347    let collect = {
348      singleCount: 0,
349      showCount: 0,
350      nodeCount: Object.keys(nodes).length,
351    };
352    for (let k in nodes) {
353      let n = nodes[k];
354      if (n.hide) {
355        continue;
356      }
357      collect.showCount++;
358      if (n.deep !== IrToPicture.INVALID_DEEP) {
359        collect.singleCount++;
360        if (maxx < n.pos.x + n.nameWidth + this.offx_) {
361          maxx = n.pos.x + n.nameWidth + this.offx_;
362        }
363        if (minx > n.pos.x + this.offx_) {
364          minx = n.pos.x + this.offx_;
365        }
366      }
367      if (XTools.InRect(XTools.MOUSE_POS.x, XTools.MOUSE_POS.y, n.pos.x + this.offx_ - 3, n.pos.y + this.offy_ - 10, n.nameWidth + 6, 20)) {
368        mouseOn = k;
369      }
370      n.outhx = {};
371    }
372    this.selectLines_ = [];
373    let mmx1 = this.drawLines(this.offx_, this.offy_, nodes, lines, levely, [minx - 20, maxx + 20], false); //未选中的线
374    for (let k in nodes) { //画节点
375      let n = nodes[k];
376      if (n.deep === IrToPicture.INVALID_DEEP) {
377        if (n.pos.x === IrToPicture.INVALID_DEEP) {
378          n.pos.x = mmx1[1] - this.offx_ + 20;
379        }
380      }
381      if (n.hide) {
382        continue;
383      }
384      let dx = n.pos.x + this.offx_;
385      let dy = n.pos.y + this.offy_;
386      if (this.selectPoint_.indexOf(parseInt(k)) >= 0) {
387        if (this.drapSelect_) {
388          dx += this.drapSelect_.dx;
389          dy += this.drapSelect_.dy;
390        }
391        X2DFast.gi().fillRect(dx - 3, dy - 10, n.nameWidth + 6, 20, 0xffffff00);
392        X2DFast.gi().drawRect(dx - 3, dy - 10, n.nameWidth + 6, 20, 0xff000000, 2);
393      }
394      else {
395        X2DFast.gi().fillRect(dx - 3, dy - 10, n.nameWidth + 6, 20, XTools.CONFIG.NodeColor[n.type]);
396        let selectWith = false;
397        for (let inl of n.in) {
398          if (this.selectPoint_.indexOf(parseInt(inl.fromId)) >= 0) {
399            selectWith = true;
400            break;
401          }
402        }
403        if (!selectWith) {
404          for (let outl of n.out) {
405            if (this.selectPoint_.indexOf(parseInt(outl.toId)) >= 0) {
406              selectWith = true;
407              break;
408            }
409          }
410        }
411        if (selectWith) {
412          X2DFast.gi().drawRect(dx - 3, dy - 10, n.nameWidth + 6, 20, 0xff000000, 2);
413        }
414      }
415      X2DFast.gi().drawText(n.name, 14, dx + n.nameWidth / 2, dy + 2, 1, 1, 0, -2, -2, 0xff000000);
416    }
417    this.drawLines(this.offx_, this.offy_, nodes, lines, levely, [minx - 20, maxx + 20], true); //选中的线
418    for (let ln of this.selectLines_) {
419      let [r, g, b, a] = XTexture.ExpandColor(ln[4]);
420      r = Math.max(0, r * 255 - 32);
421      g = Math.max(0, g * 255 - 32);
422      b = Math.max(0, b * 255 - 32);
423      this.drawLine(ln[0], ln[1], ln[2], ln[3], 0xff000000 | (r << 16) | (g << 8) | b, ln[5] + 1);
424    }
425    if (mouseOn >= 0) {
426      let n = nodes[mouseOn]; //显示选中节点的信息
427      let w = n.ir.maxDetailWidth + 2;
428      let h = n.ir.detailList.length * 16 + 2;
429      let x = XTools.MOUSE_POS.x - w;
430      let y = XTools.MOUSE_POS.y - h;
431      if (x < 10) {
432        x = 10;
433      }
434      if (y < 130) {
435        y = 130;
436      }
437
438      X2DFast.gi().fillRect(x, y, w, h, (XTools.CONFIG.NodeColor[n.type] & 0xffffff) | 0xC0000000);
439
440      for (let i = 0; i < n.ir.detailList.length; i++) {
441        X2DFast.gi().drawText(n.ir.detailList[i], 14, x + 1, y + 1 + i * 16, 1, 1, 0, -1, -1, 0xff000000);
442      }
443    }
444
445    for (let btn of this.btnGo_) {
446      btn.draw();
447    }
448
449    let x1 = 9999;
450    let y1 = 9999;
451    let x2 = -9999;
452    let y2 = -9999;
453    for (let k in nodes) {
454      let n = nodes[k];
455      if (n.hide) {
456        continue;
457      }
458      if (n.pos.x < x1) {
459        x1 = n.pos.x;
460      }
461      if (n.pos.x + n.nameWidth > x2) {
462        x2 = n.pos.x + n.nameWidth;
463      }
464
465      if (n.pos.y < y1) {
466        y1 = n.pos.y;
467      }
468      if (n.pos.y + n.nameWidth > y2) {
469        y2 = n.pos.y + IrToPicture.NODEH;
470      }
471    }
472    x1 = Math.min(mmx1[0] - this.offx_, x1) - Scr.logicw / 3;
473    x2 = Math.max(mmx1[1] - this.offx_, x2) + Scr.logicw / 3;
474    y1 = y1 - Scr.logich / 3;
475    y2 = y2 + Scr.logich / 3;
476    this.dragScoll = {
477      x1: x1,
478      x2: x2,
479      y1: y1,
480      y2: y2,
481    };
482    let scrollW = x2 - x1;
483    let scrollH = y2 - y1;
484    this.dragScoll.hh = scrollH - Scr.logich;
485    this.dragScoll.ww = scrollW - Scr.logicw;
486    if (this.dragScoll.hh < 1) {
487      this.dragScoll.hh = 1;
488    }
489    if (this.dragScoll.ww < 1) {
490      this.dragScoll.ww = 1;
491    }
492    if (this.drapBackground_) {
493      this.scrollY_.setBarOff(-(this.offy_ + this.dragScoll.y1) / this.dragScoll.hh);
494      this.scrollX_.setBarOff(-(this.offx_ + this.dragScoll.x1) / this.dragScoll.ww);
495    }
496    else {
497      this.offy_ = (-this.scrollY_.getBarOff()) * this.dragScoll.hh - this.dragScoll.y1;
498      this.offx_ = (-this.scrollX_.getBarOff()) * this.dragScoll.ww - this.dragScoll.x1;
499    }
500    if (this.dragScoll.hh > 1) {
501      this.scrollY_.move(Scr.logicw - 20, 100, 20, Scr.logich - 100 - 20).draw();
502    }
503    if (this.dragScoll.ww > 1) {
504      this.scrollX_.move(20, Scr.logich - 20, Scr.logicw - 40, 20).draw();
505    }
506
507    this.drawSmallMap(nodes, x1, x2, y1, y2);
508
509    if (this.searchInput) {
510      let x = this.searchInput.pos[0];
511      let y = this.searchInput.pos[1];
512      let w = this.searchInput.pos[2];
513      let h = this.searchInput.pos[3];
514      X2DFast.gi().fillRect(x, y, w, h, 0x80000000);
515
516      let searchResultTxt =
517        this.searchInput.result.length === 0 ? '0/0' :
518          this.searchInput.point + 1 + '/' + this.searchInput.result.length;
519
520      this.searchInput.btnUp.move(x + 20, y + 50, 32, 24).draw();
521
522      X2DFast.gi().drawText(
523        searchResultTxt,
524        20,
525        x + w / 2,
526        y + 50 + 12,
527        1,
528        1,
529        0,
530        -2,
531        -2,
532        0xffffffff
533      ) + 16;
534
535      this.searchInput.btnDown.move(x + w - 20 - 32, y + 50, 32, 24).draw();
536      this.searchInput.btnClose.move(x + w - 40, y + 10, 30, 30).draw();
537    }
538  }
539  checkLevel(levely, n1, n2) {
540    let i1 = levely.indexOf(n1.pos.y);
541    let i2 = levely.indexOf(n2.pos.y);
542    return i1 + 1 === i2;
543  }
544  drawLines(offx, offy, nodes, lines, levely, mmx, select) {
545    let aaa = 5;
546    if (true) {
547      aaa = -5;
548      for (let l of lines) {
549        let n1 = nodes[l.fromId];
550        let n2 = nodes[l.toId];
551        if (n1.hide || n2.hide) {
552          continue;
553        }
554
555        let lor = n1.pos.x + n2.pos.x < -50 ? 0 : 1;
556        if (this.checkLevel(levely, n1, n2)) { }
557        else {
558          if (!(n1.outh[l.outNum] in n1.outhx)) {
559            mmx[lor] += lor === 0 ? aaa : -aaa;
560            n1.outhx[n1.outh[l.outNum]] = mmx[lor];
561          }
562        }
563      }
564    }
565    let mmx1 = [mmx[0], mmx[1]];
566    for (let l of lines) {
567      let n1 = nodes[l.fromId];
568      let n2 = nodes[l.toId];
569      if (n1.hide || n2.hide) {
570        continue;
571      }
572
573      let x1 = n1.pos.x + n1.nameWidth - 5 + offx - n1.ltypes.indexOf(l.lineType) * 5;
574      let y1 = n1.pos.y + 10 + offy;
575      let x2 = n2.pos.x + n2.nameWidth - 5 + offx - l.inNum * 5;
576      let y2 = n2.pos.y - 10 + offy;
577      let lor = n1.pos.x + n2.pos.x < -50 ? 0 : 1;
578
579      let selected = false;
580      if (this.selectPoint_.indexOf(l.fromId) >= 0 || this.selectPoint_.indexOf(l.toId) >= 0) {
581        selected = true;
582        if (this.drapSelect_) {
583          if (this.selectPoint_.indexOf(l.fromId) >= 0) {
584            x1 += this.drapSelect_.dx;
585            y1 += this.drapSelect_.dy;
586          }
587          if (this.selectPoint_.indexOf(l.toId) >= 0) {
588            x2 += this.drapSelect_.dx;
589            y2 += this.drapSelect_.dy;
590          }
591        }
592      }
593
594      if (select !== selected) {
595        if (this.checkLevel(levely, n1, n2)) { }
596        else {
597          mmx[lor] += lor === 0 ? -aaa : aaa;
598        }
599        continue;
600      }
601
602      let c = 0xffc0c0c0;
603      let lw = 1;
604
605      if (selected) { //选中的点进出的线使用指定的颜色,增加线宽
606        c = XTools.CONFIG.LineColor[l.lineType];
607        lw = 2;
608      }
609      let ls = [];
610      if (this.checkLevel(levely, n1, n2)) {
611        ls.push([x1, y1, x1, y1 + n1.outh[l.outNum], c, lw]);
612        ls.push([x1, y1 + n1.outh[l.outNum], x2, y1 + n1.outh[l.outNum], c, lw]);
613        ls.push([x2, y1 + n1.outh[l.outNum], x2, y2, c, lw]);
614      }
615      else {
616        let lx = n1.outhx[n1.outh[l.outNum]]; //n1.outhx[l.outNum] 或 mmx[lor]
617        let ly = n2.inh[l.fromId + l.lineType]; //n2.inh[l.inNum] 或 n2.inh[n1.ir.id]
618
619        ls.push([x1, y1, x1, y1 + n1.outh[l.outNum], c, lw]);
620        ls.push([x1, y1 + n1.outh[l.outNum], lx, y1 + n1.outh[l.outNum], c, lw]);
621        ls.push([lx, y1 + n1.outh[l.outNum], lx, y2 - ly, c, lw]);
622        ls.push([lx, y2 - ly, x2, y2 - ly, c, lw]);
623        ls.push([x2, y2 - ly, x2, y2, c, lw]);
624        mmx[lor] += lor === 0 ? -aaa : aaa;
625      }
626      let mouseOn = false;
627      for (let ln of ls) {
628        mouseOn |= this.drawLine(...ln);
629      }
630      if (mouseOn) {
631        this.selectLines_.push(...ls);
632      }
633    }
634    return [Math.min(mmx1[0], mmx[0]), Math.max(mmx1[1], mmx[1])];
635  }
636  drawLine(x1, y1, x2, y2, c, lw = 1) {
637    if (x1 === x2) {
638      if (y1 > y2) {
639        [y1, y2] = [y2, y1];
640      }
641      X2DFast.px2f.fillRect(x1, y1, lw, y2 - y1 + lw, c);
642      if (XTools.InRect(XTools.MOUSE_POS.x, XTools.MOUSE_POS.y, x1 - 1, y1, lw + 2, y2 - y1)) {
643        return true;
644      }
645    }
646    else if (y1 === y2) {
647      if (x1 > x2) {
648        [x1, x2] = [x2, x1];
649      }
650      X2DFast.px2f.fillRect(x1, y1, x2 - x1, lw, c);
651      if (XTools.InRect(XTools.MOUSE_POS.x, XTools.MOUSE_POS.y, x1, y1 - 1, x2 - x1, lw + 2)) {
652        return true;
653      }
654    }
655    else {
656
657    }
658    return false;
659  }
660  locateNode(p) {
661    this.selectPoint_ = [parseInt(p)];
662    let nodes = this.visable_.nodes;
663    let n = nodes[p];
664
665    this.offx_ = Scr.logicw / 2 - n.pos.x;
666    this.offy_ = Scr.logich / 2 - n.pos.y;
667    this.scrollY_.setBarOff(-(this.offy_ + this.dragScoll.y1) / this.dragScoll.hh);
668    this.scrollX_.setBarOff(-(this.offx_ + this.dragScoll.x1) / this.dragScoll.ww);
669    this.offy_ = (-this.scrollY_.getBarOff()) * this.dragScoll.hh - this.dragScoll.y1;
670    this.offx_ = (-this.scrollX_.getBarOff()) * this.dragScoll.ww - this.dragScoll.x1;
671  }
672  findNext() {
673    if (this.searchInput) {
674      this.searchInput.point += 1;
675      if (this.searchInput.point >= this.searchInput.result.length) {
676        this.searchInput.point = 0;
677      }
678      this.locateNode(this.searchInput.result[this.searchInput.point]);
679    }
680  }
681  resetOffset(x, y) {
682    let [tx, ty, w, h] = this.smallMapRect;
683    let [x1, y1, x2, y2] = [this.dragScoll.x1, this.dragScoll.y1, this.dragScoll.x2, this.dragScoll.y2];
684    if (x1 === x2 || y1 === y2) {
685      return;
686    }
687    let sw = w / (x2 - x1);
688    let sh = h / (y2 - y1);
689    this.offx_ = (tx - x + Scr.logicw * sw / 2) / sw - x1;
690    this.offy_ = (ty - y + Scr.logich * sh / 2) / sh - y1;
691    this.scrollY_.setBarOff(-(this.offy_ + this.dragScoll.y1) / this.dragScoll.hh);
692    this.scrollX_.setBarOff(-(this.offx_ + this.dragScoll.x1) / this.dragScoll.ww);
693    this.offy_ = (-this.scrollY_.getBarOff()) * this.dragScoll.hh - this.dragScoll.y1;
694    this.offx_ = (-this.scrollX_.getBarOff()) * this.dragScoll.ww - this.dragScoll.x1;
695  }
696
697  checkMsgAndDrapSelect_(msg, x, y){
698    if (msg === 3 && this.drapSelect_) {
699      let nodes = this.visable_.nodes;
700      for (let k of this.selectPoint_) {
701        nodes[k].pos.x += this.drapSelect_.dx;
702        nodes[k].pos.y += this.drapSelect_.dy;
703      }
704      this.drapSelect_ = null;
705    }
706  }
707
708  checkDrapBackground_(msg, x, y){
709    if (this.drapBackground_) {
710      if (msg === 2) {
711        this.offx_ -= this.drapBackground_.x - x;
712        this.offy_ -= this.drapBackground_.y - y;
713        this.drapBackground_.x = x;
714        this.drapBackground_.y = y;
715      }
716      return true;
717    }
718  }
719
720  checkDrapSelect_(msg, x, y){
721    if (this.drapSelect_) {
722      if (msg === 2) {
723        if (Math.abs(this.drapSelect_.x - x) > 10 ||
724          Math.abs(this.drapSelect_.y - y) > 10 ||
725          this.drapSelect_.dx !== 0 ||
726          this.drapSelect_.dy !== 0) {
727          this.drapSelect_.dx -= this.drapSelect_.x - x;
728          this.drapSelect_.dy -= this.drapSelect_.y - y;
729          this.drapSelect_.x = x;
730          this.drapSelect_.y = y;
731        }
732      }
733      return true;
734    }
735  }
736
737  checkSearchInput(msg, x, y){
738    if (this.searchInput) {
739      if (XTools.InRect(x, y, ...this.searchInput.pos)) {
740        if (this.searchInput.btnUp.onTouch(msg, x, y)) {
741          if (this.searchInput.btnUp.isClicked()) {
742            this.searchInput.point -= 1;
743            if (this.searchInput.point < 0) {
744              this.searchInput.point = this.searchInput.result.length - 1;
745            }
746            this.locateNode(this.searchInput.result[this.searchInput.point]);
747          }
748        }
749        if (this.searchInput.btnDown.onTouch(msg, x, y)) {
750          if (this.searchInput.btnDown.isClicked()) {
751            this.findNext();
752          }
753        }
754        if (this.searchInput.btnClose.onTouch(msg, x, y)) {
755          if (this.searchInput.btnClose.isClicked()) {
756            this.searchInput = null;
757            CanvasInput.HideEx();
758          }
759        }
760        return true;
761      }
762    }
763  }
764
765  checkMsg(msg, x, y){
766    if (msg === 1) {
767      let nodes = this.visable_.nodes;
768      for (let k in nodes) {
769        let n = nodes[k];
770        if (n.hide) {
771          continue;
772        }
773        if (XTools.InRect(x, y, n.pos.x + this.offx_ - 3, n.pos.y + this.offy_ - 10, n.nameWidth + 6, 20)) {
774          if (XTools.KEY_CTRL) {
775            this.selectPoint_.push(parseInt(k));
776          }
777          else {
778            if (this.selectPoint_.indexOf(parseInt(k)) < 0) {
779              this.selectPoint_ = [parseInt(k)];
780            }
781          }
782          this.drapSelect_ = {
783            x: x,
784            y: y,
785            dx: 0,
786            dy: 0,
787          };
788          return true;
789        }
790      }
791      this.selectPoint_ = [];
792    }
793  }
794
795  onTouch(msg, x, y) {
796    if (this.loading()) {
797      return true;
798    }
799    if (this.smallMapLocked_) {
800      if (msg === 2) {
801        this.resetOffset(x, y);
802      }
803      if (msg === 3) {
804        this.smallMapLocked_ = false;
805      }
806      return true;
807    }
808    if (msg === 6) {
809      this.drapBackground_ = null;
810    }
811    this.checkMsgAndDrapSelect_(msg, x, y)
812    this.checkDrapBackground_(msg, x, y)
813    this.checkDrapSelect_(msg, x, y)
814    if (this.scrollX_.onTouch(msg, x, y)) {
815      return true;
816    }
817    if (this.scrollY_.onTouch(msg, x, y)) {
818      return true;
819    }
820    if (XTools.InRect(x, y, ...this.smallMapRect)) {
821      if (msg === 1) {
822        this.resetOffset(x, y);
823        this.smallMapLocked_ = true;
824      }
825      return true;
826    }
827    this.checkSearchInput(msg, x, y)
828    for (let i = this.btnGo_.length - 1; i >= 0; i--) {
829      if (this.btnGo_[i].onTouch(msg, x, y)) {
830        return true;
831      }
832    }
833    this.checkMsg(msg, x, y)
834    if (msg === 4) {
835      this.drapBackground_ = {
836        x: x,
837        y: y,
838      };
839    }
840    return false;
841  }
842  onKey(k) {
843    if (this.loading()) {
844      return true;
845    }
846    if (this.searchInput) {
847      return true;
848    }
849    switch (k) {
850      case 'PageUp':
851        this.selectNode(0);
852        return true;
853      case 'PageDown':
854        this.selectNode(1);
855        return true;
856      case 'H':
857      case 'h':
858        this.hideNode(0);
859        return true;
860      case ' ':
861        this.hideNode(1);
862        return true;
863      case 'S':
864      case 's':
865        this.hideNode(2);
866        return true;
867      case 'Enter':
868        this.freshNode();
869        return true;
870    }
871    if (k === 'ctrl+f' || k === 'ctrl+F') {
872      this.searchInput = {
873        pos: [(Scr.logicw - 300), Scr.logich / 2, 200, 80],
874        result: [],
875        point: 0,
876        btnUp: new XButton(0, 0, 0, 0, '<'),
877        btnDown: new XButton(0, 0, 0, 0, '>'),
878        btnClose: new XButton(0, 0, 0, 0, '❌'),
879      };
880      let x = this.searchInput.pos[0];
881      let y = this.searchInput.pos[1];
882      let w = this.searchInput.pos[2];
883      let h = this.searchInput.pos[3];
884      this.searchInput.Open = () => {
885        CanvasInput.Reset(x, y + 10, w - 32 - 40, 32, '', null, (v) => {
886          function isRegExp(s) {
887            try {
888              new RegExp(s);
889              return true;
890            } catch (e) {
891              return false;
892            }
893          }
894          this.searchInput.result = [];
895          if (v.length > 0) {
896            let nodes = this.visable_.nodes;
897            this.selectPoint_ = [];
898            for (let i in nodes) {
899              let n = nodes[i];
900              let searchName;
901              if (XTools.CONFIG.OpTypeJsBytecode.indexOf(n.ir.op) >= 0) {
902                searchName = n.ir.bytecode;
903              }
904              else if (n.ir.typedop) {
905                searchName = n.ir.typedop;
906              }
907              else {
908                searchName = n.ir.op;
909              }
910              if (n.ir.id === v || searchName.indexOf(v) >= 0 || (isRegExp(v) && searchName.match(v))) {
911                this.searchInput.result.push(i);
912              }
913            }
914            if (this.searchInput.result.length > 0) {
915              this.locateNode(this.searchInput.result[0]);
916              this.searchInput.point = 0;
917            }
918          }
919        }, this.findNext.bind(this));
920      };
921      CanvasInput.SetSafeArea(...this.searchInput.pos);
922      this.searchInput.Open();
923    }
924    return false;
925  }
926}
927
928module.exports = {
929  IrViewer
930};