Unfortunately I have to use npm
This commit is contained in:
@@ -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%;
|
||||
}
|
||||
}
|
||||
+23
-29
@@ -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
@@ -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"
|
||||
}
|
||||
Reference in New Issue
Block a user