• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.js';
17import { BaseStruct, computeUnitWidth, isSurroundingPoint, ns2x, Rect, Render } from './ProcedureWorkerCommon.js';
18import { AnimationRanges } from '../../bean/FrameComponentBean.js';
19import { ColorUtils } from '../../component/trace/base/ColorUtils.js';
20
21export class FrameDynamicRender extends Render {
22  renderMainThread(
23    req: {
24      useCache: boolean;
25      context: CanvasRenderingContext2D;
26      type: string;
27      animationRanges: AnimationRanges[];
28    },
29    row: TraceRow<FrameDynamicStruct>
30  ): void {
31    let frameDynamicList: FrameDynamicStruct[] = row.dataList;
32    let frameDynamicFilter: FrameDynamicStruct[] = row.dataListCache;
33    this.frameDynamic(frameDynamicList, frameDynamicFilter, row, req.animationRanges, req.useCache);
34    if (req.animationRanges.length > 0 && req.animationRanges[0] && frameDynamicFilter.length > 0) {
35      let modelType: string = row.getAttribute('model-type') || 'x';
36      let [minValue, maxValue] = this.getMinAndMaxData(frameDynamicFilter, modelType);
37      let preDynamic: FrameDynamicStruct = frameDynamicFilter[0];
38      let isDraw = false;
39      let selectUnitWidth: number = 0;
40      for (let index: number = 0; index < frameDynamicFilter.length; index++) {
41        let currDynamic: FrameDynamicStruct = frameDynamicFilter[index];
42        selectUnitWidth = computeUnitWidth(preDynamic.ts, currDynamic.ts, row.frame.width, selectUnitWidth);
43        this.refreshPointY(currDynamic, row, modelType, minValue, maxValue);
44        if (currDynamic.groupId === 0) {
45          if (currDynamic.ts > TraceRow.range!.startNS && currDynamic.ts < TraceRow.range!.endNS) {
46            isDraw = true;
47            this.drawSinglePoint(req.context, currDynamic, row, modelType, minValue, maxValue);
48          }
49        } else if (currDynamic.groupId !== invalidGroupId && index > 0 && currDynamic.groupId === preDynamic!.groupId) {
50          isDraw = true;
51          FrameDynamicStruct.draw(req.context, preDynamic, currDynamic, row, modelType);
52        }
53        FrameDynamicStruct.drawSelect(req.context, currDynamic, row);
54        preDynamic = currDynamic;
55      }
56      if (isDraw) {
57        this.drawDynamicPointYStr(req.context, frameDynamicFilter, row.frame, minValue, maxValue);
58      }
59      if (!this.setHoverFrameDynamic(row, frameDynamicFilter, selectUnitWidth) && row.isHover) {
60        FrameDynamicStruct.hoverFrameDynamicStruct = undefined;
61      }
62    }
63  }
64
65  private setHoverFrameDynamic(
66    row: TraceRow<FrameDynamicStruct>,
67    frameDynamicFilter: FrameDynamicStruct[],
68    selectUnitWidth: number
69  ): boolean {
70    let find: boolean = false;
71    let findStructList = frameDynamicFilter.filter(filter =>
72      row.isHover && isSurroundingPoint(row.hoverX, filter.frame!, selectUnitWidth / multiple));
73    if (findStructList.length > 0) {
74      find = true;
75      let hoverIndex: number = 0;
76      if (findStructList.length > unitIndex) {
77        hoverIndex = Math.ceil(findStructList.length / multiple);
78      }
79      FrameDynamicStruct.hoverFrameDynamicStruct = findStructList[hoverIndex];
80    }
81    return find;
82  }
83
84  private frameDynamic(
85    frameDynamicList: FrameDynamicStruct[],
86    frameDynamicFilter: FrameDynamicStruct[],
87    row: TraceRow<FrameDynamicStruct>,
88    animationRanges: AnimationRanges[],
89    use: boolean
90  ): void {
91    let startNS: number = TraceRow.range!.startNS;
92    let endNS: number = TraceRow.range!.endNS;
93    let totalNS: number = TraceRow.range!.totalNS;
94    let frame: Rect = row.frame;
95    let modelName: string | undefined | null = row.getAttribute('model-name');
96    if ((use || !TraceRow.range!.refresh) && frameDynamicFilter.length > 0) {
97      this.refreshDynamicFrame(frameDynamicFilter, frame, startNS, endNS, totalNS, modelName!);
98      return;
99    }
100    frameDynamicFilter.length = 0;
101    if (frameDynamicList) {
102      let groupIdList: number[] = [];
103      for (let dataIndex: number = 0; dataIndex < frameDynamicList.length; dataIndex++) {
104        let currentDynamic: FrameDynamicStruct = frameDynamicList[dataIndex];
105        if (currentDynamic.appName === modelName) {
106          currentDynamic.groupId = invalidGroupId;
107          for (let rangeIndex = 0; rangeIndex < animationRanges.length; rangeIndex++) {
108            let currentRange = animationRanges[rangeIndex];
109            if (currentDynamic.ts >= currentRange.start && currentDynamic.ts <= currentRange.end) {
110              currentDynamic.groupId = currentRange.start;
111              break;
112            }
113          }
114          if (currentDynamic.ts < startNS && (dataIndex + unitIndex) < frameDynamicList.length &&
115            frameDynamicList[dataIndex + unitIndex].ts >= startNS && currentDynamic.groupId !== invalidGroupId) {
116            this.refreshFilterDynamicFrame(frameDynamicFilter, currentDynamic, row.frame, startNS,
117              endNS, totalNS, groupIdList);
118          }
119          if (currentDynamic.ts >= startNS && currentDynamic.ts <= endNS && currentDynamic.groupId !== invalidGroupId) {
120            this.refreshFilterDynamicFrame(frameDynamicFilter, currentDynamic, row.frame, startNS,
121              endNS, totalNS, groupIdList);
122          }
123          if (currentDynamic.ts >= endNS && currentDynamic.groupId !== invalidGroupId) {
124            this.refreshFilterDynamicFrame(frameDynamicFilter, currentDynamic, row.frame, startNS,
125              endNS, totalNS, groupIdList);
126            break;
127          }
128        }
129      }
130      this.setSimpleGroupId(groupIdList, frameDynamicFilter);
131    }
132  };
133
134  private setSimpleGroupId(
135    groupIdList: number[],
136    frameDynamicFilter: FrameDynamicStruct[]
137  ): void {
138    let simpleGroup = groupIdList.filter(groupId => {
139      return groupId !== invalidGroupId && groupIdList.indexOf(groupId) === groupIdList.lastIndexOf(groupId);
140    });
141    frameDynamicFilter.forEach(dynamic => {
142      if (simpleGroup.indexOf(dynamic.groupId!) > invalidGroupId) {
143        dynamic.groupId = 0;
144      }
145    });
146  }
147
148  private refreshDynamicFrame(
149    frameDynamicFilter: FrameDynamicStruct[],
150    frame: Rect,
151    startNS: number,
152    endNS: number,
153    totalNS: number,
154    modelName: string
155  ): void {
156    for (let index: number = 0; index < frameDynamicFilter.length; index++) {
157      let frameDynamicNode: FrameDynamicStruct = frameDynamicFilter[index];
158      frameDynamicNode.frame = undefined;
159      if (frameDynamicNode.appName === modelName) {
160        FrameDynamicStruct.setFrameDynamic(frameDynamicNode, startNS, endNS, totalNS, frame);
161      }
162    }
163  };
164
165  private drawSinglePoint(
166    ctx: CanvasRenderingContext2D,
167    currDynamic: FrameDynamicStruct,
168    row: TraceRow<FrameDynamicStruct>,
169    modelType: string,
170    minValue: number,
171    maxValue: number
172  ): void {
173    let smallArcRadius: number = 2;
174    // @ts-ignore
175    currDynamic.typeValue = currDynamic[modelType];
176    currDynamic.frame!.y = row.frame.height - padding -
177      ((row.frame.height - padding * multiple) * ((currDynamic.typeValue || 0) - minValue) / (maxValue - minValue));
178    ctx.beginPath();
179    ctx.lineWidth = 1;
180    ctx.globalAlpha = 1;
181    ctx.arc(currDynamic.frame!.x, currDynamic.frame!.y, smallArcRadius, 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 refreshPointY(
190    curDynamic: FrameDynamicStruct,
191    row: TraceRow<FrameDynamicStruct>,
192    modelType: string,
193    minValue: number,
194    maxValue: number
195  ): void {
196    // @ts-ignore
197    let currDynamicValue = curDynamic[modelType];
198    if (curDynamic.frame) {
199      let pointY = (row.frame.height - padding * multiple) * ((currDynamicValue - minValue) / (maxValue - minValue));
200      curDynamic.frame.y = row.frame.height - padding - pointY;
201    }
202  };
203
204  private drawDynamicPointYStr(
205    ctx: CanvasRenderingContext2D,
206    frameDynamicFilter: FrameDynamicStruct[],
207    frame: Rect,
208    minValue: number,
209    maxValue: number
210  ): void {
211    if (frameDynamicFilter.length > 0) {
212      ctx.globalAlpha = 1;
213      ctx.lineWidth = 1;
214      ctx.fillStyle = ColorUtils.ANIMATION_COLOR[3];
215      let minUnitValue: number = 10;
216      let fixedNumber = 2;
217      let totalValue = maxValue - minValue;
218      let pointYInterval = totalValue / (yScaleNumber - unitIndex);
219      for (let index = 0; index < yScaleNumber; index++) {
220        let pointYValue = minValue + pointYInterval * index;
221        let pointYHeight = (frame.height - padding * multiple) * (pointYValue - minValue) / totalValue;
222        let pointY = frame.height - multiple * padding - pointYHeight;
223        if (pointYValue !== 0) {
224          if (maxValue - minValue <= minUnitValue) {
225            ctx.fillText(`- ${pointYValue.toFixed(fixedNumber)}`, 0, pointY);
226          } else {
227            ctx.fillText(`- ${Math.ceil(pointYValue)}`, 0, pointY);
228          }
229        }
230      }
231    }
232  };
233
234  private getMinAndMaxData(
235    frameDynamicFilter: FrameDynamicStruct[],
236    modelType: string
237  ): [number, number] {
238    let min: number = Math.min.apply(Math, frameDynamicFilter.map(filterData => {
239      // @ts-ignore
240      return filterData[modelType];
241    }));
242    let max: number = Math.max.apply(Math, frameDynamicFilter.map(filterData => {
243      // @ts-ignore
244      return filterData[modelType];
245    }));
246
247    let yScaleMinValue: number = 1;
248    let yScaleMinSpacing: number = 10;
249    if (min === max) {
250      if (max <= yScaleMinValue) {
251        max = yScaleMinValue;
252        min = 0;
253      } else if (max <= yScaleMinSpacing) {
254        max = yScaleMinSpacing;
255        min = 0;
256      } else {
257        max += yScaleMinSpacing;
258        min -= yScaleMinSpacing;
259      }
260    }
261    return [min, max];
262  };
263
264  private refreshFilterDynamicFrame(
265    frameDynamicFilter: FrameDynamicStruct[],
266    currentFrameDynamic: FrameDynamicStruct,
267    frame: Rect,
268    startNS: number,
269    endNS: number,
270    totalNS: number,
271    groupIdList: number[]
272  ): void {
273    groupIdList.push(currentFrameDynamic.groupId!);
274    frameDynamicFilter.push(currentFrameDynamic);
275    FrameDynamicStruct.setFrameDynamic(currentFrameDynamic, startNS, endNS, totalNS, frame);
276  };
277}
278
279export class FrameDynamicStruct extends BaseStruct {
280  static hoverFrameDynamicStruct: FrameDynamicStruct | undefined;
281  static selectFrameDynamicStruct: FrameDynamicStruct | undefined;
282  ts: number = 0;
283  id: number | undefined;
284  x: number | undefined;
285  y: number | undefined;
286  width: number | undefined;
287  height: number | undefined;
288  alpha: number = 0;
289  appName: string | undefined;
290  groupId: number | undefined;
291  typeValue: number | undefined;
292
293  static setFrameDynamic(
294    dynamicNode: FrameDynamicStruct,
295    startNS: number,
296    endNS: number,
297    totalNS: number,
298    frame: Rect
299  ): void {
300    let pointX: number = ns2x(dynamicNode.ts || 0, startNS, endNS, totalNS, frame);
301    if (!dynamicNode.frame) {
302      dynamicNode.frame = new Rect(0, 0, 0, 0);
303    }
304    dynamicNode.frame.x = Math.floor(pointX);
305    dynamicNode.frame.height = frame.height;
306  }
307
308  static draw(
309    ctx: CanvasRenderingContext2D,
310    preDynamicStruct: FrameDynamicStruct,
311    currDynamicStruct: FrameDynamicStruct,
312    row: TraceRow<FrameDynamicStruct>,
313    modelType: string
314  ): void {
315    if (preDynamicStruct.frame && currDynamicStruct.frame) {
316      // @ts-ignore
317      preDynamicStruct.typeValue = preDynamicStruct[modelType];
318      // @ts-ignore
319      currDynamicStruct.typeValue = currDynamicStruct[modelType];
320      if (preDynamicStruct.typeValue === currDynamicStruct.typeValue) {
321        ctx.strokeStyle = ColorUtils.ANIMATION_COLOR[1];
322      } else {
323        ctx.strokeStyle = ColorUtils.ANIMATION_COLOR[2];
324      }
325      ctx.beginPath();
326      ctx.globalAlpha = 1;
327      ctx.lineWidth = 3;
328      ctx.lineJoin = 'round';
329      ctx.moveTo(preDynamicStruct.frame.x, preDynamicStruct.frame.y);
330      ctx.lineTo(currDynamicStruct.frame.x, currDynamicStruct.frame.y);
331      ctx.stroke();
332      ctx.closePath();
333      FrameDynamicStruct.drawSelect(ctx, preDynamicStruct, row);
334    }
335  }
336
337  static drawSelect(
338    ctx: CanvasRenderingContext2D,
339    currDynamicStruct: FrameDynamicStruct,
340    row: TraceRow<FrameDynamicStruct>
341  ): void {
342    if ((currDynamicStruct === FrameDynamicStruct.hoverFrameDynamicStruct && row.isHover) ||
343      currDynamicStruct === FrameDynamicStruct.selectFrameDynamicStruct) {
344      FrameDynamicStruct.drawSelectOrHoverArc(ctx, currDynamicStruct);
345    }
346    if (row.getAttribute('check-type') === '2' && FrameDynamicStruct.isSelect(currDynamicStruct)) {
347      FrameDynamicStruct.drawSelectOrHoverArc(ctx, currDynamicStruct);
348    }
349  }
350
351  static drawSelectOrHoverArc(
352    ctx: CanvasRenderingContext2D,
353    currDynamicStruct: FrameDynamicStruct
354  ): void {
355    if (currDynamicStruct.frame &&
356      currDynamicStruct.ts > TraceRow.range!.startNS &&
357      currDynamicStruct.ts < TraceRow.range!.endNS) {
358      let bigArcRadius: number = 3;
359      ctx.beginPath();
360      ctx.lineWidth = 3;
361      ctx.globalAlpha = 1;
362      ctx.arc(currDynamicStruct.frame.x, currDynamicStruct.frame.y, bigArcRadius, 0, multiple * Math.PI);
363      ctx.strokeStyle = ColorUtils.ANIMATION_COLOR[7];
364      ctx.fillStyle = ColorUtils.ANIMATION_COLOR[9];
365      ctx.stroke();
366      ctx.fill();
367    }
368  }
369
370  static isSelect(currDynamicStruct: FrameDynamicStruct): boolean | 0 | undefined {
371    return TraceRow.rangeSelectObject &&
372      TraceRow.rangeSelectObject.startNS &&
373      TraceRow.rangeSelectObject.endNS &&
374      currDynamicStruct.ts >= TraceRow.rangeSelectObject.startNS &&
375      currDynamicStruct.ts <= TraceRow.rangeSelectObject.endNS;
376  }
377}
378
379const padding: number = 3;
380const invalidGroupId: number = -1;
381const multiple: number = 2;
382const unitIndex: number = 1;
383const yScaleNumber: number = 5;
384