From ec241dcd38005a3f40deebf2086cff2c6ae1b1ae Mon Sep 17 00:00:00 2001 From: Alex Bates Date: Thu, 7 Jan 2021 12:53:47 +0000 Subject: [PATCH] cool graph --- .postcssrc.js | 5 + package.json | 12 ++- src/ProgressPane.jsx | 162 ++++++++++++++++++++++++++++ src/bg/caption-checker.png | Bin 0 -> 97 bytes src/bg/red-checker.png | Bin 0 -> 97 bytes src/bg/yellow-checker.png | Bin 0 -> 97 bytes src/index.css | 214 +++++++++++++++++++++++++++++++++++++ src/index.html | 41 ++++++- src/index.js | 14 +++ src/main.jsx | 94 ++++++++++++++++ src/pmdialog2.woff2 | Bin 0 -> 17372 bytes src/progress.js | 121 +++++++++++++++++++++ 12 files changed, 659 insertions(+), 4 deletions(-) create mode 100644 .postcssrc.js create mode 100644 src/ProgressPane.jsx create mode 100644 src/bg/caption-checker.png create mode 100644 src/bg/red-checker.png create mode 100644 src/bg/yellow-checker.png create mode 100644 src/index.css create mode 100644 src/index.js create mode 100644 src/main.jsx create mode 100644 src/pmdialog2.woff2 create mode 100644 src/progress.js diff --git a/.postcssrc.js b/.postcssrc.js new file mode 100644 index 0000000..e68b81d --- /dev/null +++ b/.postcssrc.js @@ -0,0 +1,5 @@ +module.exports = { + plugins: { + "postcss-preset-env": true, + }, +} diff --git a/package.json b/package.json index 8f016ff..e7e2da0 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,16 @@ "start": "parcel src/index.html", "build": "parcel build src/index.html" }, - "dependencies": {}, + "browserslist": "> 1% and last 2 years", + "dependencies": { + "clsx": "^1.1.1", + "react": "^17.0.1", + "react-dom": "^17.0.1", + "recharts": "^2.0.0", + "use-element-dimensions": "^2.1.3" + }, "devDependencies": { - "parcel-bundler": "^1.12.4" + "parcel-bundler": "^1.12.4", + "postcss-preset-env": "^6.7.0" } } diff --git a/src/ProgressPane.jsx b/src/ProgressPane.jsx new file mode 100644 index 0000000..42ad500 --- /dev/null +++ b/src/ProgressPane.jsx @@ -0,0 +1,162 @@ +import React, { useState, useEffect } from "react" +import { createPortal } from "react-dom" +import { Area, XAxis, YAxis, AreaChart, CartesianGrid, Tooltip } from "recharts" +import useDimensions from "use-element-dimensions" + +const csvVersions = { + "1": { + timestamp: parseInt, + commit: s => s, + totalFuncs: parseInt, + nonMatchingFuncs: parseInt, + matchingFuncs: parseInt, + totalBytes: b => parseInt(b), + nonMatchingBytes: b => parseInt(b), + matchingBytes: b => parseInt(b), + }, +} + +async function fetchData() { + const csv = await fetch("https://papermar.io/reports/progress.csv") + .then(response => response.text()) + + return csv + .split("\n") + .filter(row => row.length) + .map(row => { + const [version, ...data] = row.split(",") + const structure = csvVersions[version] + const obj = {} + + for (const [key, transform] of Object.entries(structure)) { + obj[key] = transform(data.shift()) + } + + obj.percentBytes = Math.round((obj.matchingBytes / obj.totalBytes) * 100) + + return obj + }) +} + +export default function ProgressPane({ captionPortal }) { + const [data, setData] = useState(null) + + useEffect(() => { + fetchData() + .then(data => setData(data)) + }, []) + + // TODO: cute spin animation when data loads + + return
+ {data && } + {!data && "Loading..."} +
+} + +const MONTH = 2678400 + +function DataView({ data, captionPortal }) { + const [chartDimensions, chartRef] = useDimensions() + const latest = data[data.length - 1] + const oldest = data[0] + + let monthDates = [] + for (let i = new Date(1583020800000); i < new Date(latest.timestamp); i.setMonth(i.getMonth() + 1)) { + if (i > new Date(oldest.timestamp)) { + monthDates.push(i) + } + } + + console.log(monthDates) + + const [selectedEntry, setSelectedEntry] = useState(null) + + function renderTooltip(tip) { + const entry = data.find(row => row.timestamp === tip.label) + + setSelectedEntry(entry) + + return + } + + return <> + + + + + + + +
Matched{Math.round((latest.matchingBytes / latest.totalBytes) * 10000) / 100}%
+ +
+
+
+ {chartDimensions.width > 0 && + + + + + + + + + } +
+
+ + +
+ + {selectedEntry && captionPortal.current && createPortal(, captionPortal.current)} + +} + +function formatDate(timestamp, options={}) { + const date = new Date(timestamp * 1000) + + return new Intl.DateTimeFormat([], options).format(date) +} + +function EntryInfo({ entry }) { + /*const [commitMessage, setCommitMessage] = useState(null) + + useEffect(async () => { + fetch(`https://api.github.com/repos/ethteck/papermario/commits/${entry.commit}`) + .then(resp => resp.json()) + .then(resp => { + setCommitMessage(resp.commit.message.split("\n")[0]) + }) + }, [entry.commit])*/ + + return
+ + {entry.commit.substr(0, 8)} + + + + + + + + +
Matched + {Math.round((entry.matchingBytes / entry.totalBytes) * 10000) / 100}% + ({entry.matchingFuncs}/{entry.totalFuncs} functions) +
+
+} diff --git a/src/bg/caption-checker.png b/src/bg/caption-checker.png new file mode 100644 index 0000000000000000000000000000000000000000..d221284e0706882365451f44254ca365a166d57d GIT binary patch literal 97 zcmeAS@N?(olHy`uVBq!ia0vp^Od!m`1|*BN@u~nR#^NA%Cx&(BWL^R}(w;7kAsjQ4 tYrcFq_W%8Rdn1F|`u`IfgD1>qWXOx)EY-}jhz2TS@O1TaS?83{1OPWZ8two9 literal 0 HcmV?d00001 diff --git a/src/bg/red-checker.png b/src/bg/red-checker.png new file mode 100644 index 0000000000000000000000000000000000000000..322862577b9f1c2fe07d2aa889dc6385901b0905 GIT binary patch literal 97 zcmeAS@N?(olHy`uVBq!ia0vp^Od!m`1|*BN@u~nR#^NA%Cx&(BWL^R}(w;7kAsjQ4 tYqsQm{?EVtosmK9j?(Fl4!$!P8Pd8qz8(s_Dg{)=;OXk;vd$@?2>=qq8jSz| literal 0 HcmV?d00001 diff --git a/src/bg/yellow-checker.png b/src/bg/yellow-checker.png new file mode 100644 index 0000000000000000000000000000000000000000..d5f1e0a58bf0c19d55ac7e2ea1485ab464878a8c GIT binary patch literal 97 zcmeAS@N?(olHy`uVBq!ia0vp^Od!m`1|*BN@u~nR#^NA%Cx&(BWL^R}(w;7kAsjQ4 tYf6$o|L50#XJk-YQ8?Yv!D%KVLuMDJ>IAPJpMlC4JYD@<);T3K0RZS&8D9VZ literal 0 HcmV?d00001 diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..382c37e --- /dev/null +++ b/src/index.css @@ -0,0 +1,214 @@ +@font-face { + font-family: "Paper Mario Dialog Redesigned"; + src: url("pmdialog2.woff2") format("woff2"); +} + +html { + --dark: #313131; + --light: #d6d6ce; + + font-size: 28px; + font-family: "Paper Mario Dialog Redesigned", sans-serif; + font-weight: 700; + line-height: 1.2; + + color: var(--light); + --text-outline: var(--dark); + + background: #090942; /* TODO: dynamic star bg */ + + overflow: hidden; +} + +* { + box-sizing: border-box; + + /* https://owumaro.github.io/text-stroke-generator/ */ + text-shadow: var(--text-outline) 3px 0px 0px, var(--text-outline) 2.83487px 0.981584px 0px, var(--text-outline) 2.35766px 1.85511px 0px, var(--text-outline) 1.62091px 2.52441px 0px, var(--text-outline) 0.705713px 2.91581px 0px, var(--text-outline) -0.287171px 2.98622px 0px, var(--text-outline) -1.24844px 2.72789px 0px, var(--text-outline) -2.07227px 2.16926px 0px, var(--text-outline) -2.66798px 1.37182px 0px, var(--text-outline) -2.96998px 0.42336px 0px, var(--text-outline) -2.94502px -0.571704px 0px, var(--text-outline) -2.59586px -1.50383px 0px, var(--text-outline) -1.96093px -2.27041px 0px, var(--text-outline) -1.11013px -2.78704px 0px, var(--text-outline) -0.137119px -2.99686px 0px, var(--text-outline) 0.850987px -2.87677px 0px, var(--text-outline) 1.74541px -2.43999px 0px, var(--text-outline) 2.44769px -1.73459px 0px, var(--text-outline) 2.88051px -0.838247px 0px; +} + +a:any-link { + color: #3796ff; + --text-outline: #d3e5f9; + text-decoration: none; +} + +button { + all: unset; + + border-radius: 100em; + padding: 0 .3em .2em .3em; + cursor: pointer; + + filter: brightness(1.0); + will-change: filter; + transition: filter 200ms; +} + +button.red { + background: linear-gradient(#f04e54, #f04e54 10%, #b4313e 25%, #b4313e 90%, #7f1729 95%); +} + +button.yellow { + background: linear-gradient(#f0c74e, #f0c74e 10%, #b48b31 25%, #b48b31 90%, #7f5617 95%); +} + +button.inactive { + filter: brightness(0.6); +} + +button:hover { + filter: brightness(1.0); +} + +.tab { + margin-right: .25em; +} + +#container { + /* TODO: use flex on body */ + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + + display: flex; + flex-direction: column; + align-items: center; + + width: 1400px; + max-width: calc(100vw - 10vh); + + height: 1200px; + max-height: calc(100vh - 10vw); +} + +nav { + margin-bottom: -.6em; + z-index: 1; +} + +main { + flex: 1; + width: 100%; + + background-repeat: repeat; + background-size: 32px; + background-position: center; + image-rendering: pixelated; + + border-radius: 1rem; + + padding: 1em; + + overflow-y: auto; + + display: flex; + flex-direction: column; + + transition: 600ms transform ease-in-out; +} + +main.red { + background-color: #e2b6b3; + background-image: url(bg/red-checker.png); +} + +main.yellow { + background-color: #e2d8b3; + background-image: url(bg/yellow-checker.png); +} + +main > * { + image-rendering: initial; +} + +.shadow-box { + background: #ffffff66; + padding: 1em; + margin: .5em; + margin-bottom: 1em; + + border-radius: 16px; + box-shadow: .5em .5em 3px -.2em #00000044; + + border-top: 4px solid #ffffff33; + padding-top: calc(1em - 2px); + + border-left: 2px solid #ffffff33; + border-right: 2px solid #00000055; + border-bottom: 2px solid #00000055; + + position: relative; +} + +.shadow-box-inner { + border-radius: 12px; + box-shadow: inset .3em .3em 3px #00000033; + + width: 100%; + height: 100%; + + display: flex; + overflow: hidden; + + background: white; +} + +button.shadow-box-title { + position: absolute; + left: 50%; + bottom: -1em; + transform: translateX(-50%); + + width: 60%; + + text-align: center; + + border-radius: 1em; + + padding-top: .2em; + height: 1.2em; +} + +.caption { + background-image: url(bg/caption-checker.png); + background-repeat: repeat; + background-size: 32px; + background-position: center; + image-rendering: pixelated; + + border-radius: 1rem; + width: 85%; + height: 3rem; + + padding: 8px 16px; + margin-top: -14px; + z-index: 1; +} + +.progress-chart { + flex: 1; + + font-size: 12px; + overflow: hidden; + + user-select: none; + -webkit-user-select: none; +} + +.outline-invert { + color: var(--dark); + --text-outline: var(--light); +} + +.thin { + font-weight: 400; + text-shadow: var(--text-outline) 2px 0px 0px, var(--text-outline) 1.75517px 0.958851px 0px, var(--text-outline) 1.0806px 1.68294px 0px, var(--text-outline) 0.141474px 1.99499px 0px, var(--text-outline) -0.832294px 1.81859px 0px, var(--text-outline) -1.60229px 1.19694px 0px, var(--text-outline) -1.97998px 0.28224px 0px, var(--text-outline) -1.87291px -0.701566px 0px, var(--text-outline) -1.30729px -1.5136px 0px, var(--text-outline) -0.421592px -1.95506px 0px, var(--text-outline) 0.567324px -1.91785px 0px, var(--text-outline) 1.41734px -1.41108px 0px, var(--text-outline) 1.92034px -0.558831px 0px; +} + +.flex-row { display: flex; flex-direction: row } +.flex-grow { flex: 1; height: 0 } +.flex-spacer { width: 1em; height: 1em } + +.align-right { text-align: right } diff --git a/src/index.html b/src/index.html index 5ea616c..f8f7a81 100644 --- a/src/index.html +++ b/src/index.html @@ -1,11 +1,48 @@ - + Paper Mario Reverse Engineering + + + - Hello world! +
+ +
diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..9a062a7 --- /dev/null +++ b/src/index.js @@ -0,0 +1,14 @@ +import moment from "moment" + +import("./progress.js").then(async ({ fetchData, functionsChart }) => { + const data = await fetchData() + + functionsChart(data, document.getElementById("progress-chart")) + + const first = data[0] + const latest = data[data.length - 1] + + document.getElementById("matched-rom-percent").innerText = Math.round((latest.matchingBytes / latest.totalBytes) * 10000) / 100 + "% matched" // TODO: include data + document.getElementById("functions-ratio").innerText = latest.matchingFuncs + "/" + latest.totalFuncs + document.getElementById("play-time").innerText = moment(latest.timestamp).from(first.timestamp, true) +}).catch(console.error) diff --git a/src/main.jsx b/src/main.jsx new file mode 100644 index 0000000..3af4661 --- /dev/null +++ b/src/main.jsx @@ -0,0 +1,94 @@ +import React, { useState, useRef, useEffect } from "react" +import ReactDOM from "react-dom" +import clsx from "clsx" + +import ProgressPane from "./ProgressPane" + +const tabs = [ + { + slug: "/", + name: "Info", + color: "red", + pane: () =>
I am the info page
, + }, + { + slug: "/progress", + name: "Progress", + color: "yellow", + pane: (props) => + }, +] + +let routedTabIndex = tabs.findIndex(tab => tab.slug === document.location.pathname) +if (routedTabIndex == -1) routedTabIndex = 0 + +function App() { + const [paneIndex, setPaneIndex] = useState(routedTabIndex) + const [tabIndex, setTabIndex] = useState(routedTabIndex) + const [rotation, setRotation] = useState(0) + const [flip, setFlip] = useState(false) + + const pane = useRef() + let lockTabs = false // not state + + function switchToTab(index) { + if (index === paneIndex || index === tabIndex) return + + console.info("switching to tab", index) + + setRotation(rotation - 180) + setTabIndex(index) + + history.pushState(null, tabs[index].name, tabs[index].slug) + + setTimeout(() => { + setPaneIndex(index) + setFlip(!flip) + }, 300) // half the animation time + } + + useEffect(() => { + function listener() { + switchToTab(tabs.findIndex(tab => tab.slug === document.location.pathname)) + } + + window.addEventListener("popstate", listener) + return () => window.removeEventListener("popstate", listener) + }, [rotation, flip, tabIndex, paneIndex]) + + const captionPortal = useRef() + + return <> + +
+
+ {tabs[paneIndex].pane({ captionPortal })} +
+
+
+ +} + +ReactDOM.render(, document.getElementById("container")) diff --git a/src/pmdialog2.woff2 b/src/pmdialog2.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..60c1b9e38f3b57b6cdddb3192dcb1b025ba1df58 GIT binary patch literal 17372 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

L2C+f&Ls^1 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 T&DyLUbI0hnF4lhmY5)KL%Y$;A literal 0 HcmV?d00001 diff --git a/src/progress.js b/src/progress.js new file mode 100644 index 0000000..d4fd472 --- /dev/null +++ b/src/progress.js @@ -0,0 +1,121 @@ +import Chart from "chart.js" + +const csvVersions = { + "1": { + timestamp: s => new Date(parseInt(s) * 1000), + commit: s => s, + totalFuncs: parseInt, + nonMatchingFuncs: parseInt, + matchingFuncs: parseInt, + totalBytes: parseInt, + nonMatchingBytes: parseInt, + matchingBytes: parseInt, + }, +} + +export async function fetchData() { + const csv = await fetch("https://papermar.io/reports/progress.csv") + .then(response => response.text()) + + return csv + .split("\n") + .filter(row => row.length) + .map(row => { + const [version, ...data] = row.split(",") + const structure = csvVersions[version] + const obj = {} + + for (const [key, transform] of Object.entries(structure)) { + obj[key] = transform(data.shift()) + } + + return obj + }) +} + +export async function functionsChart(data, ctx) { + return new Chart(ctx, { + type: "line", + data: { + datasets: [ + { + label: "Matching Functions", + borderColor: "transparent", + backgroundColor: "#7f5617", + pointBackgroundColor: "transparent", + pointBorderColor: "black", + data: data.map(row => { + return { + x: row.timestamp, + y: row.matchingFuncs, + } + }), + lineTension: 0, + }, + { + label: "Split Functions", + borderColor: "transparent", + backgroundColor: "#b48b31", + pointBackgroundColor: "transparent", + pointBorderColor: "black", + data: data.map(row => { + return { + x: row.timestamp, + y: row.totalFuncs, + } + }), + lineTension: 0, + }, + ], + }, + options: { + responsive: true, + maintainAspectRatio: false, + scales: { + xAxes: [ + { + type: "time", + //distribution: "series", + ticks: { + max: Date.now(), + fontFamily: "Paper Mario Dialog Redesigned", + }, + time: { + isoWeekday: true, + unit: "month", + }, + gridLines: { + drawBorder: true, + } + }, + ], + yAxes: [ + { + label: "Functions", + ticks: { + fontFamily: "Paper Mario Dialog Redesigned", + } + } + ] + }, + tooltips: { + enabled: false, + intersect: false, + custom(tooltipModel) { + const title = document.getElementById("progress-chart-tooltip-title") + const desc = document.getElementById("rogress-chart-tooltip-description") + + if (!tooltipModel.dataPoints) { + title.innerText = "" + desc.innerText = "" + } + + const row = data[tooltipModel.dataPoints[0].index] + console.log(row) + + title.innerText = row.commit + }, + }, + }, + }) +}