/************************ Ordinal Three body Orbits ************************/ function calculateDistance(x1, y1, x2, y2) { return Math.sqrt(Math.pow((x2 - x1), 2) + Math.pow((y2 - y1), 2)); } class CanvasBasic { constructor(canvasId, dpr) { this.canvas = document.getElementById(canvasId); this.ctx = this.canvas.getContext("2d"); this.dpr = dpr } clear() { this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); } fade(coverColor) { this.ctx.fillStyle = coverColor this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); } setCanvas(size, zRatio) { let z = zRatio * size; this.canvas.width = size * this.dpr; this.canvas.height = size * this.dpr; this.canvas.style.width = size + 'px' this.canvas.style.height = size + 'px' this.canvas.style.position = "absolute"; this.canvas.style.left = "50%"; this.canvas.style.top = "50%"; this.canvas.style.position = "absolute"; this.canvas.style.transform = `translate3d(-50%,-50%,${z}px)` } resetCanvas(size, zRatio) { this.setCanvas(size, zRatio); this.clear(); } setContextStyle(color, shadowColor, shadowBlur, lineWidth) { this.ctx.strokeStyle = color; this.ctx.fillStyle = color; this.ctx.shadowColor = shadowColor; this.ctx.shadowBlur = shadowBlur; this.ctx.lineWidth = lineWidth * this.dpr; } } class Canvas3DDashboard extends CanvasBasic { constructor(canvasId, dpr) { super(canvasId, dpr); } drawRect(gridLen, numOfGrids, corner) { gridLen = gridLen * this.dpr; const size = gridLen * numOfGrids; if (typeof corner === 'undefined') { this.ctx.setLineDash([]) } else { let patten = Array(4).fill([(numOfGrids - corner * 2), corner * 2]).flat() patten.unshift(corner) this.ctx.setLineDash(patten.map(num => num * gridLen)) } this.ctx.strokeRect(this.canvas.width / 2 - size / 2, this.canvas.height / 2 - size / 2, size, size); } drawLine(gridLen, numOfGrids, corner) { gridLen = gridLen * this.dpr; const size = gridLen * numOfGrids this.ctx.setLineDash([gridLen * corner, gridLen * (1 - corner * 2), gridLen * corner, 0]) for (let i = 1; i < numOfGrids; i++) { this.ctx.moveTo(this.canvas.width / 2 - size / 2, this.canvas.height / 2 - size / 2 + gridLen * i); this.ctx.lineTo(this.canvas.width / 2 - size / 2 + size, this.canvas.height / 2 - size / 2 + gridLen * i); this.ctx.moveTo(this.canvas.width / 2 - size / 2 + gridLen * i, this.canvas.height / 2 - size / 2); this.ctx.lineTo(this.canvas.width / 2 - size / 2 + gridLen * i, this.canvas.height / 2 - size / 2 + size); } this.ctx.stroke(); } drawDecoration(gridLen, numOfGrids, deltaR) { gridLen = gridLen * this.dpr; const size = gridLen * numOfGrids const delta = gridLen * deltaR this.ctx.setLineDash([]); this.ctx.beginPath(); const drawSide = (sign) => { this.ctx.moveTo(this.canvas.width / 2 + sign * (size / 2 + delta), this.canvas.height / 2 - (size * 0.2 + delta)); this.ctx.lineTo(this.canvas.width / 2 + sign * (size / 2), this.canvas.height / 2 - (size * 0.2)); this.ctx.lineTo(this.canvas.width / 2 + sign * (size / 2), this.canvas.height / 2 + (size * 0.2)); this.ctx.lineTo(this.canvas.width / 2 + sign * (size / 2 + delta), this.canvas.height / 2 + (size * 0.2 + delta)); } drawSide(-1); // Left side drawSide(1); // Right side this.ctx.stroke(); } } class EffectBasic { constructor(effectsSettings, seed) { this.canvasHeight = window.innerHeight; this.dpr = window.devicePixelRatio || 1; this.seed = seed === undefined ? this.fetchBlockHeight() : seed; this.settings = effectsSettings; this.checkEra(this.seed) this.createDiv() this.UIBack = new Canvas3DDashboard('canvasUIBack', this.dpr); this.UIMid = new Canvas3DDashboard('canvasUIMid', this.dpr); this.UIFront = new Canvas3DDashboard('canvasUIFront', this.dpr); this.normalXFor3D = 0; this.normalYFor3D = 0; this.degFactorFor3D = 0; this.aimDegFactorFor3D = 0; this.mouseHasLeft = false; this.mouseInputPanel = document.getElementById('mouseInputPanel'); this.card3D = document.getElementById('card3D'); this.maxDis4card3DEffects = Math.min(window.innerHeight / 2, window.innerWidth / 2) this.width = window.innerWidth this.height = window.innerWidth } init() { this.fitCanvas() this.apply3DcardEffects() } checkEra(seed) { const difficultyAdjustmentPeriod = 2016; const halvingPeriod = 210000; const era = Math.floor((seed - 1) / difficultyAdjustmentPeriod); const epoch = Math.floor((seed - 1) / halvingPeriod); this.firstBlockAfterDiffAdjust = ((seed - 1) % difficultyAdjustmentPeriod === 0) this.firstBlockAfterHalving = ((seed - 1) % halvingPeriod === 0) this.shouldResetEachPeriod = !(this.firstBlockAfterDiffAdjust || this.firstBlockAfterHalving) console.log("era: ", era, 'havling epoch: ', epoch, 'firstBlockAfterDiffAdjust: ', this.firstBlockAfterDiffAdjust, "firstBlockAfterHalving: ", this.firstBlockAfterHalving, "shouldRestEachPeriod: ", this.shouldResetEachPeriod) this.color = this.settings.colorSchemes[era % this.settings.colorSchemes.length]; this.solidColor = `rgb(${this.color[0]},${this.color[1]},${this.color[2]})`; this.shallowColor = `rgba(${this.color[0]},${this.color[1]},${this.color[2]},0.7)`; this.dimColor = `rgba(${this.color[0]},${this.color[1]},${this.color[2]},0.5)`; this.loomColor = `rgba(${this.color[0]},${this.color[1]},${this.color[2]},0.2)`; this.energetic = this.firstBlockAfterHalving this.era = era this.epoch = epoch } fitCanvas(canvasHeight) { this.width = canvasHeight this.height = canvasHeight this.canvasHeight = canvasHeight this.maxDis4card3DEffects = Math.min(window.innerHeight / 2, window.innerWidth / 2) if (canvasHeight <= this.settings.canvasSizeThreshold) { this.useCard3DEffects = false this.closeDashboard() } else { this.useCard3DEffects = this.settings.sholdUseCard3DEffects this.draw3DDashboardDecoration(canvasHeight) } } onEachFrame() { this.handleMouseLeaveFor3Dcard() } apply3DcardEffects() { const updateEffect = (clientX, clientY) => { if (this.useCard3DEffects) { const dx = clientX - (this.card3D.getBoundingClientRect().left + this.card3D.clientWidth / 2); const dy = clientY - (this.card3D.getBoundingClientRect().top + this.card3D.clientHeight / 2); const angle = Math.atan2(dy, dx); const distance = Math.sqrt(dx * dx + dy * dy); this.normalXFor3D = Math.cos(angle); this.normalYFor3D = Math.sin(angle); this.aimDegFactorFor3D = Math.min(distance / this.maxDis4card3DEffects, 1); } } const projectionEnd = (e) => { this.energetic = this.firstBlockAfterHalving; } const projectionBegin = (e) => { if (e.touches.length == 3) { e.preventDefault(); this.energetic = true; this.aimDegFactorFor3D = 0; } else { this.energetic = this.firstBlockAfterHalving; } } const handleTouchMove = (e) => { if (e.touches.length <= 1) { e.preventDefault(); const firstTouch = e.touches[0]; updateEffect(firstTouch.clientX, firstTouch.clientY); } } this.mouseInputPanel.addEventListener('mousemove', (e) => { updateEffect(e.clientX, e.clientY); }); this.mouseInputPanel.addEventListener('touchmove', handleTouchMove, { passive: false }); this.mouseInputPanel.addEventListener('touchstart', projectionBegin, { passive: false }); this.mouseInputPanel.addEventListener('touchend', projectionEnd, false); this.mouseInputPanel.addEventListener('touchcancel', projectionEnd, false); this.mouseInputPanel.onmouseenter = this.mouseInputPanel.ontouchstart = () => { this.mouseHasLeft = false; }; this.mouseInputPanel.onmouseleave = this.mouseInputPanel.ontouchend = () => { this.mouseHasLeft = true; }; } handleMouseLeaveFor3Dcard() { if (this.useCard3DEffects) { if (this.mouseHasLeft) { this.degFactorFor3D = Math.max(this.degFactorFor3D - this.settings.anglePerFrame4card3DEffects, 0); } else { this.degFactorFor3D = this.degFactorFor3D + this.settings.anglePerFrame4card3DEffects < this.aimDegFactorFor3D ? this.degFactorFor3D + this.settings.anglePerFrame4card3DEffects : this.aimDegFactorFor3D; } this.card3D.style.transform = `rotate3d(${-this.normalYFor3D},${this.normalXFor3D},0,${this.degFactorFor3D * this.settings.max3DDegree}deg)`; } else { this.card3D.style.transform = `rotate3d(0,0,0,0deg)`; } } createDiv() { var divAll = document.createElement('div'); divAll.className = "ThreeBodyProblem-container isFullScreenWide isUnselectable"; divAll.style.cssText = `height: 100vh;background-color: ${this.settings.backgroundColor};`; var divPanel = document.createElement('div'); divPanel.id = "panel"; divPanel.style.cssText = "perspective:1500px;position:absolute;left:50%;top: 50%;transform: translate(-50%,-50%);"; var divCard3D = document.createElement('div'); divCard3D.id = "card3D"; divCard3D.style.cssText = "position: relative;width:0px;height:0px; transform-style: preserve-3d;"; var divMouseInputPanel = document.createElement('div'); divMouseInputPanel.id = "mouseInputPanel"; divMouseInputPanel.style.cssText = "position: absolute; width: 100%; height: 100vh;"; var createCanvas = function (id) { var canvas = document.createElement('canvas'); canvas.id = id; return canvas; }; var canvasIds = ['canvasUIBack', 'canvasUIMid', 'canvasUIFront', 'canvasTrace', 'canvasTail', 'canvasTop']; var canvases = canvasIds.map(createCanvas); for (var i = 0; i < canvasIds.length; i++) { divCard3D.appendChild(canvases[i]); } divPanel.appendChild(divCard3D); divAll.appendChild(divPanel); divAll.appendChild(divMouseInputPanel); document.body.appendChild(divAll); document.body.style.margin = "0"; document.body.style.padding = "0"; document.body.style.overflow = "hidden"; } closeDashboard() { this.UIBack.clear(); this.UIMid.clear(); this.UIFront.clear(); } draw3DDashboardDecoration(size) { var gridLen = size / 18 this.UIBack.resetCanvas(size, 0); this.UIBack.setContextStyle(this.loomColor, this.shallowColor, gridLen, gridLen * 0.1); this.UIBack.drawRect(gridLen, 14, 9); // this.UIBack.setContextStyle(this.loomColor, this.shallowColor, gridLen, gridLen * 0.1); // this.UIBack.drawRect(gridLen, 13); // this.UIBack.drawDecoration(gridLen, 14, 0.7); this.UIMid.resetCanvas(size, 0.03); this.UIMid.setContextStyle(this.loomColor, this.shallowColor, gridLen, gridLen * 0.03); // this.UIMid.drawRect(gridLen, 12); this.UIMid.setContextStyle(this.loomColor, this.shallowColor, gridLen, gridLen * 0.03); this.UIMid.drawLine(gridLen, 12, 0.15); this.UIFront.resetCanvas(size, 0.1); this.UIFront.setContextStyle(this.solidColor, this.shallowColor, gridLen, gridLen * 0.1); this.UIFront.drawRect(gridLen, 14, 9); } canvasNotSupported() { if (!(window.requestAnimationFrame && this.canvasTop.canvas && this.canvasTop.canvas?.getContext)) { console.log('Canvas not supported.') return true } if (!this.ctxTop) { console.log('Canvas not supported.') return true } return false } showMessage(title, content) { var canvasSize = this.canvasHeight var mainContainer = document.querySelector(".ThreeBodyProblem-container"); var container = document.createElement('div'); container.id = 'container'; container.style.cssText = `position:absolute;top:55%;left:50%;width:${canvasSize}px; height:${canvasSize}px;transform:translate3d(-50%,-50%,0px);` container.innerHTML = `
${content}