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