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