From b5a62e5a88a0ff67595b5cd61428d8a99244d0fe Mon Sep 17 00:00:00 2001 From: ElementalAlchemist Date: Tue, 12 Nov 2024 22:04:23 -0600 Subject: [PATCH 01/12] Implement canvas-based bus system --- thrimbletrimmer/driveclock/db_dawn.png | Bin 196 -> 0 bytes thrimbletrimmer/driveclock/db_day.png | Bin 196 -> 0 bytes thrimbletrimmer/driveclock/db_dusk.png | Bin 196 -> 0 bytes thrimbletrimmer/driveclock/db_night.png | Bin 196 -> 0 bytes thrimbletrimmer/driveclock/db_stop.png | Bin 0 -> 173 bytes thrimbletrimmer/driveclock/drive.css | 0 thrimbletrimmer/driveclock/drive.html | 62 +----- thrimbletrimmer/driveclock/drive.js | 276 +++++++++++++++--------- thrimbletrimmer/driveclock/point.png | Bin 0 -> 397 bytes thrimbletrimmer/driveclock/stops.png | Bin 7397 -> 0 bytes 10 files changed, 186 insertions(+), 152 deletions(-) delete mode 100644 thrimbletrimmer/driveclock/db_dawn.png delete mode 100644 thrimbletrimmer/driveclock/db_day.png delete mode 100644 thrimbletrimmer/driveclock/db_dusk.png delete mode 100644 thrimbletrimmer/driveclock/db_night.png create mode 100644 thrimbletrimmer/driveclock/db_stop.png create mode 100644 thrimbletrimmer/driveclock/drive.css create mode 100644 thrimbletrimmer/driveclock/point.png delete mode 100644 thrimbletrimmer/driveclock/stops.png diff --git a/thrimbletrimmer/driveclock/db_dawn.png b/thrimbletrimmer/driveclock/db_dawn.png deleted file mode 100644 index 552dc35c887af27018c364e409409d5b5969def0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 196 zcmeAS@N?(olHy`uVBq!ia0y~yU~d4jQ<#{6q}0Bn{y>T+z$e62Tfe=?*HJs~ShStV z?TH5rf#SuUE{-7;bKYKcJ(kE=; z_rRj@F^G}ZZXzzDUNGlCVI7E2vjWNp%R&Y2@P9AQZ^#Jq^8^{~>FVdQ&MBb@01;v| AssI20 diff --git a/thrimbletrimmer/driveclock/db_day.png b/thrimbletrimmer/driveclock/db_day.png deleted file mode 100644 index 5cf7d3a0c9b2b0b5faf2712cabc50091342dca78..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 196 zcmeAS@N?(olHy`uVBq!ia0y~yU~d4jQ<#{6q}0Bn{y>T+z$e7j@!X@Qlclxuj%_a! z+h4z&4Jcmh>EaktG3V`7M_vX)9+r)&l`7tw+!wx|=$km@EJy)CaA30cExSuBeZm%g z4=frVgBW@3CgL*c1#=D*)`18$E1-<9EL7kQ|M&9zhKw*jPms}`u6{1-oD!M<5P>#P diff --git a/thrimbletrimmer/driveclock/db_dusk.png b/thrimbletrimmer/driveclock/db_dusk.png deleted file mode 100644 index b58fb45917fc6ebf2dcf6ab60119e5728be819ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 196 zcmeAS@N?(olHy`uVBq!ia0y~yU~d4jQ<#{6q}0Bn{y>T+z$e7@{-k|(CuM2p9otwM zYGD$&5hz~l>EaktG3V`7M_vX)9+r)&l`7tw+!wx|=$km@EE`Y(L2zKQ_bt0iEPcWj zeh(}fAA=Zq?Iz+f>IHKS6xM+VH7lTuuq;&I4*&P^{DzD$KTnX+p00i_>zopr06c&; Am;e9( diff --git a/thrimbletrimmer/driveclock/db_night.png b/thrimbletrimmer/driveclock/db_night.png deleted file mode 100644 index c444a53276f8f252ce869ed69771edd918e70bdc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 196 zcmeAS@N?(olHy`uVBq!ia0y~yU~d4jQ<#{6q}0Bn{y>T+z$e62NZ8CkK}tLCn3|Mm z*3vtwK=EQv7srr_Id88z@-i6muxwPVRPo;AzVQ7--^3|r*?_!@p - - - - - -
-
-
-
-
-
- - - - - + + + Bus Progress + + + + + + + diff --git a/thrimbletrimmer/driveclock/drive.js b/thrimbletrimmer/driveclock/drive.js index 788bde4..ab571cc 100644 --- a/thrimbletrimmer/driveclock/drive.js +++ b/thrimbletrimmer/driveclock/drive.js @@ -1,122 +1,198 @@ - -const PAGE_WIDTH = 1920; -const MINUTES_PER_PAGE = 60; -const POINT_WIDTH = PAGE_WIDTH * 8 * 60 / MINUTES_PER_PAGE; -const MILES_PER_PAGE = 45; -const BUS_POSITION_X = 93; -const BASE_ODO = 109.3; -const UPDATE_INTERVAL_MS = 5000 -const WUBLOADER_URL = ""; -const SKY_URLS = { - day: "db_day.png", - dawn: "db_dawn.png", - dusk: "db_dusk.png", - night: "db_night.png", -}; -const BUS_URLS = { - day: "bus_day.png", - dawn: "bus_day.png", - dusk: "bus_day.png", - night: "bus_night.png", +const COLORS = { + day: { + sky: "#41cee2", + ground: "#e5931b", + surface: "#b77616", + }, + dusk: { + sky: "#db92be", + ground: "#dd926a", + surface: "#b17555", + }, + night: { + sky: "#121336", + ground: "#30201a", + surface: "#261a15", + }, + dawn: { + sky: "#2b2f87", + ground: "#724d41", + surface: "#5b3e34", + }, }; -function setSkyElements(left, right, timeToTransition) { - const leftElement = document.getElementById("timeofday-left"); - const rightElement = document.getElementById("timeofday-right"); - const busElement = document.getElementById("bus"); +// The width from the left side of the bus image to the front of the bus +const BUS_FRONT_OFFSET = 73; - leftElement.style.backgroundImage = `url(${SKY_URLS[left]})`; - rightElement.style.backgroundImage = `url(${SKY_URLS[right]})`; +// Start time of each day phase +const DAY_START_MINUTES = 450; +const DUSK_START_MINUTES = 1140; +const NIGHT_START_MINUTES = 1200; +const DAWN_START_MINUTES = 400; - if (left === right) { - leftElement.style.width = "100%"; - } else { - const transitionPercent = timeToTransition / MINUTES_PER_PAGE; - leftElement.style.width = `${transitionPercent * 100}%` - } +const BUS_STOP_OFFSET = 6; +const POINT_OFFSET = 17; - bus.style.backgroundImage = `url(${BUS_URLS[left]})`; -} +// Bus stop positions are recorded in miles with the 0 position +// at route start. This array can be looped every point. +const BUS_STOP_POSITIONS = [1, 55.2, 125.4, 166.3, 233.9, 295.2]; -function nextSkyTransition(timeofday, clock) { - switch (timeofday) { - case "dawn": +// The default scaling factor is 20 seconds per pixel at max speed. +// This gives us +// - 3px per minute +// - 4px per mile +let scaleFactor = 1; + +function nextPhase(timeOfDay) { + switch (timeOfDay) { case "day": - return [19 * 60, "dusk"]; // 7pm + case "dawn": + return "dusk"; case "dusk": - return [20 * 60, "night"]; // 8pm + return "night"; case "night": - return [6 * 60 + 40, "dawn"]; // 6:40am + return "dawn"; } } -function setSky(timeofday, clock) { - const [transition, newSky] = nextSkyTransition(timeofday, clock); - // 1440 minutes in 24h, this code will return time remaining even if - // the transition is in the morning and we're currently in the evening. - const timeToTransition = (1440 + transition - clock) % 1440; - if (timeToTransition < MINUTES_PER_PAGE) { - // Transition on screen - setSkyElements(timeofday, newSky, timeToTransition); - } else { - // No transition on screen - setSkyElements(timeofday, timeofday, undefined); +function phaseStartTime(timeOfDay) { + switch (timeOfDay) { + case "day": + return DAY_START_MINUTES; + case "dusk": + return DUSK_START_MINUTES; + case "night": + return NIGHT_START_MINUTES; + case "dawn": + return DAWN_START_MINUTES; } } -function setOdo(odo) { - const distancePixels = PAGE_WIDTH * (odo - BASE_ODO) / MILES_PER_PAGE; - const offset = (BUS_POSITION_X - distancePixels) % POINT_WIDTH; +function drawBackground(context, timeOfDay, leftX, width) { + const skyColor = COLORS[timeOfDay].sky; + const groundColor = COLORS[timeOfDay].ground; + const surfaceColor = COLORS[timeOfDay].surface; - const stopsElement = document.getElementById("stops"); - stopsElement.style.backgroundPosition = `${offset}px 0px`; + context.fillStyle = COLORS[timeOfDay].sky; + context.fillRect(leftX, 0, width, 100); + context.fillStyle = COLORS[timeOfDay].surface; + context.fillRect(leftX, 80, width, 1); + context.fillStyle = COLORS[timeOfDay].ground; + context.fillRect(leftX, 81, width, 7); + context.fillRect(leftX, 89, width, 3); + context.fillRect(leftX, 94, width, 2); + context.fillRect(leftX, 99, width, 1); } -async function update() { - const busDataResponse = await fetch(`${WUBLOADER_URL}/thrimshim/bus/buscam`); - if (!busDataResponse.ok) { - return; - } - const busData = await busDataResponse.json(); - console.log("Got data:", busData); - setOdo(busData.odometer); - setSky(busData.timeofday, busData.clock_minutes); -} +async function drawRoad() { + const busDataResponse = await fetch("/thrimshim/bus/buscam"); + if (!busDataResponse.ok) { + return; + } + const busData = await busDataResponse.json(); + + const canvas = document.getElementById("road"); + if (!canvas.getContext) { + return; + } + const context = canvas.getContext("2d"); + + // Clear the previous canvas before starting + context.clearRect(0, 0, 1920, 100); + + const currentTime = busData.clock_minutes; + const distance = busData.odometer; + const timeOfDay = busData.timeofday; + + drawBackground(context, timeOfDay, 0, BUS_FRONT_OFFSET); -// Initial conditions, before the first refresh finishes -setSky("day", 7 * 60); -setOdo(BASE_ODO); - -// Testing mode. Set true to enable. -const test = false; -if (test) { - let h = 0; - // Set to how long 1h of in-game time should take in real time - const hourTimeMs = 1 * 1000; - // Set to how often to update the screen - const interval = 30; - setInterval(() => { - h += interval / hourTimeMs; - setOdo(BASE_ODO + 45 * h); - if (h < 19) { - setSky("day", 60 * h); - } else { - m = (h % 24) * 60; - let tod; - if (m < 6 * 60 + 40) { - tod = "night"; - } else if (m < 19 * 60) { - tod = "dawn"; - } else if (m < 20 * 60) { - tod = "dusk"; - } else { - tod = "night"; + const maxWidth = 1920 - BUS_FRONT_OFFSET; + + // TODO Figure out scaling factor + const startMinute = busData.clock_minutes; + const timeDuration = maxWidth / 3; + + let previousTime = startMinute; + let previousTimeOfDay = timeOfDay; + let remainingDuration = timeDuration; + let x = BUS_FRONT_OFFSET; + while (remainingDuration > 0) { + const nextTimeOfDay = nextPhase(previousTimeOfDay); + const nextStartTime = phaseStartTime(nextTimeOfDay); + + let thisDuration = nextStartTime - previousTime; + if (thisDuration < 0) { + thisDuration += 1440; + } + + // TODO Figure out scaling factor + const pixelWidth = thisDuration * 3; + drawBackground(context, previousTimeOfDay, x, pixelWidth); + + remainingDuration -= thisDuration; + previousTime = nextStartTime; + previousTimeOfDay = nextTimeOfDay; + x += pixelWidth; + } + + x = BUS_FRONT_OFFSET; + const currentPointProgress = distance % 360; + let distanceToNextPoint; + if (currentPointProgress <= 109.3) { + distanceToNextPoint = 109.3 - currentPointProgress; + } else { + distanceToNextPoint = 469.3 - currentPointProgress; + } + + // TODO Figure out scaling factor + x += distanceToNextPoint * 4; + const pointImage = new Image(); + pointImage.src = "point.png"; + context.drawImage(pointImage, x - POINT_OFFSET, 0); + while (x < maxWidth) { + // TODO Figure out scaling factor + x += 360 * 4; + context.drawImage(pointImage, x - POINT_OFFSET, 0); + } + + const distanceOnRoute = (distance - 109.3) % 360; + const busStopImage = new Image(); + busStopImage.src = "db_stop.png"; + // TODO Figure out scaling factor + let distanceTracked = distanceOnRoute - BUS_FRONT_OFFSET / 4; + if (distanceTracked < 0) { + distanceTracked += 720; + } + x = 0; + while (x < 1920) { + const distanceTrackedOnRoute = distanceTracked % 360; + let nextBusStopPosition = null; + for (const busStopPosition of BUS_STOP_POSITIONS) { + if (busStopPosition >= distanceTrackedOnRoute + 1) { + nextBusStopPosition = busStopPosition; + break; } - setSky(tod, m); } - }, interval); -} else { - // Do first update immediately, then every UPDATE_INTERVAL_MS - setInterval(update, UPDATE_INTERVAL_MS); - update(); + if (nextBusStopPosition === null) { + nextBusStopPosition = 360 + BUS_STOP_POSITIONS[0]; + } + const nextBusStopDistance = nextBusStopPosition - distanceTrackedOnRoute; + distanceTracked += nextBusStopDistance; + // TODO Figure out scaling factor + x += nextBusStopDistance * 4; + context.drawImage(busStopImage, x - BUS_STOP_OFFSET, 0); + } + + const busImage = new Image(); + if (timeOfDay === "night") { + busImage.src = "bus_night.png"; + } else { + busImage.src = "bus_day.png"; + } + context.drawImage(busImage, 0, 0); } + +window.addEventListener("DOMContentLoaded", (event) => { + drawRoad(); + setInterval(drawRoad, 10000); +}); diff --git a/thrimbletrimmer/driveclock/point.png b/thrimbletrimmer/driveclock/point.png new file mode 100644 index 0000000000000000000000000000000000000000..601c57ab0bdcb430f12d4cf6358c7fcf7e57c927 GIT binary patch literal 397 zcmeAS@N?(olHy`uVBq!ia0vp^N1P;jIbW(++GCit4OaVeV;+Jy2Y-@A-4dhqi|l zTqanV*5ymvmU*8rlMJpsVZ8d-^|lVBsH8gY$zf_5T}_`0=e61XemsrWB;<>~O7U~W zM@!8wyG%BGGV^=Jd7n3X)iUP)cmJ}s$zqYa%%WQzjp=@2uCG4+*;jw)(w>rdna=Ot zLZ950S8O)hJ4^0ko9To3$!(f8LsJnRn7_(&^6^ iCf=3;1|ceF=;(O&`&pHFaLbyNAQ?|rKbLh*2~7ZGXR9~> literal 0 HcmV?d00001 diff --git a/thrimbletrimmer/driveclock/stops.png b/thrimbletrimmer/driveclock/stops.png deleted file mode 100644 index 552266e2c402b7f9b1b2b802dda9b577d1ada7f9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7397 zcmeAS@N?(olHy`uVBq!ia0y~yumLg|QaIRvWY@eGtqcq@Ydl>XLn`LHy|uCQ_6e!B zhxN}kFfZ@(VAo^OX^cL!ZGqUjs2f6Ogbp~#%;=f3t8M2&y;7FWNqeHWn&YeH9WiQ{ zXn*|kw1^4UZmm8)Eoj{i7LWxXP{A#w2w{QAXUkIA!HfpY@(hHyLs%7pmyr3T7a>)X zvh_QHM>?x0!qsK>hdpO(;Z7pmjE19`-~Vrl;y=7t->==dyTj4?4NHL6KDFGb?x78Q{25OCwsz_9T3fIfw6(~jM>v3tm79!am`4YivSbEW% z39jcu-H!l z!ynf3&*f{^c%7KB{-?Nq`{AF*Kh63)eap91n)Uq};j`g^x<{~A0Lh->%bS?N+yhgr zO%UP=+O`PZ29sZI2&qZgf`YFTE3Q5)`p5eJ;MG;s3~80`zZA@r!a&9!y!=o=aN!D8 zgvF$?PAqnDiRM4Nns0sIV~ffSH}lqS;3U-yg!+c>4=no1?bm<%eY;!V{Px>5ixF}p zvm$^la&tLSS6}@-=j(^XpHDxwIF`11Zl)B;CLuI<7s?#J@jhSPcD=tWkbZ2@mv2Q$ zqDr;wOMdfxyZHU9`~O+lR2J;t-}~5N-&H+?jU==s4yno;B=e~10D2UjGBd@&W zfldQ@*m4`mjzDO*aI#P)dH?>}Z?ml~iy?$bW=&eX7g8gUtP`wZg7aHj@^pfAHORc0 zU(dJKoB|^zI8f+E?)osV zO#b;>uX*+B!kl#AE2WiA;F@{WJ8$1T0?ez~r;k{^`up|g>B;Y1_Q7nJFx`y<#%5q> z*qM0~5-tg+WRbG=gsE}}UW8PwGD6GyvuV45+3XmwWcc{^)yn4GcYSlV-8Ps0HYdny zx5(FdbMH?Lnsq=Nrs2~?4y1rLFxRz)$oTAZhLq}H?xssfJnzN%h-S=#y|;2e*$7l_ z?2Wdq*>L;r9obh8mu~pJFMj@hjvAQZ3X&978uxaeEt5W8*wfMR_usd7KeM*)uKc11 z^932S{B2-3b#y$oxMv$ZS63$(3a;Jy`Ll)n{{R2}_-Zb7b=mpF8s>+F zrxz#;ti{0eRc=4MBjf+W@>uH?x%|rap8wk;{jvOw_B&W{*6{Q)Wwt2n{`N_I`r4$0 zfo~2se82R&qvD%1EPxVrnmEDOK>CdDX+8)y_}vABc*B}62;PB9FQj21z`)?KYR|WX zz0bO`v)0~a-821OXBA9N!JNVvwwo3Dp3z@(sSne=-wWRdUxYWmCbUzSC*;pA(z@Ki=*X@&!Lw!DGJ7(C_U zg{fg+IN`;Q;OX@46NF1O^k-bHJ`jBF;XBJud%r-F6i|aVrN*WKjrESoe%f#UFC%_` zUGMw*`;TwWzu&SKX7z`@3<|pefmiM!m2@z3iKb61{3b`VVoZe&d=&O9fhh;mRH8*F zZaGj|Z&&jZ?N&^M3hlS5F-3_Nu^f$h;sXm!WrNIU)T0Fn@!Zi|PkdNms2t7p7(qh3 zz-WO_d{|+q9N~4n*L%kATd&2{ZkyuzHyjo?4crv=D1Mw6tw>;jNiscJk&ql@2o0kZ i2_h^=W{vQQ Date: Wed, 13 Nov 2024 00:03:13 -0600 Subject: [PATCH 02/12] Implement scaling --- thrimbletrimmer/driveclock/drive.css | 9 +++++ thrimbletrimmer/driveclock/drive.html | 7 ++++ thrimbletrimmer/driveclock/drive.js | 54 +++++++++++++-------------- 3 files changed, 43 insertions(+), 27 deletions(-) diff --git a/thrimbletrimmer/driveclock/drive.css b/thrimbletrimmer/driveclock/drive.css index e69de29..368695c 100644 --- a/thrimbletrimmer/driveclock/drive.css +++ b/thrimbletrimmer/driveclock/drive.css @@ -0,0 +1,9 @@ +#scale { + display: block; + margin-top: 20px; +} + +#scale-disclaimer { + font-size: 75%; + font-style: italic; +} diff --git a/thrimbletrimmer/driveclock/drive.html b/thrimbletrimmer/driveclock/drive.html index bdcd78f..a373636 100644 --- a/thrimbletrimmer/driveclock/drive.html +++ b/thrimbletrimmer/driveclock/drive.html @@ -8,5 +8,12 @@ + +

+ Scale modifications will be applied on the next update, which occur every 10 seconds. +

diff --git a/thrimbletrimmer/driveclock/drive.js b/thrimbletrimmer/driveclock/drive.js index ab571cc..c5f6bcc 100644 --- a/thrimbletrimmer/driveclock/drive.js +++ b/thrimbletrimmer/driveclock/drive.js @@ -37,11 +37,14 @@ const POINT_OFFSET = 17; // at route start. This array can be looped every point. const BUS_STOP_POSITIONS = [1, 55.2, 125.4, 166.3, 233.9, 295.2]; -// The default scaling factor is 20 seconds per pixel at max speed. -// This gives us -// - 3px per minute -// - 4px per mile -let scaleFactor = 1; +const BUS_DAY_IMAGE = new Image(); +BUS_DAY_IMAGE.src = "bus_day.png"; +const BUS_NIGHT_IMAGE = new Image(); +BUS_NIGHT_IMAGE.src = "bus_night.png"; +const BUS_STOP_IMAGE = new Image(); +BUS_STOP_IMAGE.src = "db_stop.png"; +const POINT_IMAGE = new Image(); +POINT_IMAGE.src = "point.png"; function nextPhase(timeOfDay) { switch (timeOfDay) { @@ -107,10 +110,17 @@ async function drawRoad() { drawBackground(context, timeOfDay, 0, BUS_FRONT_OFFSET); const maxWidth = 1920 - BUS_FRONT_OFFSET; + // The default scaling factor (1) is 20 seconds per pixel at max speed. + // This gives us + // - 3px per minute + // - 4px per mile + let scaleFactor = +document.getElementById("scale-input").value; + if (scaleFactor === 0 || isNaN(scaleFactor)) { + scaleFactor = 1; + } - // TODO Figure out scaling factor const startMinute = busData.clock_minutes; - const timeDuration = maxWidth / 3; + const timeDuration = maxWidth / (3 * scaleFactor); let previousTime = startMinute; let previousTimeOfDay = timeOfDay; @@ -126,7 +136,7 @@ async function drawRoad() { } // TODO Figure out scaling factor - const pixelWidth = thisDuration * 3; + const pixelWidth = thisDuration * 3 * scaleFactor; drawBackground(context, previousTimeOfDay, x, pixelWidth); remainingDuration -= thisDuration; @@ -144,22 +154,15 @@ async function drawRoad() { distanceToNextPoint = 469.3 - currentPointProgress; } - // TODO Figure out scaling factor - x += distanceToNextPoint * 4; - const pointImage = new Image(); - pointImage.src = "point.png"; - context.drawImage(pointImage, x - POINT_OFFSET, 0); + x += distanceToNextPoint * 4 * scaleFactor; + context.drawImage(POINT_IMAGE, x - POINT_OFFSET, 0); while (x < maxWidth) { - // TODO Figure out scaling factor - x += 360 * 4; - context.drawImage(pointImage, x - POINT_OFFSET, 0); + x += 360 * 4 * scaleFactor; + context.drawImage(POINT_IMAGE, x - POINT_OFFSET, 0); } const distanceOnRoute = (distance - 109.3) % 360; - const busStopImage = new Image(); - busStopImage.src = "db_stop.png"; - // TODO Figure out scaling factor - let distanceTracked = distanceOnRoute - BUS_FRONT_OFFSET / 4; + let distanceTracked = distanceOnRoute - BUS_FRONT_OFFSET / (4 * scaleFactor); if (distanceTracked < 0) { distanceTracked += 720; } @@ -178,18 +181,15 @@ async function drawRoad() { } const nextBusStopDistance = nextBusStopPosition - distanceTrackedOnRoute; distanceTracked += nextBusStopDistance; - // TODO Figure out scaling factor - x += nextBusStopDistance * 4; - context.drawImage(busStopImage, x - BUS_STOP_OFFSET, 0); + x += nextBusStopDistance * 4 * scaleFactor; + context.drawImage(BUS_STOP_IMAGE, x - BUS_STOP_OFFSET, 0); } - const busImage = new Image(); if (timeOfDay === "night") { - busImage.src = "bus_night.png"; + context.drawImage(BUS_NIGHT_IMAGE, 0, 0); } else { - busImage.src = "bus_day.png"; + context.drawImage(BUS_DAY_IMAGE, 0, 0); } - context.drawImage(busImage, 0, 0); } window.addEventListener("DOMContentLoaded", (event) => { From 4098619de9412351280eefd79bacd5fe410591e8 Mon Sep 17 00:00:00 2001 From: ElementalAlchemist Date: Wed, 13 Nov 2024 00:15:12 -0600 Subject: [PATCH 03/12] Add key-out color on the ground --- thrimbletrimmer/driveclock/drive.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/thrimbletrimmer/driveclock/drive.js b/thrimbletrimmer/driveclock/drive.js index c5f6bcc..cb0425e 100644 --- a/thrimbletrimmer/driveclock/drive.js +++ b/thrimbletrimmer/driveclock/drive.js @@ -21,6 +21,8 @@ const COLORS = { }, }; +const KEY_OUT_COLOR = "#2b6ec6"; + // The width from the left side of the bus image to the front of the bus const BUS_FRONT_OFFSET = 73; @@ -76,8 +78,10 @@ function drawBackground(context, timeOfDay, leftX, width) { const groundColor = COLORS[timeOfDay].ground; const surfaceColor = COLORS[timeOfDay].surface; + context.fillStyle = KEY_OUT_COLOR; + context.fillRect(leftX, 80, width, 20); context.fillStyle = COLORS[timeOfDay].sky; - context.fillRect(leftX, 0, width, 100); + context.fillRect(leftX, 0, width, 80); context.fillStyle = COLORS[timeOfDay].surface; context.fillRect(leftX, 80, width, 1); context.fillStyle = COLORS[timeOfDay].ground; From 95c0a86da0262e80b4584c2e3db6577c5acbb704 Mon Sep 17 00:00:00 2001 From: ElementalAlchemist Date: Wed, 13 Nov 2024 01:23:06 -0600 Subject: [PATCH 04/12] Constantize the canvas width in case we want it widened in the future --- thrimbletrimmer/driveclock/drive.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/thrimbletrimmer/driveclock/drive.js b/thrimbletrimmer/driveclock/drive.js index cb0425e..40b1db9 100644 --- a/thrimbletrimmer/driveclock/drive.js +++ b/thrimbletrimmer/driveclock/drive.js @@ -48,6 +48,9 @@ BUS_STOP_IMAGE.src = "db_stop.png"; const POINT_IMAGE = new Image(); POINT_IMAGE.src = "point.png"; +// This should match the HTML canvas width +const CANVAS_PIXEL_WIDTH = 1920; + function nextPhase(timeOfDay) { switch (timeOfDay) { case "day": @@ -105,7 +108,7 @@ async function drawRoad() { const context = canvas.getContext("2d"); // Clear the previous canvas before starting - context.clearRect(0, 0, 1920, 100); + context.clearRect(0, 0, CANVAS_PIXEL_WIDTH, 100); const currentTime = busData.clock_minutes; const distance = busData.odometer; @@ -113,7 +116,7 @@ async function drawRoad() { drawBackground(context, timeOfDay, 0, BUS_FRONT_OFFSET); - const maxWidth = 1920 - BUS_FRONT_OFFSET; + const maxWidth = CANVAS_PIXEL_WIDTH - BUS_FRONT_OFFSET; // The default scaling factor (1) is 20 seconds per pixel at max speed. // This gives us // - 3px per minute @@ -171,7 +174,7 @@ async function drawRoad() { distanceTracked += 720; } x = 0; - while (x < 1920) { + while (x < CANVAS_PIXEL_WIDTH) { const distanceTrackedOnRoute = distanceTracked % 360; let nextBusStopPosition = null; for (const busStopPosition of BUS_STOP_POSITIONS) { From f678556ff5ef9728eba92d1ca63633cc1b0671f7 Mon Sep 17 00:00:00 2001 From: ElementalAlchemist Date: Wed, 13 Nov 2024 02:13:09 -0600 Subject: [PATCH 05/12] Show icons for points we've driven past --- thrimbletrimmer/driveclock/drive.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/thrimbletrimmer/driveclock/drive.js b/thrimbletrimmer/driveclock/drive.js index 40b1db9..b51cee9 100644 --- a/thrimbletrimmer/driveclock/drive.js +++ b/thrimbletrimmer/driveclock/drive.js @@ -152,7 +152,7 @@ async function drawRoad() { x += pixelWidth; } - x = BUS_FRONT_OFFSET; + x = 0; const currentPointProgress = distance % 360; let distanceToNextPoint; if (currentPointProgress <= 109.3) { @@ -161,9 +161,14 @@ async function drawRoad() { distanceToNextPoint = 469.3 - currentPointProgress; } + distanceToNextPoint += BUS_FRONT_OFFSET / (4 * scaleFactor); + if (distanceToNextPoint >= 360) { + distanceToNextPoint -= 360; + } + x += distanceToNextPoint * 4 * scaleFactor; context.drawImage(POINT_IMAGE, x - POINT_OFFSET, 0); - while (x < maxWidth) { + while (x < CANVAS_PIXEL_WIDTH) { x += 360 * 4 * scaleFactor; context.drawImage(POINT_IMAGE, x - POINT_OFFSET, 0); } From 58f3b867f75b6b2ba86f3631464ab32469cb3ac6 Mon Sep 17 00:00:00 2001 From: ElementalAlchemist Date: Wed, 13 Nov 2024 03:38:03 -0600 Subject: [PATCH 06/12] Better align bus stop signs with the front of the bus --- thrimbletrimmer/driveclock/drive.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/thrimbletrimmer/driveclock/drive.js b/thrimbletrimmer/driveclock/drive.js index b51cee9..3d818cd 100644 --- a/thrimbletrimmer/driveclock/drive.js +++ b/thrimbletrimmer/driveclock/drive.js @@ -32,7 +32,7 @@ const DUSK_START_MINUTES = 1140; const NIGHT_START_MINUTES = 1200; const DAWN_START_MINUTES = 400; -const BUS_STOP_OFFSET = 6; +const BUS_STOP_OFFSET = 8; const POINT_OFFSET = 17; // Bus stop positions are recorded in miles with the 0 position From ee5a36f4257249b73bf0c264eb119e3e44f5acee Mon Sep 17 00:00:00 2001 From: ElementalAlchemist Date: Wed, 13 Nov 2024 16:27:00 -0600 Subject: [PATCH 07/12] Default to filling the canvas with the key-out color --- thrimbletrimmer/driveclock/drive.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/thrimbletrimmer/driveclock/drive.js b/thrimbletrimmer/driveclock/drive.js index 3d818cd..66a22cb 100644 --- a/thrimbletrimmer/driveclock/drive.js +++ b/thrimbletrimmer/driveclock/drive.js @@ -109,6 +109,10 @@ async function drawRoad() { // Clear the previous canvas before starting context.clearRect(0, 0, CANVAS_PIXEL_WIDTH, 100); + // Background the whole thing as the key-out color in case we need to bail + // out before drawing (e.g. we're in a non-DB game menu) + context.fillStyle = KEY_OUT_COLOR; + context.fillRect(0, 0, CANVAS_PIXEL_WIDTH, 100); const currentTime = busData.clock_minutes; const distance = busData.odometer; From 8b7b87d45021f9194d6458fff1985b700e3c3796 Mon Sep 17 00:00:00 2001 From: ElementalAlchemist Date: Thu, 14 Nov 2024 22:36:33 -0600 Subject: [PATCH 08/12] Fix bus stops disappearing early The existing code tried to avoid infinite loops due to floating point math. It turns out the +1 was overzealous and caused bus stop signs to disappear before expected at high scale numbers. --- thrimbletrimmer/driveclock/drive.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/thrimbletrimmer/driveclock/drive.js b/thrimbletrimmer/driveclock/drive.js index 66a22cb..a53a7c5 100644 --- a/thrimbletrimmer/driveclock/drive.js +++ b/thrimbletrimmer/driveclock/drive.js @@ -187,7 +187,7 @@ async function drawRoad() { const distanceTrackedOnRoute = distanceTracked % 360; let nextBusStopPosition = null; for (const busStopPosition of BUS_STOP_POSITIONS) { - if (busStopPosition >= distanceTrackedOnRoute + 1) { + if (busStopPosition >= distanceTrackedOnRoute + 0.05) { nextBusStopPosition = busStopPosition; break; } From 5410b5c1fe609db42f6eca1efd6c8db0ce6ff7c0 Mon Sep 17 00:00:00 2001 From: ElementalAlchemist Date: Thu, 14 Nov 2024 23:52:16 -0600 Subject: [PATCH 09/12] Shorten refresh interval to 2.5 seconds --- thrimbletrimmer/driveclock/drive.html | 2 +- thrimbletrimmer/driveclock/drive.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/thrimbletrimmer/driveclock/drive.html b/thrimbletrimmer/driveclock/drive.html index a373636..d73e794 100644 --- a/thrimbletrimmer/driveclock/drive.html +++ b/thrimbletrimmer/driveclock/drive.html @@ -13,7 +13,7 @@

- Scale modifications will be applied on the next update, which occur every 10 seconds. + Scale modifications will be applied on the next update, which occur every 2.5 seconds.

diff --git a/thrimbletrimmer/driveclock/drive.js b/thrimbletrimmer/driveclock/drive.js index a53a7c5..58c029d 100644 --- a/thrimbletrimmer/driveclock/drive.js +++ b/thrimbletrimmer/driveclock/drive.js @@ -210,5 +210,5 @@ async function drawRoad() { window.addEventListener("DOMContentLoaded", (event) => { drawRoad(); - setInterval(drawRoad, 10000); + setInterval(drawRoad, 2500); }); From 96a71c351969f653320e9963ea2af78827d8d19c Mon Sep 17 00:00:00 2001 From: ElementalAlchemist Date: Fri, 15 Nov 2024 03:32:44 -0600 Subject: [PATCH 10/12] Add point progress mode Adds a new mode that makes the full width be exactly one point, and the bus drives across the canvas. This also resizes the canvas to the requested size (1580x62). --- thrimbletrimmer/driveclock/bus_day.png | Bin 439 -> 4492 bytes thrimbletrimmer/driveclock/bus_night.png | Bin 520 -> 4545 bytes thrimbletrimmer/driveclock/db_stop.png | Bin 173 -> 4306 bytes thrimbletrimmer/driveclock/drive.css | 2 +- thrimbletrimmer/driveclock/drive.html | 11 ++- thrimbletrimmer/driveclock/drive.js | 88 ++++++++++++++++------- thrimbletrimmer/driveclock/point.png | Bin 397 -> 4455 bytes 7 files changed, 73 insertions(+), 28 deletions(-) diff --git a/thrimbletrimmer/driveclock/bus_day.png b/thrimbletrimmer/driveclock/bus_day.png index c85bccd3bbb8ca3508cb50451d0325fe9eb18f6d..02398106bfd39172af4bd731f83e7e037aff58dc 100644 GIT binary patch literal 4492 zcmeHLe{dAl9p6htXn>FiZBr23*%(l9Z*TX0?sA9uaS1t-i)V6@ATu?4d+)-+-R*h1 zm%BuMglKHgKr2Ekibg;z6^v5AR3eBeF+(lIG96o*vC!0Uu*2|U2En$H^t*ePOEPuF z$xQt(clY*v-+SNh=Y8Mjec$))-6l`fvYAZoO|Zi-fSfIKfHZv*+Bluic7 zv@Qdi3H44W49Ih!eglg5ljK~-K~`VWw6``hv_|S>F1H6~^t3^5w1K^WF>y4_(PrFW z;7k_IY=ilAua0`8BE7EQ_2A!AVo8a|RZ@aSqEbK%A`DCHT=$%#{I`X7_C2=p$nNdg zjXAleJTG6`O>8-mRk|-{b=&g!mHJz)3un5Xd3Mi*MZzZdelzQX+Uxlp3p$8}bWP{% zzB%^(KRed0J@)Li`>)6ww&)5Z=ES0${vF}Elk-j*U;6nUnmTR04{j}9TV>}zp0eq# zclAAAfB5djw>ulkIx3rAu@&DxXGz`V`sIvwP#oO1d-b*}XO13-{B7{S^3?}o4-NHt ze!OzU=iKSsyAQS(wCCV^o@VZQJ7>=$`F+k8Z_HjW#P?HMD;C!NsN>G*`-`|5+? z3-Rs%zxu2@tDxob4+nqt;AdX6;D8fy8liomk5IJUK!rloSPNMxZ3K{%L*Hm2dz&JNsz$ODiwdZ; z5rvifu@HVCZ6Aq6gW7WZ0)>Jo1gbL3N{^dTJ#lFZm4XH_6w$1J>~Wfk7#JmMoNwxg zww$q!fO#5soc74tHD%CpyE&&M#8mfOP6wgR&-o=m^mCdhSZQ;BVa=oXoFRgGguH^spO_ zlb=_3C$As?P3K|nNFF8)7S51#Uxjl^elgJWZQAPL!R??ATV5{8Fn^QQl{`^3=<#GL z*$j%>QNnTUP;k7ET!PFuBERM*U?qoyReZPsLH0<6>qx)&4WYnVO|*#?EF`iBYA7%? z$(mUkX%>*hELbh9(U#o5G`cJWlsF$nMGe3sa0TJ1eM8~*Xd89!cwgeHkh%*%7)jG4 zZ5|VG(~YYJnM4A>?6AbCM9q|(VS#ToefUA|I#@m)@V|3+8V=yeK(;a5-`E=v#V4SjIYs`XTQOSLN( z*4o^6Lcb0DwQq5J#L&9COH(meoQp?`cD>f(U6{2jvQgQXk zu7!!Cxou}XpFVP~uz2&v-uBuxo3DSh!TiEYxuNdN$Np5mpyMFBLpBa!v95w6nU4?s Q16G2$N~@e*#r3WK1;ol7MF0Q* delta 393 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 N?0H4D-{fp;{125?FQxzh delta 475 zcmX@8+`*#R8Q|y6%O%Cdz`(%k>ERLtq*FkcgBeJ=+oYxeDZc=p5ZB2v0xE1xO-&sO zS5Ed3kgl(-tpzd{E?gKTdcy&zNTejlFBnKOFlZUtJ2*HbHgwNjv2yF){a#jobr~2K zLp@y_Ln`LHz4p4OSwVm`AzOWAu35Rk+28v!cIs*vI(0=YFaFlfJlWT-uTgThMI@Gt!S#;#BS=SwPO~XE)NiM083MXr2F0Ou1RV(t$^U3LH z6}2iA^PISNc4{p-5+NFtlA^r-{{Fh}>3=@Dvfs4}dcNetg3rl~DnWI^6MvuGzQUrj z>$JD<&YM2V{TB&7=TS)0%)NcfBXP=^1S`wVs^}-LJcaJA-*-`3wfKgg-=hDM>f0ox zJQn?48arM7>5~t0)AV19yP78_FngHs$rR)^nZWgOTj&KVb4K4n$A-}bE? zax&$rM(@p~e)`6RevApf^K!OSm+U{CFUOAstGs$+5*Sk)^Um*j-P~smc6zptKUuN| z<@3uv-mo^^@m1C4+w$kHe=gjAd&SNAU00c9L7_5XzjGY}=eMW&I-9J$Ktb>6>gTe~ HDWM4fC&0s? diff --git a/thrimbletrimmer/driveclock/db_stop.png b/thrimbletrimmer/driveclock/db_stop.png index 6bc52e82e3230b02181c938f0a23650c56d2436b..a3f3fe384c1abf23c5ca3326e4d9e458b3bc82cd 100644 GIT binary patch literal 4306 zcmeHKdvFs)7{3%M5DFFnMFBYviqPS5k6a!-6NEM`4K$_F07Yhm&D~x(NOCc`HYtLZ zDhk@c7wCv64Ez%j6dzcnC~rHAB96?U4k`@eFzE1d#@G0ues@XJc67$+jQ-bjHv8@F z@B8iUZ@>N0U0+i@Ynb&mD?t#${FT0WP~Qk;%D@5eed?a~e}U>#T<2H({$gS%L^jMiJXIES<&tk1t*{_+qzd| zF#WV)wg*@qQOn9|{AFciEUqbP7!yQl%j&1Sl}BcbJGNx;-tC))Hx`aKUh~qW?bL?7 z*79`W^0f=bSK0n-nLNz@_!B$M-5a|mz486I^SfSs_*HW7gU#-{+*9{euJ4>3s!XGEL-rh=0=4!W&JT}VH@ws=^s`sDx zVcONiLmMnb8h`N4!j2cBbw|e@wZHh>Crw-3hi5-ix~kfPKFwc0;XPaXA0NJVanIKJ ziq@)?FS|>pjk>Gu%F+dV@Vt6Hy?yz{t0(vEihXl_*MjA{8s=UqBPPAE zu4r8$x#Lm3cu(Pu`4f-%Hvc^G_UovFd1lV!g}1hj8=5X%`|YoXI?pBB6tw)afVHUk z$}MNEj~UR?KI5(EnX?`5eC@I1ojkbd^v?^&9=vqtcR#&zvp4VXnS;j~7C-w-^GD;C zUi3d-aQx{!;m@^s*B-1myK%q`+L{j>ZEdHgS5IgzXxOyRGJN=&A1M6#1N+jG7xq(y8<)!wD&_#&lJSk_IOVX$`uUqF|itT3;j< z2;}IaiL45s2a`fEhO@CuB*OIcNa*E_K$2b1KYApBO)<>O!wIb+F5&V<9MvcGq>!bY zf2<)MHn$^73=ZQ6Koc-4*K11S#^pQ=3F_5I%=7}Wdm(jI=@zTk+Kdr%J3SWy?m6CG z=&rq+U_b=|qEC|=jOF=#UdotXlr>3}MblL5C2k3YcpAw_q6Or1(IHN8(%6CQg3GBW zg5BMN${$VWC@Nut3dn6L@NlfeW8_k31=}$#@NR`J5m-Ad$dbgeEbovwdk>1aaTTHx zg?p~bppt<~0lY(UOSH?b2(%zcJRQOuOLKM(g;0s)atN|XB}-z37LOn}PBnt+F%yf{ zn*)a6;>;Sqm*Q<~cTY_i=?XY_sVX(vkm?=?su5hPBSTHjW#^m@-pRUP8kdXj1}(tx z1Vp02Y35FI#5fiaHUq*UBTj+9?1#OGWpRvjEgsagu$RhZ-P~{hu9J*(( zY$1UfaYkX_$_z=%P_!OH_Q-~7SHJoXp&+<9UXVFEZI=ZO0>CNJA;pPk#o=&rPFZo5 zxLl?RIrfC6=t&gEGwVS|&aw*(Huu}jxNDmS;3g>h8ZJg zbnV!~{7n;&3Fwhzz%SDW$qRBJlS_tK&0xL%=O=p>|K}7W**{62^zE0cU#>nW&?j(z zboI;CCk6Th?vJkjOfGBpa|%b{zn~;MmOgGwe+3V%(KS`Ua;Q5yJK_8CxJj#5uLgVm z{P{~x?loTS7JXiIEivQ57mCpc&6(vR*5!{kUgJYzm5UMtF?6I+EX3w5W1w@8?hlj? zx;XU48zwQcj=pJ7SNMH1gJVufOJ@+1i;4#C4u=aAiz-R^+lX~eXK5&1ymsf#;#Z2< zbj!?i-sZ}5ah-3-w3b)KNBP6HcP|$dOdWFMNegjw!yWCveJ;9fRs>xPl)UP|Nnu^w=ycVwY5NYXlUr#r)wqwg&0eM z{DK)Ap4~_Tay&g<978JNk|icK1O^B=acgQeMml*pC@_cwFfqESv~?`#?&w(1!O+Yh zv4dkl=Z=mBSC)fHFK#_lJN&lkHXqwT#w#g3Ms4D13=Co?6t_%FY>)+-%HZkh=d#Wz Gp$P!9o;bAt diff --git a/thrimbletrimmer/driveclock/drive.css b/thrimbletrimmer/driveclock/drive.css index 368695c..fb14518 100644 --- a/thrimbletrimmer/driveclock/drive.css +++ b/thrimbletrimmer/driveclock/drive.css @@ -3,7 +3,7 @@ margin-top: 20px; } -#scale-disclaimer { +.settings-disclaimer { font-size: 75%; font-style: italic; } diff --git a/thrimbletrimmer/driveclock/drive.html b/thrimbletrimmer/driveclock/drive.html index d73e794..49982c5 100644 --- a/thrimbletrimmer/driveclock/drive.html +++ b/thrimbletrimmer/driveclock/drive.html @@ -7,13 +7,18 @@ - + -

- Scale modifications will be applied on the next update, which occur every 2.5 seconds. + +

+ Settings modifications will be applied on the next update, which occur every 2.5 seconds.

+

The scale setting is ignored in Point Progress Mode.

diff --git a/thrimbletrimmer/driveclock/drive.js b/thrimbletrimmer/driveclock/drive.js index 58c029d..114db63 100644 --- a/thrimbletrimmer/driveclock/drive.js +++ b/thrimbletrimmer/driveclock/drive.js @@ -21,10 +21,8 @@ const COLORS = { }, }; -const KEY_OUT_COLOR = "#2b6ec6"; - // The width from the left side of the bus image to the front of the bus -const BUS_FRONT_OFFSET = 73; +const BUS_FRONT_OFFSET = 72; // Start time of each day phase const DAY_START_MINUTES = 450; @@ -49,7 +47,12 @@ const POINT_IMAGE = new Image(); POINT_IMAGE.src = "point.png"; // This should match the HTML canvas width -const CANVAS_PIXEL_WIDTH = 1920; +const CANVAS_PIXEL_WIDTH = 1580; + +const BUS_TRAVEL_WIDTH = CANVAS_PIXEL_WIDTH - BUS_FRONT_OFFSET; +const PIXELS_PER_MILE = BUS_TRAVEL_WIDTH / 360; +const PIXELS_PER_MINUTE = BUS_TRAVEL_WIDTH / 480; +const FULL_SPEED_MILES_PER_MINUTE = 0.75; function nextPhase(timeOfDay) { switch (timeOfDay) { @@ -81,17 +84,12 @@ function drawBackground(context, timeOfDay, leftX, width) { const groundColor = COLORS[timeOfDay].ground; const surfaceColor = COLORS[timeOfDay].surface; - context.fillStyle = KEY_OUT_COLOR; - context.fillRect(leftX, 80, width, 20); context.fillStyle = COLORS[timeOfDay].sky; - context.fillRect(leftX, 0, width, 80); + context.fillRect(leftX, 0, width, 56); context.fillStyle = COLORS[timeOfDay].surface; - context.fillRect(leftX, 80, width, 1); + context.fillRect(leftX, 56, width, 1); context.fillStyle = COLORS[timeOfDay].ground; - context.fillRect(leftX, 81, width, 7); - context.fillRect(leftX, 89, width, 3); - context.fillRect(leftX, 94, width, 2); - context.fillRect(leftX, 99, width, 1); + context.fillRect(leftX, 57, width, 5); } async function drawRoad() { @@ -108,19 +106,62 @@ async function drawRoad() { const context = canvas.getContext("2d"); // Clear the previous canvas before starting - context.clearRect(0, 0, CANVAS_PIXEL_WIDTH, 100); - // Background the whole thing as the key-out color in case we need to bail - // out before drawing (e.g. we're in a non-DB game menu) - context.fillStyle = KEY_OUT_COLOR; - context.fillRect(0, 0, CANVAS_PIXEL_WIDTH, 100); + context.clearRect(0, 0, CANVAS_PIXEL_WIDTH, 62); + + const pointModeCheckbox = document.getElementById("point-progress-checkbox"); + if (pointModeCheckbox.checked) { + drawRoadPoint(context, busData); + } else { + drawRoadDynamic(context, busData); + } +} + +function drawRoadPoint(context, busData) { + const busDistance = (busData.odometer + 250.7) % 360; + const busRemainingDistance = 360 - busDistance; + const busRemainingDistancePixels = busRemainingDistance * PIXELS_PER_MILE; + + const busDistancePixels = busDistance * PIXELS_PER_MILE; + let x = busDistancePixels + BUS_FRONT_OFFSET; + drawBackground(context, busData.timeofday, 0, x); + let currentTimeOfDay = busData.timeofday; + let currentTime = busData.clock_minutes; + while (x < CANVAS_PIXEL_WIDTH) { + const nextTimeOfDay = nextPhase(currentTimeOfDay); + const nextStartTime = phaseStartTime(nextTimeOfDay); + + let thisDuration = nextStartTime - currentTime; + if (thisDuration < 0) { + thisDuration += 1440; + } + const pixelWidth = thisDuration * PIXELS_PER_MINUTE; + drawBackground(context, currentTimeOfDay, x, pixelWidth); + x += pixelWidth; + currentTimeOfDay = nextTimeOfDay; + currentTime += thisDuration; + } + + context.drawImage(POINT_IMAGE, CANVAS_PIXEL_WIDTH - POINT_OFFSET, 0); + + for (const busStopDistance of BUS_STOP_POSITIONS) { + const busStopPixelPosition = + BUS_FRONT_OFFSET + PIXELS_PER_MILE * busStopDistance - BUS_STOP_OFFSET; + context.drawImage(BUS_STOP_IMAGE, busStopPixelPosition, 16); + } + + if (busData.timeofday === "night") { + context.drawImage(BUS_NIGHT_IMAGE, busDistancePixels, 32); + } else { + context.drawImage(BUS_DAY_IMAGE, busDistancePixels, 32); + } +} - const currentTime = busData.clock_minutes; +function drawRoadDynamic(context, busData) { const distance = busData.odometer; const timeOfDay = busData.timeofday; drawBackground(context, timeOfDay, 0, BUS_FRONT_OFFSET); - const maxWidth = CANVAS_PIXEL_WIDTH - BUS_FRONT_OFFSET; // The default scaling factor (1) is 20 seconds per pixel at max speed. // This gives us // - 3px per minute @@ -131,7 +172,7 @@ async function drawRoad() { } const startMinute = busData.clock_minutes; - const timeDuration = maxWidth / (3 * scaleFactor); + const timeDuration = BUS_TRAVEL_WIDTH / (3 * scaleFactor); let previousTime = startMinute; let previousTimeOfDay = timeOfDay; @@ -146,7 +187,6 @@ async function drawRoad() { thisDuration += 1440; } - // TODO Figure out scaling factor const pixelWidth = thisDuration * 3 * scaleFactor; drawBackground(context, previousTimeOfDay, x, pixelWidth); @@ -198,13 +238,13 @@ async function drawRoad() { const nextBusStopDistance = nextBusStopPosition - distanceTrackedOnRoute; distanceTracked += nextBusStopDistance; x += nextBusStopDistance * 4 * scaleFactor; - context.drawImage(BUS_STOP_IMAGE, x - BUS_STOP_OFFSET, 0); + context.drawImage(BUS_STOP_IMAGE, x - BUS_STOP_OFFSET, 16); } if (timeOfDay === "night") { - context.drawImage(BUS_NIGHT_IMAGE, 0, 0); + context.drawImage(BUS_NIGHT_IMAGE, 0, 32); } else { - context.drawImage(BUS_DAY_IMAGE, 0, 0); + context.drawImage(BUS_DAY_IMAGE, 0, 32); } } diff --git a/thrimbletrimmer/driveclock/point.png b/thrimbletrimmer/driveclock/point.png index 601c57ab0bdcb430f12d4cf6358c7fcf7e57c927..a1516883022ed0156945cf4382904f6f246afef4 100644 GIT binary patch literal 4455 zcmeHKeNYtV8NWLa@%Si4ZB_i(EWxPF-tOMr-pA(7ARNjmM^G+!N~U#h_dQtPZZEeB zcc3*u!J?u0Fq#ibDkC$kqa@Lq$S7(MgN&1*+We7^GDb@sgB>d}wXv-R`|jR>FwIPd zndCq2=DqjX_x(M;_j!Ns^FDLCtFT~giguw^rBbCh^BfytkH{@C0se}$wRd568F-vN zr*ow$5k5eqI$ougF|es8*cx@5Dh`hAgzYhC%NV&WRL@ZQ7HFr(?7h%l9>Zv$O{z7j z6xg@Irib=i*dK1YypU@j=lB7urAqd)N zrb!b-tJ@w$Bs1MVHsd5@Y2b?Uk-@-A5ak!(Yyu>F(Emda?1<{&%(KHr>r=S<-?oBa05q>78rq z``o^L`w7!k`8fS!!*AaBW6c3e&$>O?+Y4;WY>JK9tDvy#m> z=>FMH9Ba;OPD9#v>Q^33Ykw}I*RlVbIZH;FK77ymWt$eXEJ`|*-E{q5=N?`Qclwy} zPhHx~y4yb)7@Z&2(3N#OFmSc+#1)%bbE$jZXWwi}@4ofq&3~Twv0c+M(A`^A^7_5H zPZn<-a=w{-@imR{+a}GO9c!-cjY|;f&r+S8pRFobT9;h4@02<A<2P3@OBY9l;G%NUZ3@3PjF5(Z$2d1(;6A3bG1&|OgDCGlo zbhzsy6yZ5Lx`}cTu3!!*#?WAV!^0M_{=TzFLp2(jvAH=c6||*2E~_lN~LSq#%vs;c!?N zHt2*3P zv)+JNO+E_-D3&o=eI_Hv7$&oef=>!FA+Wj>>Ik)h>8WUiELRR{#bk8249KSd1;a=Z zBdIaLV6rKK;js7EaLXm-f(J@%OU@gSQi(yPNx$OLapV(RafGa@i z=|!fb@3dUga!pBrDS@Z6Yg(=;DKI7Qbas7ja%msEPJsaY7ZipsOYIA~FTfYA1n>H_ z4%I{X+ji#nufvg9!Mx3)N|iK6Zfe#3*7}g_Q+V;hb zhAb@MdC{LgQkeC`=R3c;@qA+Mum4?SITC+&Ih9;`p|!xe?%H3H>lPjR)0Q&Uk@cV_ z!BPMC(Y}lldR>1-W`)l4XXk#+oCnQh@;lpZu2PMh*XLi>H>A$0J6uz9Z*=(h#Vy6y zbL?sV`-2NsHV?k_bxZr;+gG+d)_98_P1=(_Z$TS(_PsCSAMfqmbEf_4-vx)`m#B8% z_|ld!e!OXlUqyvaNzp=SK2b0Hr~4)Uydxl(Q$Qz!}z7mZCCEqe=r2nH@rKG fUvZ{<#g4eUZMD?E&CxfZL@H-)f#bdGtquPLLP`lo literal 397 zcmeAS@N?(olHy`uVBq!ia0vp^N1P;jIbW(++GCit4OaVeV;+Jy2Y-@A-4dhqi|l zTqanV*5ymvmU*8rlMJpsVZ8d-^|lVBsH8gY$zf_5T}_`0=e61XemsrWB;<>~O7U~W zM@!8wyG%BGGV^=Jd7n3X)iUP)cmJ}s$zqYa%%WQzjp=@2uCG4+*;jw)(w>rdna=Ot zLZ950S8O)hJ4^0ko9To3$!(f8LsJnRn7_(&^6^ iCf=3;1|ceF=;(O&`&pHFaLbyNAQ?|rKbLh*2~7ZGXR9~> From f6f42fbb458ac6ee05023920bc086489796b26f4 Mon Sep 17 00:00:00 2001 From: ElementalAlchemist Date: Fri, 15 Nov 2024 03:37:05 -0600 Subject: [PATCH 11/12] Minor cleanup of the standard mode --- thrimbletrimmer/driveclock/drive.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/thrimbletrimmer/driveclock/drive.js b/thrimbletrimmer/driveclock/drive.js index 114db63..ff31267 100644 --- a/thrimbletrimmer/driveclock/drive.js +++ b/thrimbletrimmer/driveclock/drive.js @@ -172,13 +172,11 @@ function drawRoadDynamic(context, busData) { } const startMinute = busData.clock_minutes; - const timeDuration = BUS_TRAVEL_WIDTH / (3 * scaleFactor); let previousTime = startMinute; let previousTimeOfDay = timeOfDay; - let remainingDuration = timeDuration; let x = BUS_FRONT_OFFSET; - while (remainingDuration > 0) { + while (x < CANVAS_PIXEL_WIDTH) { const nextTimeOfDay = nextPhase(previousTimeOfDay); const nextStartTime = phaseStartTime(nextTimeOfDay); @@ -190,7 +188,6 @@ function drawRoadDynamic(context, busData) { const pixelWidth = thisDuration * 3 * scaleFactor; drawBackground(context, previousTimeOfDay, x, pixelWidth); - remainingDuration -= thisDuration; previousTime = nextStartTime; previousTimeOfDay = nextTimeOfDay; x += pixelWidth; From aedd51e5f8f0533928ef3379f353535d6491e68d Mon Sep 17 00:00:00 2001 From: ElementalAlchemist Date: Fri, 15 Nov 2024 12:25:16 -0600 Subject: [PATCH 12/12] Fix small gaps in the background --- thrimbletrimmer/driveclock/drive.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/thrimbletrimmer/driveclock/drive.js b/thrimbletrimmer/driveclock/drive.js index ff31267..266bb89 100644 --- a/thrimbletrimmer/driveclock/drive.js +++ b/thrimbletrimmer/driveclock/drive.js @@ -84,6 +84,9 @@ function drawBackground(context, timeOfDay, leftX, width) { const groundColor = COLORS[timeOfDay].ground; const surfaceColor = COLORS[timeOfDay].surface; + width = Math.ceil(width); + leftX = Math.floor(leftX); + context.fillStyle = COLORS[timeOfDay].sky; context.fillRect(leftX, 0, width, 56); context.fillStyle = COLORS[timeOfDay].surface;