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(clickRowType: string, sp: SpSystemTrace, row: TraceRow<any>) { 305 return new Promise((resolve, reject) => { 306 if (clickRowType === TraceRow.ROW_TYPE_FRAME_SPACING) { 307 FrameSpacingStruct.selectFrameSpacingStruct = 308 FrameSpacingStruct.hoverFrameSpacingStruct || row.getHoverStruct(false, true); 309 if (FrameSpacingStruct.selectFrameSpacingStruct) { 310 sp.traceSheetEL?.displayFrameSpacingData(FrameSpacingStruct.selectFrameSpacingStruct); 311 sp.timerShaftEL?.modifyFlagList(undefined); 312 } 313 reject(new Error()); 314 } else { 315 resolve(null); 316 } 317 }); 318} 319export class FrameSpacingStruct extends BaseStruct { 320 static hoverFrameSpacingStruct: FrameSpacingStruct | undefined; 321 static selectFrameSpacingStruct: FrameSpacingStruct | undefined; 322 physicalWidth: number | undefined; 323 physicalHeight: number | undefined; 324 preTs: number | undefined; 325 currentTs: number = 0; 326 frameSpacingResult: number | undefined; 327 id: number = 0; 328 groupId: number | undefined; 329 currentFrameWidth: number | undefined; 330 preFrameWidth: number | undefined; 331 currentFrameHeight: number | undefined; 332 preFrameHeight: number | undefined; 333 x: number | undefined; 334 y: number | undefined; 335 preX: number | undefined; 336 preY: number | undefined; 337 nameId: string | undefined; 338 339 static setFrameSpacingFrame( 340 frameSpacingNode: FrameSpacingStruct, 341 startNS: number, 342 endNS: number, 343 totalNS: number, 344 row: Rect 345 ): void { 346 let pointX = ns2x(frameSpacingNode.currentTs || 0, startNS, endNS, totalNS, row); 347 if (!frameSpacingNode.frame) { 348 frameSpacingNode.frame = new Rect(0, 0, 0, 0); 349 } 350 frameSpacingNode.frame.x = Math.floor(pointX); 351 } 352 353 static isSelect(currSpacingStruct: FrameSpacingStruct): boolean | 0 | undefined { 354 return ( 355 TraceRow.rangeSelectObject && 356 TraceRow.rangeSelectObject.startNS && 357 TraceRow.rangeSelectObject.endNS && 358 currSpacingStruct.currentTs >= TraceRow.rangeSelectObject.startNS && 359 currSpacingStruct.currentTs <= TraceRow.rangeSelectObject.endNS 360 ); 361 } 362 363 static refreshHoverStruct( 364 preFrameSpacing: FrameSpacingStruct, 365 frameSpacing: FrameSpacingStruct, 366 row: TraceRow<FrameSpacingStruct>, 367 minValue: number, 368 maxValue: number 369 ): void { 370 if (frameSpacing.frame) { 371 frameSpacing.frame.y = // @ts-ignore 372 row.frame.height - 373 Math.floor( 374 // @ts-ignore 375 ((frameSpacing.frameSpacingResult! - minValue) * (row.frame.height - padding * multiple)) / 376 (maxValue - minValue) 377 ) - 378 padding; 379 } 380 } 381 382 static draw( 383 ctx: CanvasRenderingContext2D, 384 preFrameSpacing: FrameSpacingStruct, 385 currentStruct: FrameSpacingStruct, 386 rowFrame: TraceRow<FrameSpacingStruct>, 387 minValue: number, 388 maxValue: number 389 ): void { 390 if (currentStruct.frame && preFrameSpacing.frame) { 391 this.drawPolyline(ctx, preFrameSpacing, currentStruct, rowFrame, minValue, maxValue); 392 } 393 } 394 395 static drawSelect( 396 currentStruct: FrameSpacingStruct, 397 ctx: CanvasRenderingContext2D, 398 rowFrame: TraceRow<FrameSpacingStruct> 399 ): void { 400 if ( 401 currentStruct.frame && 402 currentStruct.currentTs > TraceRow.range!.startNS && 403 currentStruct.currentTs < TraceRow.range!.endNS 404 ) { 405 if ( 406 (currentStruct === FrameSpacingStruct.hoverFrameSpacingStruct && rowFrame.isHover) || 407 currentStruct === FrameSpacingStruct.selectFrameSpacingStruct 408 ) { 409 ctx.lineWidth = 3; 410 ctx.beginPath(); 411 ctx.arc(currentStruct.frame.x, currentStruct.frame.y, selectRadius, 0, multiple * Math.PI); 412 ctx.strokeStyle = ColorUtils.ANIMATION_COLOR[7]; 413 ctx.lineWidth = 2; 414 ctx.globalAlpha = 1; 415 ctx.fillStyle = ColorUtils.ANIMATION_COLOR[9]; 416 ctx.stroke(); 417 ctx.fill(); 418 ctx.closePath(); 419 } 420 } 421 if (rowFrame.getAttribute('check-type') === '2' && FrameSpacingStruct.isSelect(currentStruct)) { 422 ctx.beginPath(); 423 ctx.lineWidth = 3; 424 ctx.arc(currentStruct.frame!.x, currentStruct.frame!.y, selectRadius, 0, multiple * Math.PI); 425 ctx.strokeStyle = ColorUtils.ANIMATION_COLOR[7]; 426 ctx.fillStyle = ColorUtils.ANIMATION_COLOR[9]; 427 ctx.lineWidth = 2; 428 ctx.globalAlpha = 1; 429 ctx.stroke(); 430 ctx.fill(); 431 ctx.closePath(); 432 } 433 } 434 435 static drawParallelLine( 436 ctx: CanvasRenderingContext2D, 437 rowFrame: Rect, 438 dashedLines: number[], 439 index: number, 440 minValue: number, 441 maxValue: number 442 ): void { 443 let pointY = 444 rowFrame.height - 445 Math.floor(((dashedLines[index] - minValue) * (rowFrame.height - padding * multiple)) / (maxValue - minValue)) - 446 padding; 447 let lineDash = 10; 448 let textPadding = 4; 449 ctx.beginPath(); 450 ctx.lineWidth = 2; 451 ctx.setLineDash([lineDash]); 452 ctx.strokeStyle = ColorUtils.ANIMATION_COLOR[6]; 453 ctx.fillStyle = ColorUtils.ANIMATION_COLOR[6]; 454 ctx.moveTo(0, pointY); 455 ctx.lineTo(rowFrame.width, pointY); 456 ctx.stroke(); 457 ctx.strokeStyle = ColorUtils.ANIMATION_COLOR[3]; 458 ctx.fillStyle = ColorUtils.ANIMATION_COLOR[3]; 459 if (index === 0) { 460 ctx.fillText(dashedLines[index].toString(), 0, pointY + multiple * textPadding); 461 } else if (index === unitIndex) { 462 ctx.fillText(dashedLines[index].toString(), 0, pointY + textPadding); 463 } else { 464 ctx.fillText(dashedLines[index].toString(), 0, pointY - textPadding); 465 } 466 ctx.closePath(); 467 } 468 469 static drawPolyline( 470 ctx: CanvasRenderingContext2D, 471 preFrameSpacing: FrameSpacingStruct, 472 currentStruct: FrameSpacingStruct, 473 rowFrame: TraceRow<FrameSpacingStruct>, 474 minValue: number, 475 maxValue: number 476 ): void { 477 ctx.beginPath(); 478 let prePointY = // @ts-ignore 479 rowFrame.frame.height - 480 Math.floor( 481 // @ts-ignore 482 ((preFrameSpacing.frameSpacingResult! - minValue) * (rowFrame.frame.height - padding * multiple)) / 483 (maxValue - minValue) 484 ) - 485 padding; 486 let currentPointY = // @ts-ignore 487 rowFrame.frame.height - 488 Math.floor( 489 // @ts-ignore 490 ((currentStruct.frameSpacingResult! - minValue) * (rowFrame.frame.height - padding * multiple)) / 491 (maxValue - minValue) 492 ) - 493 padding; 494 if (preFrameSpacing.frame && currentStruct.frame) { 495 ctx.strokeStyle = ColorUtils.ANIMATION_COLOR[2]; 496 ctx.fillStyle = ColorUtils.ANIMATION_COLOR[2]; 497 ctx.lineWidth = 2; 498 ctx.setLineDash([0]); 499 ctx.moveTo(preFrameSpacing.frame.x, prePointY); 500 ctx.lineTo(currentStruct.frame.x, currentPointY); 501 ctx.stroke(); 502 ctx.closePath(); 503 FrameSpacingStruct.drawSelect(preFrameSpacing, ctx, rowFrame); 504 } 505 } 506} 507 508const padding = 3; 509const invalidGroupId: number = -1; 510const multiple: number = 2; 511const unitIndex: number = 1; 512const selectRadius: number = 3; 513 514const smallTick = { 515 60: { 516 firstLine: 11.4, 517 secondLine: 13, 518 thirdLine: 14.6, 519 }, 520 90: { 521 firstLine: 13.7, 522 secondLine: 19.4, 523 thirdLine: 25, 524 }, 525 120: { 526 firstLine: 13.7, 527 secondLine: 19.4, 528 thirdLine: 25, 529 }, 530}; 531