Unfortunately I have to use npm

This commit is contained in:
Lani Aung
2025-06-03 21:46:32 -07:00
parent f49a1617fb
commit 23e5e075cc
14 changed files with 2260 additions and 38 deletions
+2 -1
View File
@@ -1 +1,2 @@
.idea
.idea
/node_modules/
+1727
View File
File diff suppressed because it is too large Load Diff
+11
View File
@@ -0,0 +1,11 @@
{
"devDependencies": {
"ts-loader": "^9.5.2",
"typescript": "^5.8.3",
"webpack": "^5.99.9",
"webpack-cli": "^6.0.1"
},
"dependencies": {
"openmeteo": "^1.2.0"
}
}
+10
View File
@@ -0,0 +1,10 @@
import Substrate from "./substrate"
import Weather from "./weather";
const background = new Substrate("substrate-canvas", 20)
let weather: Weather
navigator.geolocation.getCurrentPosition(position => {
weather = new Weather(position.coords.latitude, position.coords.longitude)
}, () => {
weather = new Weather()
})
+11
View File
@@ -0,0 +1,11 @@
import State from "./state"
export default class SandPainter {
private color: number
private gain: number
constructor(state: State) {
this.color = state.getRandomColor()
this.gain = Math.random() / 10
}
}
+29
View File
@@ -0,0 +1,29 @@
export default class State {
colors: number[]
maxCracks: number
crackGrid: number[]
height: number
width: number
constructor(colors: number[], maxCracks: number, height: number, width: number) {
this.colors = colors
this.maxCracks = maxCracks
this.height = height
this.width = width
this.crackGrid = []
for (let x = 0; x < width; x++) {
for (let y = 0; y < height; y++) {
this.crackGrid[y * width + x] = 10000
}
}
for (let i = 0; i < 16; i++) {
this.crackGrid[Math.floor(Math.random() * (height * width))] = Math.ceil(Math.random() * 360)
}
}
getRandomColor(): number {
return this.colors[Math.floor(Math.random() * this.colors.length)]
}
}
+14
View File
@@ -0,0 +1,14 @@
html,
body {
height: 100%;
margin: 0;
overflow: hidden;
padding: 0;
}
body {
canvas {
height: 100%;
width: 100%;
}
}
+23 -29
View File
@@ -1,38 +1,23 @@
// TS attempt of http://www.complexification.net/gallery/machines/substrate/index.php by laniaung.me 2025-06-02
import State from "./state"
import SandPainter from "./sandPainter"
export default class Substrate {
private colors = [ 0x3a1e3e, 0x7c2d3b, 0xb94f3c, 0xf4a462, 0xf9c54e ] // https://colormagic.app/palette/671eeb6c42273fc4c1bb1ac7
private maxCracks = 20
private state: State
constructor(maxCracks: number) {
this.maxCracks = maxCracks
}
}
constructor(id: string, maxCracks: number, colors?: number[]) {
if (!colors?.length) colors = [ 0x3a1e3e, 0x7c2d3b, 0xb94f3c, 0xf4a462, 0xf9c54e ] // https://colormagic.app/palette/671eeb6c42273fc4c1bb1ac7
class State {
crackGrid: number[]
height: number
width: number
constructor(height: number, width: number) {
this.height = height
this.width = width
this.crackGrid = []
for (let x = 0; x < width; x++) {
for (let y = 0; y < height; y++) {
this.crackGrid[y * width + x] = 10000
}
let canvas = document.getElementById(id)
if (!canvas) {
canvas = document.createElement("canvas")
document.body.appendChild(canvas)
}
for (let i = 0; i < 16; i++) {
this.crackGrid[Math.floor(Math.random() * (height * width))] = Math.ceil(Math.random() * 360)
}
this.state = new State(colors, maxCracks, canvas.clientHeight, canvas.clientWidth)
}
}
class SandPainter {
}
class Crack {
private x: number = 0
private y: number = 0
@@ -41,7 +26,7 @@ class Crack {
private state: State
constructor(state: State) {
this.painter = new SandPainter()
this.painter = new SandPainter(state)
this.state = state
this.init()
}
@@ -73,6 +58,15 @@ class Crack {
}
continue() {
this.x += .42 * Math.cos(this.angle * Math.PI / 180)
this.y += .42 * Math.sin(this.angle * Math.PI / 180)
}
}
}
declare global {
interface Window {
main: Substrate
}
}
window.main = new Substrate("substrate-canvas", 20)
+114
View File
@@ -0,0 +1,114 @@
import { fetchWeatherApi } from 'openmeteo'
export default class Weather {
private readonly options: WeatherOptions
constructor(latitude?: number, longitude?: number) {
latitude ??= 47.61002138071677
longitude ??= -122.17906310779568
this.options = new WeatherOptions()
this.options = { ...this.options, latitude, longitude }
this.getWeather(this.options)
}
async getWeather(options: WeatherOptions) {
const url = "https://api.open-meteo.com/v1/forecast"
const responses = await fetchWeatherApi(url, this.options)
for (const response of responses) {
// Attributes for timezone and location
const utcOffsetSeconds = response.utcOffsetSeconds()
const timezone = response.timezone()
const timezoneAbbreviation = response.timezoneAbbreviation()
const latitude = response.latitude()
const longitude = response.longitude()
const current = response.current()!
const hourly = response.hourly()!
const daily = response.daily()!
const sunrise = daily.variables(2)!
const sunset = daily.variables(3)!
// Note: The order of weather variables in the URL query and the indices below need to match!
const weatherData = {
current: {
time: new Date((Number(current.time()) + utcOffsetSeconds) * 1000),
temperature2m: current.variables(0)!.value(),
relativeHumidity2m: current.variables(1)!.value(),
apparentTemperature: current.variables(2)!.value(),
precipitation: current.variables(3)!.value(),
windSpeed10m: current.variables(4)!.value(),
windGusts10m: current.variables(5)!.value(),
windDirection10m: current.variables(6)!.value(),
},
hourly: {
time: [...Array((Number(hourly.timeEnd()) - Number(hourly.time())) / hourly.interval())].map(
(_, i) => new Date((Number(hourly.time()) + i * hourly.interval() + utcOffsetSeconds) * 1000)
),
temperature2m: hourly.variables(0)!.valuesArray()!,
precipitation: hourly.variables(1)!.valuesArray()!,
precipitationProbability: hourly.variables(2)!.valuesArray()!,
},
daily: {
time: [...Array((Number(daily.timeEnd()) - Number(daily.time())) / daily.interval())].map(
(_, i) => new Date((Number(daily.time()) + i * daily.interval() + utcOffsetSeconds) * 1000)
),
temperature2mMax: daily.variables(0)!.valuesArray()!,
temperature2mMin: daily.variables(1)!.valuesArray()!,
sunrise: [...Array(sunrise.valuesInt64Length())].map(
(_, i) => new Date((Number(sunrise.valuesInt64(i)) + utcOffsetSeconds) * 1000)
),
sunset: [...Array(sunset.valuesInt64Length())].map(
(_, i) => new Date((Number(sunset.valuesInt64(i)) + utcOffsetSeconds) * 1000)
),
uvIndexMax: daily.variables(4)!.valuesArray()!,
precipitationSum: daily.variables(5)!.valuesArray()!,
precipitationHours: daily.variables(6)!.valuesArray()!,
windSpeed10mMax: daily.variables(7)!.valuesArray()!,
windGusts10mMax: daily.variables(8)!.valuesArray()!,
windDirection10mDominant: daily.variables(9)!.valuesArray()!,
},
}
// `weatherData` now contains a simple structure with arrays for datetime and weather data
for (let i = 0; i < weatherData.hourly.time.length; i++) {
console.log(
weatherData.hourly.time[i].toISOString(),
weatherData.hourly.temperature2m[i],
weatherData.hourly.precipitation[i],
weatherData.hourly.precipitationProbability[i]
)
}
for (let i = 0; i < weatherData.daily.time.length; i++) {
console.log(
weatherData.daily.time[i].toISOString(),
weatherData.daily.temperature2mMax[i],
weatherData.daily.temperature2mMin[i],
weatherData.daily.sunrise[i].toISOString(),
weatherData.daily.sunset[i].toISOString(),
weatherData.daily.uvIndexMax[i],
weatherData.daily.precipitationSum[i],
weatherData.daily.precipitationHours[i],
weatherData.daily.windSpeed10mMax[i],
weatherData.daily.windGusts10mMax[i],
weatherData.daily.windDirection10mDominant[i]
)
}
}
}
}
class WeatherOptions {
latitude: number
longitude: number
daily: string[] = ["temperature_2m_max", "temperature_2m_min", "sunrise", "sunset", "uv_index_max", "precipitation_sum", "precipitation_hours", "wind_speed_10m_max", "wind_gusts_10m_max", "wind_direction_10m_dominant"]
hourly: string[] = ["temperature_2m", "precipitation", "precipitation_probability"]
current: string[] = ["temperature_2m", "relative_humidity_2m", "apparent_temperature", "precipitation", "wind_speed_10m", "wind_gusts_10m", "wind_direction_10m"]
timezone: string = "America/Los_Angeles"
"wind_speed_unit": string = "mph"
"temperature_unit": string = "fahrenheit"
"precipitation_unit": string = "inch"
}
+5 -7
View File
@@ -1,10 +1,8 @@
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
"outDir": "./www/",
"noImplicitAny": true,
"module": "es6",
"moduleResolution": "node"
}
}
}
+21
View File
@@ -0,0 +1,21 @@
const path = require("path");
module.exports = {
entry: "./src/launch.ts",
module: {
rules: [
{
test: /\.tsx?$/,
use: "ts-loader",
exclude: /node_modules/,
},
],
},
resolve: {
extensions: [".tsx", ".ts", ".js"],
},
output: {
filename: "main.js",
path: path.resolve(__dirname, "www"),
},
};
+4 -1
View File
@@ -3,8 +3,11 @@
<head>
<meta charset="UTF-8">
<title>Weather</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<canvas id="substrate-canvas"></canvas>
<div id="weather-panel"></div>
<script src="main.js"></script>
</body>
</html>
+278
View File
File diff suppressed because one or more lines are too long
+11
View File
@@ -0,0 +1,11 @@
html,
body {
height: 100%;
margin: 0;
overflow: hidden;
padding: 0;
}
body canvas {
height: 100%;
width: 100%;
}