class CClock { transform(time) { return time; } } class CRTClock extends CClock { /** * @param {number[][]} R */ constructor(R) { super(); this.R = R; } /** * @param {Date} time * @returns {number[]} */ transform(time) { const h = time.getHours() / 24.0; const m = time.getMinutes() / 60.0; const s = time.getSeconds() / 60.0; const ret = [ h * this.R[0][0] + m * this.R[0][1] + s * this.R[0][2], h * this.R[1][0] + m * this.R[1][1] + s * this.R[1][2], h * this.R[2][0] + m * this.R[2][1] + s * this.R[2][2] ]; return ret; } } class CRTCosineClock extends CRTClock { /** * @param {Date} time * @returns {number[]} */ transform(time) { // Calculate raw progress (0.0 ~ 1.0) const hProg = (time.getHours() + time.getMinutes() / 60.0 + time.getSeconds() / 3600.0) / 24.0; const mProg = (time.getMinutes() + time.getSeconds() / 60.0) / 60.0; const sProg = (time.getSeconds() + time.getMilliseconds() / 1000.0) / 60.0; // Use Cosine wave (0 -> 1 -> 0) to ensure continuity at the cycle boundary (1.0 -> 0.0) // Formula: 0.5 - 0.5 * cos(2 * PI * t) const h = 0.5 - 0.5 * Math.cos(2 * Math.PI * hProg); const m = 0.5 - 0.5 * Math.cos(2 * Math.PI * mProg); const s = 0.5 - 0.5 * Math.cos(2 * Math.PI * sProg); // Matrix multiplication const ret = [ h * this.R[0][0] + m * this.R[0][1] + s * this.R[0][2], h * this.R[1][0] + m * this.R[1][1] + s * this.R[1][2], h * this.R[2][0] + m * this.R[2][1] + s * this.R[2][2] ]; return ret; } }