/* * Copyright (C) 2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { TraceRow } from '../../component/trace/base/TraceRow'; import { BaseStruct, computeUnitWidth, drawLoadingFrame, isSurroundingPoint, ns2x, Rect, Render, } from './ProcedureWorkerCommon'; import { type AnimationRanges } from '../../bean/FrameComponentBean'; import { ColorUtils } from '../../component/trace/base/ColorUtils'; import {SpSystemTrace} from "../../component/SpSystemTrace"; export class FrameSpacingRender extends Render { renderMainThread( req: { useCache: boolean; context: CanvasRenderingContext2D; type: string; frameRate: number; animationRanges: AnimationRanges[]; }, row: TraceRow<FrameSpacingStruct> ): void { let frameSpacingList = row.dataList; let frameSpacingFilter = row.dataListCache; this.frameSpacing( frameSpacingList, frameSpacingFilter, TraceRow.range!.startNS, TraceRow.range!.endNS, TraceRow.range!.totalNS, row, req.animationRanges, req.useCache || !TraceRow.range!.refresh ); drawLoadingFrame(req.context, row.dataListCache, row); this.render(req, frameSpacingList, row); } private render( req: { useCache: boolean; context: CanvasRenderingContext2D; type: string; frameRate: number; animationRanges: AnimationRanges[]; }, frameSpacingFilter: Array<FrameSpacingStruct>, row: TraceRow<FrameSpacingStruct> ): void { if (req.animationRanges.length > 0 && req.animationRanges[0] && frameSpacingFilter.length > 0) { let minValue = 0; let maxValue = 0; let smallTickStandard = { firstLine: 0, secondLine: 0, thirdLine: 0, }; if (req.frameRate) { // @ts-ignore smallTickStandard = smallTick[req.frameRate]; [minValue, maxValue] = this.maxMinData( smallTickStandard.firstLine, smallTickStandard.thirdLine, frameSpacingFilter ); } else { minValue = Math.min.apply( Math, frameSpacingFilter.map((filterData) => { return filterData.frameSpacingResult!; }) ); maxValue = Math.max.apply( Math, frameSpacingFilter.map((filterData) => { return filterData.frameSpacingResult!; }) ); } let selectUnitWidth: number = 0; this.drawTraceRow(frameSpacingFilter, selectUnitWidth, req, row, minValue, maxValue, smallTickStandard); let findStructList = frameSpacingFilter.filter( (filter) => row.isHover && isSurroundingPoint(row.hoverX, filter.frame!, selectUnitWidth / multiple) ); this.setHoverStruct(findStructList, row); } } private drawTraceRow( frameSpacingFilter: Array<FrameSpacingStruct>, selectUnitWidth: number, req: any, row: TraceRow<FrameSpacingStruct>, minValue: number, maxValue: number, smallTickStandard: any ) { let preFrameSpacing: FrameSpacingStruct = frameSpacingFilter[0]; let isDraw = false; for (let index: number = 0; index < frameSpacingFilter.length; index++) { let currentStruct = frameSpacingFilter[index]; selectUnitWidth = computeUnitWidth( preFrameSpacing.currentTs, currentStruct.currentTs, row.frame.width, selectUnitWidth ); FrameSpacingStruct.refreshHoverStruct(preFrameSpacing, currentStruct, row, minValue, maxValue); if (currentStruct.groupId === 0) { if (currentStruct.currentTs > TraceRow.range!.startNS && currentStruct.currentTs < TraceRow.range!.endNS) { isDraw = true; this.drawPoint(req.context, currentStruct, row, minValue, maxValue); } } else if ( currentStruct.groupId !== invalidGroupId && index > 0 && currentStruct.groupId === preFrameSpacing!.groupId ) { isDraw = true; FrameSpacingStruct.draw(req.context, preFrameSpacing, currentStruct, row, minValue, maxValue); } FrameSpacingStruct.drawSelect(currentStruct, req.context, row); preFrameSpacing = currentStruct; } if (req.frameRate) { if (isDraw) { this.drawDashedLines(Object.values(smallTickStandard), req, row, minValue, maxValue); } } } private setHoverStruct(findStructList: Array<FrameSpacingStruct>, row: TraceRow<FrameSpacingStruct>) { let find = false; if (findStructList.length > 0) { find = true; let hoverIndex: number = 0; if (findStructList.length > unitIndex) { hoverIndex = Math.ceil(findStructList.length / multiple); } FrameSpacingStruct.hoverFrameSpacingStruct = findStructList[hoverIndex]; } if (!find && row.isHover) { FrameSpacingStruct.hoverFrameSpacingStruct = undefined; } } private drawPoint( ctx: CanvasRenderingContext2D, currentStruct: FrameSpacingStruct, row: TraceRow<FrameSpacingStruct>, minValue: number, maxValue: number ): void { let currentPointY = row.frame.height - Math.floor( ((currentStruct.frameSpacingResult! - minValue) * (row.frame.height - padding * multiple)) / (maxValue - minValue) ) - padding; ctx.beginPath(); ctx.lineWidth = 1; ctx.globalAlpha = 1; ctx.arc(currentStruct.frame!.x, currentPointY, multiple, 0, multiple * Math.PI); ctx.strokeStyle = ColorUtils.ANIMATION_COLOR[2]; ctx.fillStyle = ColorUtils.ANIMATION_COLOR[2]; ctx.stroke(); ctx.fill(); ctx.closePath(); } private drawDashedLines( dashedLines: number[], req: { useCache: boolean; context: CanvasRenderingContext2D; type: string; frameRate: number }, row: TraceRow<FrameSpacingStruct>, minVale: number, maxValue: number ): void { for (let i = 0; i < dashedLines.length; i++) { FrameSpacingStruct.drawParallelLine(req.context, row.frame, dashedLines, i, minVale, maxValue); } } private frameSpacing( frameSpacingList: Array<FrameSpacingStruct>, frameSpacingFilter: Array<FrameSpacingStruct>, startNS: number, endNS: number, totalNS: number, row: TraceRow<FrameSpacingStruct>, animationRanges: AnimationRanges[], use: boolean ): void { let frame: Rect = row.frame; let modelName: string | undefined | null = row.getAttribute('model-name'); if ((use || !TraceRow.range!.refresh) && frameSpacingFilter.length > 0) { frameSpacingList.length = 0; let groupIdList: number[] = []; for (let index = 0; index < frameSpacingFilter.length; index++) { let item = frameSpacingFilter[index]; if (modelName === item.nameId) { item.groupId = invalidGroupId; for (let rangeIndex = 0; rangeIndex < animationRanges.length; rangeIndex++) { let currentRange = animationRanges[rangeIndex]; if (item.currentTs >= currentRange.start && item.currentTs <= currentRange.end) { item.groupId = currentRange.start; break; } } if ( item.currentTs < startNS && index + unitIndex < frameSpacingFilter.length && frameSpacingFilter[index + unitIndex].currentTs >= startNS && item.groupId !== invalidGroupId ) { this.refreshFrame(frameSpacingList, item, startNS, endNS, totalNS, frame, groupIdList); } if (item.currentTs >= startNS && item.currentTs <= endNS && item.groupId !== invalidGroupId) { this.refreshFrame(frameSpacingList, item, startNS, endNS, totalNS, frame, groupIdList); } if (item.currentTs > endNS && item.groupId !== invalidGroupId) { this.refreshFrame(frameSpacingList, item, startNS, endNS, totalNS, frame, groupIdList); break; } } } this.grouping(groupIdList, frameSpacingList); return; } } private grouping(groupIdList: number[], frameSpacingFilter: Array<FrameSpacingStruct>): void { let simpleGroup = groupIdList.filter((groupId) => { return groupId !== invalidGroupId && groupIdList.indexOf(groupId) === groupIdList.lastIndexOf(groupId); }); frameSpacingFilter.forEach((frameSpacing) => { if (simpleGroup.indexOf(frameSpacing.groupId!) > invalidGroupId) { frameSpacing.groupId = 0; frameSpacing.frameSpacingResult = 0; frameSpacing.preFrameWidth = 0; frameSpacing.preFrameHeight = 0; frameSpacing.preTs = 0; frameSpacing.preX = 0; frameSpacing.preY = 0; } }); } private refreshFrame( frameSpacingFilter: Array<FrameSpacingStruct>, item: FrameSpacingStruct, startNS: number, endNS: number, totalNS: number, frame: Rect, groupIdList: number[] ): void { frameSpacingFilter.push(item); FrameSpacingStruct.setFrameSpacingFrame(item, startNS, endNS, totalNS, frame); groupIdList.push(item.groupId!); } private maxMinData(tickStandardMin: number, tickStandardMax: number, filter: FrameSpacingStruct[]): [number, number] { let min = Math.min.apply( Math, filter.map((filterData) => { return filterData.frameSpacingResult!; }) ); let max = Math.max.apply( Math, filter.map((filterData) => { return filterData.frameSpacingResult!; }) ); if (max < tickStandardMax) { max = tickStandardMax + padding; } if (min > tickStandardMin) { min = tickStandardMin; } return [min, max]; } } export function FrameSpacingStructOnClick(clickRowType: string, sp: SpSystemTrace) { return new Promise((resolve,reject) => { if (clickRowType === TraceRow.ROW_TYPE_FRAME_SPACING && FrameSpacingStruct.hoverFrameSpacingStruct) { FrameSpacingStruct.selectFrameSpacingStruct = FrameSpacingStruct.hoverFrameSpacingStruct; sp.traceSheetEL?.displayFrameSpacingData(FrameSpacingStruct.selectFrameSpacingStruct); sp.timerShaftEL?.modifyFlagList(undefined); reject(); }else{ resolve(null); } }); } export class FrameSpacingStruct extends BaseStruct { static hoverFrameSpacingStruct: FrameSpacingStruct | undefined; static selectFrameSpacingStruct: FrameSpacingStruct | undefined; physicalWidth: number | undefined; physicalHeight: number | undefined; preTs: number | undefined; currentTs: number = 0; frameSpacingResult: number | undefined; id: number = 0; groupId: number | undefined; currentFrameWidth: number | undefined; preFrameWidth: number | undefined; currentFrameHeight: number | undefined; preFrameHeight: number | undefined; x: number | undefined; y: number | undefined; preX: number | undefined; preY: number | undefined; nameId: string | undefined; static setFrameSpacingFrame( frameSpacingNode: FrameSpacingStruct, startNS: number, endNS: number, totalNS: number, row: Rect ): void { let pointX = ns2x(frameSpacingNode.currentTs || 0, startNS, endNS, totalNS, row); if (!frameSpacingNode.frame) { frameSpacingNode.frame = new Rect(0, 0, 0, 0); } frameSpacingNode.frame.x = Math.floor(pointX); } static isSelect(currSpacingStruct: FrameSpacingStruct): boolean | 0 | undefined { return ( TraceRow.rangeSelectObject && TraceRow.rangeSelectObject.startNS && TraceRow.rangeSelectObject.endNS && currSpacingStruct.currentTs >= TraceRow.rangeSelectObject.startNS && currSpacingStruct.currentTs <= TraceRow.rangeSelectObject.endNS ); } static refreshHoverStruct( preFrameSpacing: FrameSpacingStruct, frameSpacing: FrameSpacingStruct, row: TraceRow<FrameSpacingStruct>, minValue: number, maxValue: number ): void { if (frameSpacing.frame) { frameSpacing.frame.y = row.frame.height - Math.floor( ((frameSpacing.frameSpacingResult! - minValue) * (row.frame.height - padding * multiple)) / (maxValue - minValue) ) - padding; } } static draw( ctx: CanvasRenderingContext2D, preFrameSpacing: FrameSpacingStruct, currentStruct: FrameSpacingStruct, rowFrame: TraceRow<FrameSpacingStruct>, minValue: number, maxValue: number ): void { if (currentStruct.frame && preFrameSpacing.frame) { this.drawPolyline(ctx, preFrameSpacing, currentStruct, rowFrame, minValue, maxValue); } } static drawSelect( currentStruct: FrameSpacingStruct, ctx: CanvasRenderingContext2D, rowFrame: TraceRow<FrameSpacingStruct> ): void { if ( currentStruct.frame && currentStruct.currentTs > TraceRow.range!.startNS && currentStruct.currentTs < TraceRow.range!.endNS ) { if ( (currentStruct === FrameSpacingStruct.hoverFrameSpacingStruct && rowFrame.isHover) || currentStruct === FrameSpacingStruct.selectFrameSpacingStruct ) { ctx.lineWidth = 3; ctx.beginPath(); ctx.arc(currentStruct.frame.x, currentStruct.frame.y, selectRadius, 0, multiple * Math.PI); ctx.strokeStyle = ColorUtils.ANIMATION_COLOR[7]; ctx.lineWidth = 2; ctx.globalAlpha = 1; ctx.fillStyle = ColorUtils.ANIMATION_COLOR[9]; ctx.stroke(); ctx.fill(); ctx.closePath(); } } if (rowFrame.getAttribute('check-type') === '2' && FrameSpacingStruct.isSelect(currentStruct)) { ctx.beginPath(); ctx.lineWidth = 3; ctx.arc(currentStruct.frame!.x, currentStruct.frame!.y, selectRadius, 0, multiple * Math.PI); ctx.strokeStyle = ColorUtils.ANIMATION_COLOR[7]; ctx.fillStyle = ColorUtils.ANIMATION_COLOR[9]; ctx.lineWidth = 2; ctx.globalAlpha = 1; ctx.stroke(); ctx.fill(); ctx.closePath(); } } static drawParallelLine( ctx: CanvasRenderingContext2D, rowFrame: Rect, dashedLines: number[], index: number, minValue: number, maxValue: number ): void { let pointY = rowFrame.height - Math.floor(((dashedLines[index] - minValue) * (rowFrame.height - padding * multiple)) / (maxValue - minValue)) - padding; let lineDash = 10; let textPadding = 4; ctx.beginPath(); ctx.lineWidth = 2; ctx.setLineDash([lineDash]); ctx.strokeStyle = ColorUtils.ANIMATION_COLOR[6]; ctx.fillStyle = ColorUtils.ANIMATION_COLOR[6]; ctx.moveTo(0, pointY); ctx.lineTo(rowFrame.width, pointY); ctx.stroke(); ctx.strokeStyle = ColorUtils.ANIMATION_COLOR[3]; ctx.fillStyle = ColorUtils.ANIMATION_COLOR[3]; if (index === 0) { ctx.fillText(dashedLines[index].toString(), 0, pointY + multiple * textPadding); } else if (index === unitIndex) { ctx.fillText(dashedLines[index].toString(), 0, pointY + textPadding); } else { ctx.fillText(dashedLines[index].toString(), 0, pointY - textPadding); } ctx.closePath(); } static drawPolyline( ctx: CanvasRenderingContext2D, preFrameSpacing: FrameSpacingStruct, currentStruct: FrameSpacingStruct, rowFrame: TraceRow<FrameSpacingStruct>, minValue: number, maxValue: number ): void { ctx.beginPath(); let prePointY = rowFrame.frame.height - Math.floor( ((preFrameSpacing.frameSpacingResult! - minValue) * (rowFrame.frame.height - padding * multiple)) / (maxValue - minValue) ) - padding; let currentPointY = rowFrame.frame.height - Math.floor( ((currentStruct.frameSpacingResult! - minValue) * (rowFrame.frame.height - padding * multiple)) / (maxValue - minValue) ) - padding; if (preFrameSpacing.frame && currentStruct.frame) { ctx.strokeStyle = ColorUtils.ANIMATION_COLOR[2]; ctx.fillStyle = ColorUtils.ANIMATION_COLOR[2]; ctx.lineWidth = 2; ctx.setLineDash([0]); ctx.moveTo(preFrameSpacing.frame.x, prePointY); ctx.lineTo(currentStruct.frame.x, currentPointY); ctx.stroke(); ctx.closePath(); FrameSpacingStruct.drawSelect(preFrameSpacing, ctx, rowFrame); } } } const padding = 3; const invalidGroupId: number = -1; const multiple: number = 2; const unitIndex: number = 1; const selectRadius: number = 3; const smallTick = { 60: { firstLine: 11.4, secondLine: 13, thirdLine: 14.6, }, 90: { firstLine: 13.7, secondLine: 19.4, thirdLine: 25, }, 120: { firstLine: 13.7, secondLine: 19.4, thirdLine: 25, }, };