From 8f0b8299493d4afa53c3bd3b27ebb08ce92a9cd8 Mon Sep 17 00:00:00 2001 From: esterTion Date: Thu, 7 Oct 2021 00:44:26 +0800 Subject: [PATCH] change hold render logic --- main.js | 206 ++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 124 insertions(+), 82 deletions(-) diff --git a/main.js b/main.js index 8c15f07..734fa3b 100644 --- a/main.js +++ b/main.js @@ -45,10 +45,9 @@ let sflTsList = [] let laneToggleList = [] let idOffsetMap = [] let prevIdMap = {} -let startedHoldList = {} -let startedHoldReverseList = {} let chartHeader = {} let reverseSection = [] +let holdList = [] const arrowCanvas = { in: document.createElement('canvas'), @@ -213,10 +212,9 @@ function parseNotesFromText(text) {noteList = []; sflTsList = [] idOffsetMap = [] prevIdMap = {} - startedHoldList = {} - startedHoldReverseList = {} chartHeader = {} reverseSection = [] + holdList = [] const controlDupFix = {} const lines = text.trim().replace(/ +/g, '\t').split('\n'); @@ -391,8 +389,32 @@ function parseNotesFromText(text) {noteList = []; holdChain[k].noteWidth = (endWidth - startWidth) * (k - startIndex) / segments + startWidth } } + + let distanceMax = 0, distanceMin = Infinity + let timeStamp = Infinity, timeStampEnd = 0 + const hold = { + nodes: holdChain.map(i => { + distanceMax = Math.max(i.distance, distanceMax) + distanceMin = Math.min(i.distance, distanceMin) + timeStamp = Math.min(i.timestamp, timeStamp) + timeStampEnd = Math.max(i.timestamp, timeStampEnd) + return { + distance: i.distance, + timestamp: i.timestamp, + laneOffset: i.laneOffset, + noteWidth: i.noteWidth + } + }), + distanceMin, + distanceMax, + timeStamp, + timeStampEnd, + } + holdList.push(hold) } }) + // remove hold node & end from list + noteListForPlayback = noteListForPlayback.filter(i => i.noteType !== '10' && i.noteType !== '11') // reverse section const reverseStartList = controlList.filter(i => i.cmdType === '6') @@ -619,83 +641,69 @@ function render(now) { ctx.stroke() }) } - const startedHoldListKeys = Object.keys(startedHoldList) - if (notesToRender.holdBody.length || startedHoldListKeys.length) { - const drawnHold = {} - const drawHoldForId = id => { - if (drawnHold[id]) return - drawnHold[id] = true - const i = noteListForPlayback[idOffsetMap[id]] - const nextPart = noteListForPlayback[idOffsetMap[i.extParam2]] - if (nextPart == undefined) return - const startDistance = i.distance, endDistance = nextPart.distance - if (endDistance < currentDistance) return - if (startDistance > currentDistance + RENDER_DISTANCE) return - let startOffset = i.laneOffset, endOffset = nextPart.laneOffset - //if (startOffset == 59 && endOffset == 0) endOffset = 60 - //else if (startOffset == 0 && endOffset == 59) startOffset = 60 - - let startWidth = i.noteWidth, endWidth = nextPart.noteWidth - let actualStartOffset = startOffset, actualStartWidth = startWidth, actualStartDistance = i.distance, actualEndOffset = endOffset, actualEndWidth = endWidth, actualEndDistance = nextPart.distance - if (actualStartDistance < currentDistance) { - actualStartOffset = (endOffset - startOffset) * (currentDistance - startDistance) / (endDistance - startDistance) + startOffset - actualStartWidth = (endWidth - startWidth) * (currentDistance - startDistance) / (endDistance - startDistance) + startWidth - actualStartDistance = currentDistance - } - if (actualEndDistance > currentDistance + RENDER_DISTANCE) { - actualEndOffset = (endOffset - startOffset) * (currentDistance + RENDER_DISTANCE - startDistance) / (endDistance - startDistance) + endOffset - actualEndWidth = (endWidth - startWidth) * (currentDistance + RENDER_DISTANCE - startDistance) / (endDistance - startDistance) + endWidth - actualEndDistance = currentDistance + RENDER_DISTANCE - } - const r = distanceToRenderRadius(maxR, Math.max(actualStartDistance - currentDistance, 0) / RENDER_DISTANCE) - const start = 60 - actualStartOffset - actualStartWidth, end = 60 - actualStartOffset + { + const drawHoldList = getHoldsForDraw(currentDistance, currentTs, drawDistance); + ctx.fillStyle = 'rgba(207,162,93, 0.7)' + drawHoldList.forEach(nodes => { ctx.beginPath() - ctx.arc( - centerX, centerY, - r, - Math.PI * (start / 30), Math.PI * (end / 30) - ) - const r2 = distanceToRenderRadius(maxR, Math.min(actualEndDistance - currentDistance, RENDER_DISTANCE) / RENDER_DISTANCE) - const start2 = 60 - actualEndOffset - actualEndWidth, end2 = 60 - actualEndOffset - ctx.arc( - centerX, centerY, - r2, - Math.PI * (end2 / 30), Math.PI * (start2 / 30), - true - ) + const nodeCount = nodes.length + if (nodes[0].distance < currentDistance) { // clip to outer ring + let clipDistance = currentDistance + let clipOffset = (nodes[1].laneOffset - nodes[0].laneOffset) * (clipDistance - nodes[0].distance) / (nodes[1].distance - nodes[0].distance) + nodes[0].laneOffset + let clipWidth = (nodes[1].noteWidth - nodes[0].noteWidth) * (clipDistance - nodes[0].distance) / (nodes[1].distance - nodes[0].distance) + nodes[0].noteWidth + nodes[0] = { + distance: clipDistance, + laneOffset: clipOffset, + noteWidth: clipWidth, + } + } + if (nodes[nodeCount - 1].distance > currentDistance+RENDER_DISTANCE) { // clip to center + let clipDistance = currentDistance+RENDER_DISTANCE + let clipOffset = (nodes[nodeCount - 1].laneOffset - nodes[nodeCount - 2].laneOffset) * (clipDistance - nodes[nodeCount - 2].distance) / (nodes[nodeCount - 1].distance - nodes[nodeCount - 2].distance) + nodes[nodeCount - 2].laneOffset + let clipWidth = (nodes[nodeCount - 1].noteWidth - nodes[nodeCount - 2].noteWidth) * (clipDistance - nodes[nodeCount - 2].distance) / (nodes[nodeCount - 1].distance - nodes[nodeCount - 2].distance) + nodes[nodeCount - 2].noteWidth + nodes[nodeCount - 1] = { + distance: clipDistance, + laneOffset: clipOffset, + noteWidth: clipWidth, + } + } + for (let i=0; i=0; i--) { + const r = distanceToRenderRadius(maxR, Math.min(nodes[i].distance - currentDistance, RENDER_DISTANCE) / RENDER_DISTANCE) + const start = 60 - nodes[i].laneOffset - nodes[i].noteWidth, end = 60 - nodes[i].laneOffset + if (i === nodeCount-1) { + ctx.arc( + centerX, centerY, + r, + Math.PI * (end / 30), Math.PI * (start / 30), + true + ) + } else { + ctx.arc( + centerX, centerY, + r, + Math.PI * (start / 30), Math.PI * (start / 30) + ) + } + } ctx.closePath() ctx.fill() - } - ctx.fillStyle = 'rgba(207,162,93, 0.7)' - notesToRender.holdBody.forEach(i => { - if (i.noteType == '9' || i.noteType == '25') { - let endId = i.id - let hold = noteListForPlayback[idOffsetMap[endId]] - while (hold.noteType !== '11') { - drawHoldForId(hold.id) - endId = hold.extParam2 - hold = noteListForPlayback[idOffsetMap[endId]] - } - startedHoldList[i.id] = endId - startedHoldReverseList[endId] = i.id - } else { - drawHoldForId(prevIdMap[i.id]) - drawHoldForId(i.id) - } - }) - startedHoldListKeys.forEach(id => { - let hold = noteListForPlayback[idOffsetMap[id]] - while (hold.noteType !== '11') { - if (hold.distance > currentDistance + RENDER_DISTANCE) return - drawHoldForId(hold.id) - id = hold.extParam2 - hold = noteListForPlayback[idOffsetMap[id]] - } - if (hold.distance < currentDistance) { - const startId = startedHoldReverseList[id] - delete startedHoldList[startId] - delete startedHoldReverseList[id] - } }) } @@ -853,8 +861,6 @@ window.stop = function () { currentTs = 0 bgm.currentTime = 0 if (enableBga) bga.currentTime = 0 - startedHoldList = {} - startedHoldReverseList = {} drawForNextFrame = true sflOffset = 0 sfl = 1 @@ -902,6 +908,44 @@ function getNotesForDraw(currentDistance, renderDistance = RENDER_DISTANCE) { .filter(i => i.distance > currentDistance && i.distance < currentDistance + renderDistance) } window.getNotesForDraw = getNotesForDraw; +function getHoldsForDraw(currentDistance, currentTs, renderDistance = RENDER_DISTANCE) { + const filteredHoldList = holdList.filter(i => ( + (i.distanceMax > currentDistance || i.distanceMin < currentDistance + renderDistance) && i.timeStampEnd > currentTs + )) + const drawHoldList = [] + filteredHoldList.forEach(hold => { + let offset = 0 + let head = 0, tail = 1 + const nodeCount = hold.nodes.length + while (offset < nodeCount) { + if (hold.nodes[offset].distance < currentDistance) { // after leave, set head to last one before leaving current render + head = offset + } else if (hold.nodes[offset].distance > currentDistance + renderDistance) { // before enter, set tail to first one before enter render + tail = offset + if (tail-head > 0) drawHoldList.push(hold.nodes.slice(head, tail + 1)) + head = offset + 1 + tail = offset + 2 + } else if (offset === nodeCount - 1) { // last one in chain + tail = offset + if (tail-head > 0) drawHoldList.push(hold.nodes.slice(head, tail + 1)) + } else if (offset > 0 && offset < nodeCount - 1) { // not first or last one in chain, if change render direction (due to sfl), break render chain + if (hold.nodes[offset].distance > hold.nodes[offset - 1].distance && hold.nodes[offset].distance > hold.nodes[offset + 1].distance) { + tail = offset + if (tail-head > 0) drawHoldList.push(hold.nodes.slice(head, tail + 1)) + head = offset + } else if (hold.nodes[offset].distance < hold.nodes[offset - 1].distance && hold.nodes[offset].distance < hold.nodes[offset + 1].distance) { + tail = offset + if (tail-head > 0) drawHoldList.push(hold.nodes.slice(head, tail + 1)) + head = offset + } + } + offset++ + } + }) + return drawHoldList.filter(i => ( + i[i.length - 1].timestamp > currentTs + )) +} const pendingLaneChange = [] const transitionLength = 80 @@ -963,8 +1007,6 @@ bgm.addEventListener('seeked', function (e) { currentTs = Math.round(this.currentTime * 1000) if (enableBga) bga.currentTime = this.currentTime startNextFrame = true - startedHoldList = {} - startedHoldReverseList = {} }) bgm.addEventListener('pause', pause) bgm.addEventListener('play', play)