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 FrameSpacingRender extends Render { 22 renderMainThread( 23 req: { 24 useCache: boolean; 25 context: CanvasRenderingContext2D; 26 type: string; 27 frameRate: number 28 animationRanges: AnimationRanges[] 29 }, 30 row: TraceRow<FrameSpacingStruct> 31 ): void { 32 let frameSpacingList = row.dataList; 33 let frameSpacingFilter = row.dataListCache; 34 this.frameSpacing( 35 frameSpacingList, 36 frameSpacingFilter, 37 TraceRow.range!.startNS, 38 TraceRow.range!.endNS, 39 TraceRow.range!.totalNS, 40 row, 41 req.animationRanges, 42 req.useCache || !TraceRow.range!.refresh 43 ); 44 this.render(req, frameSpacingFilter, row); 45 } 46 47 private render(req: { useCache: boolean; context: CanvasRenderingContext2D; type: string; frameRate: number; 48 animationRanges: AnimationRanges[] }, frameSpacingFilter: Array<FrameSpacingStruct>, 49 row: TraceRow<FrameSpacingStruct>) : void { 50 if (req.animationRanges.length > 0 && req.animationRanges[0] && frameSpacingFilter.length > 0) { 51 let preFrameSpacing: FrameSpacingStruct = frameSpacingFilter[0]; 52 // @ts-ignore 53 let smallTickStandard = smallTick[req.frameRate]; 54 let [minValue, maxValue] = this.maxMinData(smallTickStandard.firstLine, 55 smallTickStandard.thirdLine, frameSpacingFilter); 56 let isDraw = false; 57 let selectUnitWidth: number = 0; 58 for (let index: number = 0; index < frameSpacingFilter.length; index++) { 59 let currentStruct = frameSpacingFilter[index]; 60 selectUnitWidth = computeUnitWidth(preFrameSpacing.currentTs, currentStruct.currentTs, 61 row.frame.width, selectUnitWidth); 62 FrameSpacingStruct.refreshHoverStruct(preFrameSpacing, currentStruct, row, minValue, maxValue); 63 if (currentStruct.groupId === 0) { 64 if (currentStruct.currentTs > TraceRow.range!.startNS && currentStruct.currentTs < TraceRow.range!.endNS) { 65 isDraw = true; 66 this.drawPoint(req.context, currentStruct, row, minValue, maxValue); 67 } 68 } else if (currentStruct.groupId !== invalidGroupId && index > 0 && 69 currentStruct.groupId === preFrameSpacing!.groupId) { 70 isDraw = true; 71 FrameSpacingStruct.draw(req.context, preFrameSpacing, currentStruct, row, minValue, maxValue); 72 } 73 FrameSpacingStruct.drawSelect(currentStruct, req.context, row); 74 preFrameSpacing = currentStruct; 75 } 76 if (isDraw) { 77 this.drawDashedLines(Object.values(smallTickStandard), req, row, minValue, maxValue); 78 } 79 let findStructList = frameSpacingFilter.filter(filter => 80 row.isHover && isSurroundingPoint(row.hoverX, filter.frame!, selectUnitWidth / multiple)); 81 let find = false; 82 if (findStructList.length > 0) { 83 find = true; 84 let hoverIndex: number = 0; 85 if (findStructList.length > unitIndex) { 86 hoverIndex = Math.ceil(findStructList.length / multiple); 87 } 88 FrameSpacingStruct.hoverFrameSpacingStruct = findStructList[hoverIndex]; 89 } 90 if (!find && row.isHover) { 91 FrameSpacingStruct.hoverFrameSpacingStruct = undefined; 92 } 93 } 94 } 95 96 private drawPoint(ctx: CanvasRenderingContext2D, currentStruct: FrameSpacingStruct, 97 row: TraceRow<FrameSpacingStruct>, minValue: number, maxValue: number): void { 98 let currentPointY = row.frame.height - Math.floor((currentStruct.frameSpacingResult! - 99 minValue) * (row.frame.height - padding * multiple) / 100 (maxValue - minValue)) - padding; 101 ctx.beginPath(); 102 ctx.lineWidth = 1; 103 ctx.globalAlpha = 1; 104 ctx.arc(currentStruct.frame!.x, currentPointY, multiple, 0, multiple * Math.PI); 105 ctx.strokeStyle = ColorUtils.ANIMATION_COLOR[2]; 106 ctx.fillStyle = ColorUtils.ANIMATION_COLOR[2]; 107 ctx.stroke(); 108 ctx.fill(); 109 ctx.closePath(); 110 } 111 112 private drawDashedLines( 113 dashedLines: number[], 114 req: { useCache: boolean; context: CanvasRenderingContext2D; type: string; frameRate: number }, 115 row: TraceRow<FrameSpacingStruct>, 116 minVale: number, 117 maxValue: number 118 ): void { 119 for (let i = 0; i < dashedLines.length; i++) { 120 FrameSpacingStruct.drawParallelLine(req.context, row.frame, dashedLines, i, minVale, maxValue); 121 } 122 } 123 124 private frameSpacing( 125 frameSpacingList: Array<FrameSpacingStruct>, 126 frameSpacingFilter: Array<FrameSpacingStruct>, 127 startNS: number, 128 endNS: number, 129 totalNS: number, 130 row: TraceRow<FrameSpacingStruct>, 131 animationRanges: AnimationRanges[], 132 use: boolean 133 ): void { 134 let frame: Rect = row.frame; 135 let modelName: string | undefined | null = row.getAttribute('model-name'); 136 if (use && frameSpacingFilter.length > 0) { 137 for (let index = 0, len = frameSpacingFilter.length; index < len; index++) { 138 if (frameSpacingFilter[index].nameId === modelName) { 139 FrameSpacingStruct.setFrameSpacingFrame(frameSpacingFilter[index], startNS, endNS, totalNS, frame); 140 } 141 } 142 return; 143 } 144 frameSpacingFilter.length = 0; 145 if (frameSpacingList) { 146 let groupIdList: number[] = []; 147 for (let index = 0; index < frameSpacingList.length; index++) { 148 let item = frameSpacingList[index]; 149 if (modelName === item.nameId) { 150 item.groupId = invalidGroupId; 151 for (let rangeIndex = 0; rangeIndex < animationRanges.length; rangeIndex++) { 152 let currentRange = animationRanges[rangeIndex]; 153 if (item.currentTs >= currentRange.start && item.currentTs <= currentRange.end) { 154 item.groupId = currentRange.start; 155 break; 156 } 157 } 158 if (item.currentTs < startNS && index + unitIndex < frameSpacingList.length && 159 frameSpacingList[index + unitIndex].currentTs >= startNS && item.groupId !== invalidGroupId) { 160 this.refreshFrame(frameSpacingFilter, item, startNS, endNS, totalNS, frame, groupIdList); 161 } 162 if (item.currentTs >= startNS && item.currentTs <= endNS && item.groupId !== invalidGroupId) { 163 this.refreshFrame(frameSpacingFilter, item, startNS, endNS, totalNS, frame, groupIdList); 164 } 165 if (item.currentTs > endNS && item.groupId !== invalidGroupId) { 166 this.refreshFrame(frameSpacingFilter, item, startNS, endNS, totalNS, frame, groupIdList); 167 break; 168 } 169 } 170 } 171 this.grouping(groupIdList, frameSpacingFilter); 172 } 173 } 174 175 private grouping(groupIdList: number[], frameSpacingFilter: Array<FrameSpacingStruct>): void { 176 let simpleGroup = groupIdList.filter(groupId => { 177 return groupId !== invalidGroupId && groupIdList.indexOf(groupId) === groupIdList.lastIndexOf(groupId); 178 }); 179 frameSpacingFilter.forEach(frameSpacing => { 180 if (simpleGroup.indexOf(frameSpacing.groupId!) > invalidGroupId) { 181 frameSpacing.groupId = 0; 182 frameSpacing.frameSpacingResult = 0; 183 frameSpacing.preFrameWidth = 0; 184 frameSpacing.preFrameHeight = 0; 185 frameSpacing.preTs = 0; 186 } 187 }); 188 } 189 190 private refreshFrame(frameSpacingFilter: Array<FrameSpacingStruct>, item: FrameSpacingStruct, 191 startNS: number, endNS: number, totalNS: number, frame: Rect, groupIdList: number[]): void { 192 frameSpacingFilter.push(item); 193 FrameSpacingStruct.setFrameSpacingFrame(item, startNS, endNS, totalNS, frame); 194 groupIdList.push(item.groupId!); 195 } 196 197 private maxMinData(tickStandardMin: number, tickStandardMax: number, filter: FrameSpacingStruct[]): [number, number] { 198 let min = Math.min.apply(Math, filter.map(filterData => { 199 return filterData.frameSpacingResult!; 200 })); 201 let max = Math.max.apply(Math, filter.map(filterData => { 202 return filterData.frameSpacingResult!; 203 })); 204 if (max < tickStandardMax) { 205 max = tickStandardMax + padding; 206 } 207 if (min > tickStandardMin) { 208 min = tickStandardMin; 209 } 210 return [min, max]; 211 } 212} 213 214export class FrameSpacingStruct extends BaseStruct { 215 static hoverFrameSpacingStruct: FrameSpacingStruct | undefined; 216 static selectFrameSpacingStruct: FrameSpacingStruct | undefined; 217 static physicalWidth: number | undefined; 218 static physicalHeight: number | undefined; 219 preTs: number | undefined; 220 currentTs: number = 0; 221 frameSpacingResult: number | undefined; 222 id: number = 0; 223 groupId: number | undefined; 224 currentFrameWidth: number | undefined; 225 preFrameWidth: number | undefined; 226 currentFrameHeight: number | undefined; 227 preFrameHeight: number | undefined; 228 x: number | undefined; 229 y: number | undefined; 230 nameId: string | undefined; 231 232 static setFrameSpacingFrame( 233 frameSpacingNode: FrameSpacingStruct, 234 startNS: number, 235 endNS: number, 236 totalNS: number, 237 row: Rect 238 ): void { 239 let pointX = ns2x(frameSpacingNode.currentTs || 0, startNS, endNS, totalNS, row); 240 if (!frameSpacingNode.frame) { 241 frameSpacingNode.frame = new Rect(0, 0, 0, 0); 242 } 243 frameSpacingNode.frame.x = Math.floor(pointX); 244 } 245 246 static isSelect(currSpacingStruct: FrameSpacingStruct) : boolean | 0 | undefined { 247 return TraceRow.rangeSelectObject && 248 TraceRow.rangeSelectObject.startNS && 249 TraceRow.rangeSelectObject.endNS && 250 currSpacingStruct.currentTs >= TraceRow.rangeSelectObject.startNS && 251 currSpacingStruct.currentTs <= TraceRow.rangeSelectObject.endNS; 252 } 253 254 static refreshHoverStruct(preFrameSpacing: FrameSpacingStruct, frameSpacing: FrameSpacingStruct, 255 row: TraceRow<FrameSpacingStruct>, minValue: number, maxValue: number): void { 256 if (frameSpacing.frame) { 257 frameSpacing.frame.y = row.frame.height - Math.floor((frameSpacing.frameSpacingResult! - 258 minValue) * (row.frame.height - padding * multiple) / 259 (maxValue - minValue)) - padding; 260 } 261 }; 262 263 static draw( 264 ctx: CanvasRenderingContext2D, 265 preFrameSpacing: FrameSpacingStruct, 266 currentStruct: FrameSpacingStruct, 267 rowFrame: TraceRow<FrameSpacingStruct>, 268 minValue: number, 269 maxValue: number 270 ): void { 271 if (currentStruct.frame && preFrameSpacing.frame) { 272 this.drawPolyline(ctx, preFrameSpacing, currentStruct, rowFrame, minValue, maxValue); 273 } 274 } 275 276 static drawSelect(currentStruct: FrameSpacingStruct, ctx: CanvasRenderingContext2D, 277 rowFrame: TraceRow<FrameSpacingStruct>): void { 278 if (currentStruct.frame && 279 currentStruct.currentTs > TraceRow.range!.startNS && 280 currentStruct.currentTs < TraceRow.range!.endNS) { 281 if ((currentStruct === FrameSpacingStruct.hoverFrameSpacingStruct && rowFrame.isHover) || 282 currentStruct === FrameSpacingStruct.selectFrameSpacingStruct) { 283 ctx.lineWidth = 3; 284 ctx.beginPath(); 285 ctx.arc(currentStruct.frame.x, currentStruct.frame.y, selectRadius, 0, multiple * Math.PI); 286 ctx.strokeStyle = ColorUtils.ANIMATION_COLOR[7]; 287 ctx.lineWidth = 2; 288 ctx.globalAlpha = 1; 289 ctx.fillStyle = ColorUtils.ANIMATION_COLOR[9]; 290 ctx.stroke(); 291 ctx.fill(); 292 ctx.closePath(); 293 } 294 } 295 if (rowFrame.getAttribute('check-type') === '2' && FrameSpacingStruct.isSelect(currentStruct)) { 296 ctx.beginPath(); 297 ctx.lineWidth = 3; 298 ctx.arc(currentStruct.frame!.x, currentStruct.frame!.y, selectRadius, 0, multiple * Math.PI); 299 ctx.strokeStyle = ColorUtils.ANIMATION_COLOR[7]; 300 ctx.fillStyle = ColorUtils.ANIMATION_COLOR[9]; 301 ctx.lineWidth = 2; 302 ctx.globalAlpha = 1; 303 ctx.stroke(); 304 ctx.fill(); 305 ctx.closePath(); 306 } 307 } 308 309 static drawParallelLine( 310 ctx: CanvasRenderingContext2D, 311 rowFrame: Rect, 312 dashedLines: number[], 313 index: number, 314 minValue: number, 315 maxValue: number 316 ): void { 317 let pointY = rowFrame.height - Math.floor((dashedLines[index] - minValue) * 318 (rowFrame.height - padding * multiple) / (maxValue - minValue)) - padding; 319 let lineDash = 10; 320 let textPadding = 4; 321 ctx.beginPath(); 322 ctx.lineWidth = 2; 323 ctx.setLineDash([lineDash]); 324 ctx.strokeStyle = ColorUtils.ANIMATION_COLOR[6]; 325 ctx.fillStyle = ColorUtils.ANIMATION_COLOR[6]; 326 ctx.moveTo(0, pointY); 327 ctx.lineTo(rowFrame.width, pointY); 328 ctx.stroke(); 329 ctx.strokeStyle = ColorUtils.ANIMATION_COLOR[3]; 330 ctx.fillStyle = ColorUtils.ANIMATION_COLOR[3]; 331 if (index === 0) { 332 ctx.fillText(dashedLines[index].toString(), 0, pointY + multiple * textPadding); 333 } else if (index === unitIndex) { 334 ctx.fillText(dashedLines[index].toString(), 0, pointY + textPadding); 335 } else { 336 ctx.fillText(dashedLines[index].toString(), 0, pointY - textPadding); 337 } 338 ctx.closePath(); 339 } 340 341 static drawPolyline( 342 ctx: CanvasRenderingContext2D, 343 preFrameSpacing: FrameSpacingStruct, 344 currentStruct: FrameSpacingStruct, 345 rowFrame: TraceRow<FrameSpacingStruct>, 346 minValue: number, 347 maxValue: number 348 ): void { 349 ctx.beginPath(); 350 let prePointY = rowFrame.frame.height - Math.floor((preFrameSpacing.frameSpacingResult! - 351 minValue) * (rowFrame.frame.height - padding * multiple) / 352 (maxValue - minValue)) - padding; 353 let currentPointY = rowFrame.frame.height - Math.floor((currentStruct.frameSpacingResult! - 354 minValue) * (rowFrame.frame.height - padding * multiple) / 355 (maxValue - minValue)) - padding; 356 if (preFrameSpacing.frame && currentStruct.frame) { 357 ctx.strokeStyle = ColorUtils.ANIMATION_COLOR[2]; 358 ctx.fillStyle = ColorUtils.ANIMATION_COLOR[2]; 359 ctx.lineWidth = 2; 360 ctx.setLineDash([0]); 361 ctx.moveTo(preFrameSpacing.frame.x, prePointY); 362 ctx.lineTo(currentStruct.frame.x, currentPointY); 363 ctx.stroke(); 364 ctx.closePath(); 365 FrameSpacingStruct.drawSelect(preFrameSpacing, ctx, rowFrame); 366 } 367 } 368} 369 370const padding = 3; 371const invalidGroupId: number = -1; 372const multiple: number = 2; 373const unitIndex: number = 1; 374const selectRadius: number = 3; 375 376const smallTick = { 377 60: { 378 firstLine: 11.4, 379 secondLine: 13, 380 thirdLine: 14.6 381 }, 382 90: { 383 firstLine: 13.7, 384 secondLine: 19.4, 385 thirdLine: 25 386 }, 387 120: { 388 firstLine: 13.7, 389 secondLine: 19.4, 390 thirdLine: 25 391 } 392}; 393