Unfortunately I have to use npm
This commit is contained in:
@@ -1 +1,2 @@
|
||||
.idea
|
||||
/node_modules/
|
||||
|
||||
Generated
+1727
File diff suppressed because it is too large
Load Diff
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
})
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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)]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
canvas {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
+22
-28
@@ -1,36 +1,21 @@
|
||||
// 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 {
|
||||
@@ -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
@@ -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"
|
||||
}
|
||||
+4
-6
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
File diff suppressed because one or more lines are too long
@@ -0,0 +1,11 @@
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
}
|
||||
body canvas {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
Reference in New Issue
Block a user