1/*
2 * Copyright (C) 2022 Huawei Device 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
16import { TraceRow } from '../../component/trace/base/TraceRow';
17import {
18  BaseStruct,
19  computeUnitWidth,
20  drawLoadingFrame,
21  isSurroundingPoint,
22  ns2x,
23  Rect,
24  Render,
25} from './ProcedureWorkerCommon';
26import { type AnimationRanges } from '../../bean/FrameComponentBean';
27import { ColorUtils } from '../../component/trace/base/ColorUtils';
28import { SpSystemTrace } from '../../component/SpSystemTrace';
29
30export class FrameSpacingRender extends Render {
31  renderMainThread(
32    req: {
33      useCache: boolean;
34      context: CanvasRenderingContext2D;
35      type: string;
36      frameRate: number;
37      animationRanges: AnimationRanges[];
38    },
39    row: TraceRow<FrameSpacingStruct>
40  ): void {
41    let frameSpacingList = row.dataList;
42    let frameSpacingFilter = row.dataListCache;
43    this.frameSpacing(
44      frameSpacingList,
45      frameSpacingFilter,
46      TraceRow.range!.startNS,
47      TraceRow.range!.endNS,
48      TraceRow.range!.totalNS,
49      row,
50      req.animationRanges,
51      req.useCache || !TraceRow.range!.refresh
52    );
53    drawLoadingFrame(req.context, row.dataListCache, row);
54    this.render(req, frameSpacingList, row);
55  }
56
57  private render(
58    req: {
59      useCache: boolean;
60      context: CanvasRenderingContext2D;
61      type: string;
62      frameRate: number;
63      animationRanges: AnimationRanges[];
64    },
65    frameSpacingFilter: Array<FrameSpacingStruct>,
66    row: TraceRow<FrameSpacingStruct>
67  ): void {
68    if (req.animationRanges.length > 0 && req.animationRanges[0] && frameSpacingFilter.length > 0) {
69      let minValue = 0;
70      let maxValue = 0;
71      let smallTickStandard = {
72        firstLine: 0,
73        secondLine: 0,
74        thirdLine: 0,
75      };
76      if (req.frameRate) {
77        // @ts-ignore
78        smallTickStandard = smallTick[req.frameRate];
79        [minValue, maxValue] = this.maxMinData(
80          smallTickStandard.firstLine,
81          smallTickStandard.thirdLine,
82          frameSpacingFilter
83        );
84      } else {
85        minValue = Math.min.apply(
86          Math,
87          frameSpacingFilter.map((filterData) => {
88            return filterData.frameSpacingResult!;
89          })
90        );
91        maxValue = Math.max.apply(
92          Math,
93          frameSpacingFilter.map((filterData) => {
94            return filterData.frameSpacingResult!;
95          })
96        );
97      }
98      let selectUnitWidth: number = 0;
99      this.drawTraceRow(frameSpacingFilter, selectUnitWidth, req, row, minValue, maxValue, smallTickStandard);
100      let findStructList = frameSpacingFilter.filter(
101        (filter) => row.isHover && isSurroundingPoint(row.hoverX, filter.frame!, selectUnitWidth / multiple)
102      );
103      this.setHoverStruct(findStructList, row);
104    }
105  }
106  private drawTraceRow(
107    frameSpacingFilter: Array<FrameSpacingStruct>,
108    selectUnitWidth: number,
109    req: any,
110    row: TraceRow<FrameSpacingStruct>,
111    minValue: number,
112    maxValue: number,
113    smallTickStandard: any
114  ) {
115    let preFrameSpacing: FrameSpacingStruct = frameSpacingFilter[0];
116    let isDraw = false;
117    for (let index: number = 0; index < frameSpacingFilter.length; index++) {
118      let currentStruct = frameSpacingFilter[index];
119      selectUnitWidth = computeUnitWidth(
120        preFrameSpacing.currentTs,
121        currentStruct.currentTs, // @ts-ignore
122        row.frame.width,
123        selectUnitWidth
124      );
125      FrameSpacingStruct.refreshHoverStruct(preFrameSpacing, currentStruct, row, minValue, maxValue);
126      if (currentStruct.groupId === 0) {
127        if (currentStruct.currentTs > TraceRow.range!.startNS && currentStruct.currentTs < TraceRow.range!.endNS) {
128          isDraw = true;
129          this.drawPoint(req.context, currentStruct, row, minValue, maxValue);
130        }
131      } else if (
132        currentStruct.groupId !== invalidGroupId &&
133        index > 0 &&
134        currentStruct.groupId === preFrameSpacing!.groupId
135      ) {
136        isDraw = true;
137        FrameSpacingStruct.draw(req.context, preFrameSpacing, currentStruct, row, minValue, maxValue);
138      }
139      FrameSpacingStruct.drawSelect(currentStruct, req.context, row);
140      preFrameSpacing = currentStruct;
141    }
142    if (req.frameRate) {
143      if (isDraw) {
144        this.drawDashedLines(Object.values(smallTickStandard), req, row, minValue, maxValue);
145      }
146    }
147  }
148  private setHoverStruct(findStructList: Array<FrameSpacingStruct>, row: TraceRow<FrameSpacingStruct>) {
149    let find = false;
150    if (findStructList.length > 0) {
151      find = true;
152      let hoverIndex: number = 0;
153      if (findStructList.length > unitIndex) {
154        hoverIndex = Math.ceil(findStructList.length / multiple);
155      }
156      FrameSpacingStruct.hoverFrameSpacingStruct = findStructList[hoverIndex];
157    }
158    if (!find && row.isHover) {
159      FrameSpacingStruct.hoverFrameSpacingStruct = undefined;
160    }
161  }
162
163  private drawPoint(
164    ctx: CanvasRenderingContext2D,
165    currentStruct: FrameSpacingStruct,
166    row: TraceRow<FrameSpacingStruct>,
167    minValue: number,
168    maxValue: number
169  ): void {
170    let currentPointY = // @ts-ignore
171      row.frame.height -
172      Math.floor(
173        // @ts-ignore
174        ((currentStruct.frameSpacingResult! - minValue) * (row.frame.height - padding * multiple)) /
175          (maxValue - minValue)
176      ) -
177      padding;
178    ctx.beginPath();
179    ctx.lineWidth = 1;
180    ctx.globalAlpha = 1;
181    ctx.arc(currentStruct.frame!.x, currentPointY, multiple, 0, multiple * Math.PI);
182    ctx.strokeStyle = ColorUtils.ANIMATION_COLOR[2];
183    ctx.fillStyle = ColorUtils.ANIMATION_COLOR[2];
184    ctx.stroke();
185    ctx.fill();
186    ctx.closePath();
187  }
188
189  private drawDashedLines(
190    dashedLines: number[],
191    req: { useCache: boolean; context: CanvasRenderingContext2D; type: string; frameRate: number },
192    row: TraceRow<FrameSpacingStruct>,
193    minVale: number,
194    maxValue: number
195  ): void {
196    for (let i = 0; i < dashedLines.length; i++) {
197      // @ts-ignore
198      FrameSpacingStruct.drawParallelLine(req.context, row.frame, dashedLines, i, minVale, maxValue);
199    }
200  }
201
202  private frameSpacing(
203    frameSpacingList: Array<FrameSpacingStruct>,
204    frameSpacingFilter: Array<FrameSpacingStruct>,
205    startNS: number,
206    endNS: number,
207    totalNS: number,
208    row: TraceRow<FrameSpacingStruct>,
209    animationRanges: AnimationRanges[],
210    use: boolean
211  ): void {
212    // @ts-ignore
213    let frame: Rect = row.frame;
214    let modelName: string | undefined | null = row.getAttribute('model-name');
215    if ((use || !TraceRow.range!.refresh) && frameSpacingFilter.length > 0) {
216      frameSpacingList.length = 0;
217      let groupIdList: number[] = [];
218      for (let index = 0; index < frameSpacingFilter.length; index++) {
219        let item = frameSpacingFilter[index];
220        if (modelName === item.nameId) {
221          item.groupId = invalidGroupId;
222          for (let rangeIndex = 0; rangeIndex < animationRanges.length; rangeIndex++) {
223            let currentRange = animationRanges[rangeIndex];
224            if (item.currentTs >= currentRange.start && item.currentTs <= currentRange.end) {
225              item.groupId = currentRange.start;
226              break;
227            }
228          }
229          if (
230            item.currentTs < startNS &&
231            index + unitIndex < frameSpacingFilter.length &&
232            frameSpacingFilter[index + unitIndex].currentTs >= startNS &&
233            item.groupId !== invalidGroupId
234          ) {
235            this.refreshFrame(frameSpacingList, item, startNS, endNS, totalNS, frame, groupIdList);
236          }
237          if (item.currentTs >= startNS && item.currentTs <= endNS && item.groupId !== invalidGroupId) {
238            this.refreshFrame(frameSpacingList, item, startNS, endNS, totalNS, frame, groupIdList);
239          }
240          if (item.currentTs > endNS && item.groupId !== invalidGroupId) {
241            this.refreshFrame(frameSpacingList, item, startNS, endNS, totalNS, frame, groupIdList);
242            break;
243          }
244        }
245      }
246      this.grouping(groupIdList, frameSpacingList);
247      return;
248    }
249  }
250
251  private grouping(groupIdList: number[], frameSpacingFilter: Array<FrameSpacingStruct>): void {
252    let simpleGroup = groupIdList.filter((groupId) => {
253      return groupId !== invalidGroupId && groupIdList.indexOf(groupId) === groupIdList.lastIndexOf(groupId);
254    });
255    frameSpacingFilter.forEach((frameSpacing) => {
256      if (simpleGroup.indexOf(frameSpacing.groupId!) > invalidGroupId) {
257        frameSpacing.groupId = 0;
258        frameSpacing.frameSpacingResult = 0;
259        frameSpacing.preFrameWidth = 0;
260        frameSpacing.preFrameHeight = 0;
261        frameSpacing.preTs = 0;
262        frameSpacing.preX = 0;
263        frameSpacing.preY = 0;
264      }
265    });
266  }
267
268  private refreshFrame(
269    frameSpacingFilter: Array<FrameSpacingStruct>,
270    item: FrameSpacingStruct,
271    startNS: number,
272    endNS: number,
273    totalNS: number,
274    frame: Rect,
275    groupIdList: number[]
276  ): void {
277    frameSpacingFilter.push(item);
278    FrameSpacingStruct.setFrameSpacingFrame(item, startNS, endNS, totalNS, frame);
279    groupIdList.push(item.groupId!);
280  }
281
282  private maxMinData(tickStandardMin: number, tickStandardMax: number, filter: FrameSpacingStruct[]): [number, number] {
283    let min = Math.min.apply(
284      Math,
285      filter.map((filterData) => {
286        return filterData.frameSpacingResult!;
287      })
288    );
289    let max = Math.max.apply(
290      Math,
291      filter.map((filterData) => {
292        return filterData.frameSpacingResult!;
293      })
294    );
295    if (max < tickStandardMax) {
296      max = tickStandardMax + padding;
297    }
298    if (min > tickStandardMin) {
299      min = tickStandardMin;
300    }
301    return [min, max];
302  }
303}
304export function FrameSpacingStructOnClick(
305  clickRowType: string,
306  sp: SpSystemTrace,
307  //@ts-ignore
308  row: TraceRow<unknown>,
309  entry?: FrameSpacingStruct,
310): Promise<unknown> {
311  return new Promise((resolve, reject) => {
312    if (clickRowType === TraceRow.ROW_TYPE_FRAME_SPACING) {
313      //@ts-ignore
314      FrameSpacingStruct.selectFrameSpacingStruct =
315        FrameSpacingStruct.hoverFrameSpacingStruct || row.getHoverStruct(false, true);
316      if (FrameSpacingStruct.selectFrameSpacingStruct || entry) {
317        let data = entry || FrameSpacingStruct.selectFrameSpacingStruct;
318        sp.traceSheetEL?.displayFrameSpacingData(data!);
319        sp.timerShaftEL?.modifyFlagList(undefined);
320      }
321      reject(new Error());
322    } else {
323      resolve(null);
324    }
325  });
326}
327export class FrameSpacingStruct extends BaseStruct {
328  static hoverFrameSpacingStruct: FrameSpacingStruct | undefined;
329  static selectFrameSpacingStruct: FrameSpacingStruct | undefined;
330  physicalWidth: number | undefined;
331  physicalHeight: number | undefined;
332  preTs: number | undefined;
333  currentTs: number = 0;
334  frameSpacingResult: number | undefined;
335  id: number = 0;
336  groupId: number | undefined;
337  currentFrameWidth: number | undefined;
338  preFrameWidth: number | undefined;
339  currentFrameHeight: number | undefined;
340  preFrameHeight: number | undefined;
341  x: number | undefined;
342  y: number | undefined;
343  preX: number | undefined;
344  preY: number | undefined;
345  nameId: string | undefined;
346
347  static setFrameSpacingFrame(
348    frameSpacingNode: FrameSpacingStruct,
349    startNS: number,
350    endNS: number,
351    totalNS: number,
352    row: Rect
353  ): void {
354    let pointX = ns2x(frameSpacingNode.currentTs || 0, startNS, endNS, totalNS, row);
355    if (!frameSpacingNode.frame) {
356      frameSpacingNode.frame = new Rect(0, 0, 0, 0);
357    }
358    frameSpacingNode.frame.x = Math.floor(pointX);
359  }
360
361  static isSelect(currSpacingStruct: FrameSpacingStruct): boolean | 0 | undefined {
362    return (
363      TraceRow.rangeSelectObject &&
364      TraceRow.rangeSelectObject.startNS &&
365      TraceRow.rangeSelectObject.endNS &&
366      currSpacingStruct.currentTs >= TraceRow.rangeSelectObject.startNS &&
367      currSpacingStruct.currentTs <= TraceRow.rangeSelectObject.endNS
368    );
369  }
370
371  static refreshHoverStruct(
372    preFrameSpacing: FrameSpacingStruct,
373    frameSpacing: FrameSpacingStruct,
374    row: TraceRow<FrameSpacingStruct>,
375    minValue: number,
376    maxValue: number
377  ): void {
378    if (frameSpacing.frame) {
379      frameSpacing.frame.y = // @ts-ignore
380        row.frame.height -
381        Math.floor(
382          // @ts-ignore
383          ((frameSpacing.frameSpacingResult! - minValue) * (row.frame.height - padding * multiple)) /
384            (maxValue - minValue)
385        ) -
386        padding;
387    }
388  }
389
390  static draw(
391    ctx: CanvasRenderingContext2D,
392    preFrameSpacing: FrameSpacingStruct,
393    currentStruct: FrameSpacingStruct,
394    rowFrame: TraceRow<FrameSpacingStruct>,
395    minValue: number,
396    maxValue: number
397  ): void {
398    if (currentStruct.frame && preFrameSpacing.frame) {
399      this.drawPolyline(ctx, preFrameSpacing, currentStruct, rowFrame, minValue, maxValue);
400    }
401  }
402
403  static drawSelect(
404    currentStruct: FrameSpacingStruct,
405    ctx: CanvasRenderingContext2D,
406    rowFrame: TraceRow<FrameSpacingStruct>
407  ): void {
408    if (
409      currentStruct.frame &&
410      currentStruct.currentTs > TraceRow.range!.startNS &&
411      currentStruct.currentTs < TraceRow.range!.endNS
412    ) {
413      if (
414        (currentStruct === FrameSpacingStruct.hoverFrameSpacingStruct && rowFrame.isHover) ||
415        currentStruct === FrameSpacingStruct.selectFrameSpacingStruct
416      ) {
417        ctx.lineWidth = 3;
418        ctx.beginPath();
419        ctx.arc(currentStruct.frame.x, currentStruct.frame.y, selectRadius, 0, multiple * Math.PI);
420        ctx.strokeStyle = ColorUtils.ANIMATION_COLOR[7];
421        ctx.lineWidth = 2;
422        ctx.globalAlpha = 1;
423        ctx.fillStyle = ColorUtils.ANIMATION_COLOR[9];
424        ctx.stroke();
425        ctx.fill();
426        ctx.closePath();
427      }
428    }
429    if (rowFrame.getAttribute('check-type') === '2' && FrameSpacingStruct.isSelect(currentStruct)) {
430      ctx.beginPath();
431      ctx.lineWidth = 3;
432      ctx.arc(currentStruct.frame!.x, currentStruct.frame!.y, selectRadius, 0, multiple * Math.PI);
433      ctx.strokeStyle = ColorUtils.ANIMATION_COLOR[7];
434      ctx.fillStyle = ColorUtils.ANIMATION_COLOR[9];
435      ctx.lineWidth = 2;
436      ctx.globalAlpha = 1;
437      ctx.stroke();
438      ctx.fill();
439      ctx.closePath();
440    }
441  }
442
443  static drawParallelLine(
444    ctx: CanvasRenderingContext2D,
445    rowFrame: Rect,
446    dashedLines: number[],
447    index: number,
448    minValue: number,
449    maxValue: number
450  ): void {
451    let pointY =
452      rowFrame.height -
453      Math.floor(((dashedLines[index] - minValue) * (rowFrame.height - padding * multiple)) / (maxValue - minValue)) -
454      padding;
455    let lineDash = 10;
456    let textPadding = 4;
457    ctx.beginPath();
458    ctx.lineWidth = 2;
459    ctx.setLineDash([lineDash]);
460    ctx.strokeStyle = ColorUtils.ANIMATION_COLOR[6];
461    ctx.fillStyle = ColorUtils.ANIMATION_COLOR[6];
462    ctx.moveTo(0, pointY);
463    ctx.lineTo(rowFrame.width, pointY);
464    ctx.stroke();
465    ctx.strokeStyle = ColorUtils.ANIMATION_COLOR[3];
466    ctx.fillStyle = ColorUtils.ANIMATION_COLOR[3];
467    if (index === 0) {
468      ctx.fillText(dashedLines[index].toString(), 0, pointY + multiple * textPadding);
469    } else if (index === unitIndex) {
470      ctx.fillText(dashedLines[index].toString(), 0, pointY + textPadding);
471    } else {
472      ctx.fillText(dashedLines[index].toString(), 0, pointY - textPadding);
473    }
474    ctx.closePath();
475  }
476
477  static drawPolyline(
478    ctx: CanvasRenderingContext2D,
479    preFrameSpacing: FrameSpacingStruct,
480    currentStruct: FrameSpacingStruct,
481    rowFrame: TraceRow<FrameSpacingStruct>,
482    minValue: number,
483    maxValue: number
484  ): void {
485    ctx.beginPath();
486    let prePointY = // @ts-ignore
487      rowFrame.frame.height -
488      Math.floor(
489        // @ts-ignore
490        ((preFrameSpacing.frameSpacingResult! - minValue) * (rowFrame.frame.height - padding * multiple)) /
491          (maxValue - minValue)
492      ) -
493      padding;
494    let currentPointY = // @ts-ignore
495      rowFrame.frame.height -
496      Math.floor(
497        // @ts-ignore
498        ((currentStruct.frameSpacingResult! - minValue) * (rowFrame.frame.height - padding * multiple)) /
499          (maxValue - minValue)
500      ) -
501      padding;
502    if (preFrameSpacing.frame && currentStruct.frame) {
503      ctx.strokeStyle = ColorUtils.ANIMATION_COLOR[2];
504      ctx.fillStyle = ColorUtils.ANIMATION_COLOR[2];
505      ctx.lineWidth = 2;
506      ctx.setLineDash([0]);
507      ctx.moveTo(preFrameSpacing.frame.x, prePointY);
508      ctx.lineTo(currentStruct.frame.x, currentPointY);
509      ctx.stroke();
510      ctx.closePath();
511      FrameSpacingStruct.drawSelect(preFrameSpacing, ctx, rowFrame);
512    }
513  }
514}
515
516const padding = 3;
517const invalidGroupId: number = -1;
518const multiple: number = 2;
519const unitIndex: number = 1;
520const selectRadius: number = 3;
521
522const smallTick = {
523  60: {
524    firstLine: 11.4,
525    secondLine: 13,
526    thirdLine: 14.6,
527  },
528  90: {
529    firstLine: 13.7,
530    secondLine: 19.4,
531    thirdLine: 25,
532  },
533  120: {
534    firstLine: 13.7,
535    secondLine: 19.4,
536    thirdLine: 25,
537  },
538};
539