mirror of
https://github.com/sigmasternchen/queermandelbrot
synced 2025-03-14 23:48:54 +00:00
Compare commits
8 commits
4211fc3074
...
30ba9c2f8f
Author | SHA1 | Date | |
---|---|---|---|
30ba9c2f8f | |||
11a6cb5e45 | |||
3a2be07fbc | |||
59d3a37686 | |||
33d976d2ae | |||
5d77366394 | |||
c98206045e | |||
68f7c53c64 |
5 changed files with 241 additions and 49 deletions
37
.github/workflows/deploy.yml
vendored
Normal file
37
.github/workflows/deploy.yml
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
name: Deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Beam
|
||||
uses: erlef/setup-beam@v1
|
||||
with:
|
||||
otp-version: "26.0.2"
|
||||
gleam-version: "1.5.1"
|
||||
rebar3-version: "3"
|
||||
# elixir-version: "1.15.4"
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
gleam deps download
|
||||
gleam run -m esgleam/install
|
||||
- name: Build
|
||||
run: |
|
||||
gleam build
|
||||
gleam run -m build
|
||||
- name: Deploy to BunnyCDN
|
||||
uses: ayeressian/bunnycdn-storage-deploy@v2.2.4
|
||||
with:
|
||||
source: "./dist/"
|
||||
destination: "/"
|
||||
upload: "true"
|
||||
remove: "true"
|
||||
storageZoneName: ${{ secrets.BUNNYCDN_STORAGE_ZONE }}
|
||||
storagePassword: ${{ secrets.BUNNYCDN_STORAGE_KEY }}
|
||||
|
23
.github/workflows/test.yml
vendored
23
.github/workflows/test.yml
vendored
|
@ -1,23 +0,0 @@
|
|||
name: test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: erlef/setup-beam@v1
|
||||
with:
|
||||
otp-version: "26.0.2"
|
||||
gleam-version: "1.5.1"
|
||||
rebar3-version: "3"
|
||||
# elixir-version: "1.15.4"
|
||||
- run: gleam deps download
|
||||
- run: gleam test
|
||||
- run: gleam format --check src test
|
45
dist/index.html
vendored
45
dist/index.html
vendored
|
@ -2,9 +2,54 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Queer Mandelbrot</title>
|
||||
<style>
|
||||
#mandelbrot {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
#menu {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
font-size: 2vh;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
ul {
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
li {
|
||||
list-style-type: none;
|
||||
}
|
||||
li a {
|
||||
display: inline-block;
|
||||
padding: 0.5vh 0 0.5vh 2vh;
|
||||
width: 10vh;
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
transition: background-color 0.5s, color 0.5s, padding-left 0.5s;
|
||||
}
|
||||
li a:hover, a.selected {
|
||||
padding-left: 4vh;
|
||||
color: black;
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="mandelbrot" width="600" height="400"></canvas>
|
||||
<div id="menu">
|
||||
<ul>
|
||||
<li><a class="rainbow" href="?rainbow">Rainbow</a></li>
|
||||
<li><a class="trans" href="?trans">Trans</a></li>
|
||||
<li><a class="lesbian" href="?lesbian">Lesbian</a></li>
|
||||
<li><a href="https://github.com/sigmasternchen/queermandelbrot" target="_blank">GitHub</a>
|
||||
</ul>
|
||||
</div>
|
||||
<script src="./queermandelbrot.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -2,7 +2,41 @@
|
|||
export const getContext = (id) =>
|
||||
document.getElementById(id).getContext("2d");
|
||||
|
||||
export const getInnerWidth = () =>
|
||||
window.innerWidth;
|
||||
|
||||
export const getInnerHeight = () =>
|
||||
window.innerHeight;
|
||||
|
||||
export const onResize = (callback) =>
|
||||
window.addEventListener("resize", callback);
|
||||
|
||||
export const setPixel = (ctx, x, y, color) => {
|
||||
ctx.fillStyle = color;
|
||||
ctx.fillRect(x, y, 1, 1);
|
||||
};
|
||||
|
||||
export const clear = (ctx) => {
|
||||
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||
};
|
||||
|
||||
export const setSize = (ctx, width, height) => {
|
||||
ctx.canvas.width = width;
|
||||
ctx.canvas.height = height;
|
||||
};
|
||||
|
||||
export const requestAnimationFrame = (callback) => {
|
||||
let eventListener = () => {
|
||||
callback();
|
||||
window.removeEventListener("message", eventListener);
|
||||
}
|
||||
|
||||
window.addEventListener("message", eventListener)
|
||||
window.postMessage(null);
|
||||
};
|
||||
|
||||
export const getQueryString = () => window.location.search;
|
||||
|
||||
export const addClassToQuerySelector = (querySelector, className) =>
|
||||
document.querySelector(querySelector)?.classList?.add?.(className);
|
||||
|
||||
|
|
|
@ -70,40 +70,115 @@ fn value_to_color(colorscheme: ColorScheme, value: Int) -> String {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
let colorscheme = Lesbian
|
||||
fn animate_for_each(
|
||||
list: List(a),
|
||||
still_valid: fn() -> Bool,
|
||||
callback: fn(a) -> Nil,
|
||||
) -> Nil {
|
||||
case list, still_valid() {
|
||||
_, False -> Nil
|
||||
[], True -> Nil
|
||||
[head, ..rest], True -> {
|
||||
callback(head)
|
||||
request_animation_frame(fn() {
|
||||
animate_for_each(rest, still_valid, callback)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let ctx = get_context("mandelbrot")
|
||||
fn redraw(
|
||||
ctx: Context2D,
|
||||
canvas_width: Int,
|
||||
canvas_height: Int,
|
||||
colorscheme: ColorScheme,
|
||||
) {
|
||||
set_size(ctx, canvas_width, canvas_height)
|
||||
clear(ctx)
|
||||
|
||||
let canvas_width = 600
|
||||
let canvas_height = 400
|
||||
let min_x = -2.0
|
||||
let max_x = 1.0
|
||||
let min_y = -1.0
|
||||
let max_y = 1.0
|
||||
|
||||
list.range(0, canvas_width - 1)
|
||||
|> list.each(fn(x) {
|
||||
list.range(0, canvas_height - 1)
|
||||
|> list.each(fn(y) {
|
||||
let location =
|
||||
Complex(
|
||||
real: int.to_float(x)
|
||||
/. int.to_float(canvas_width)
|
||||
*. { max_x -. min_x }
|
||||
+. min_x,
|
||||
imaginary: int.to_float(y)
|
||||
/. int.to_float(canvas_height)
|
||||
*. { max_y -. min_y }
|
||||
+. min_y,
|
||||
)
|
||||
let value = get_mandelbrot_value(location, 1000.0, Complex(0.0, 0.0), 0)
|
||||
case value {
|
||||
Converges -> set_pixel(ctx, x, y, "#000")
|
||||
Diverges(value) ->
|
||||
set_pixel(ctx, x, y, value_to_color(colorscheme, value))
|
||||
}
|
||||
})
|
||||
|> list.sized_chunk(1)
|
||||
|> animate_for_each(
|
||||
fn() {
|
||||
get_inner_width() == canvas_width && get_inner_height() == canvas_height
|
||||
},
|
||||
list.each(_, fn(x) {
|
||||
list.range(0, canvas_height - 1)
|
||||
|> list.each(fn(y) {
|
||||
let pad_horizontal =
|
||||
{ max_x -. min_x } /. { max_y -. min_y }
|
||||
<. int.to_float(canvas_width) /. int.to_float(canvas_height)
|
||||
|
||||
let #(sizing_factor, x_offset, y_offset) = case pad_horizontal {
|
||||
True -> {
|
||||
let scaling = int.to_float(canvas_height) /. { max_y -. min_y }
|
||||
#(
|
||||
scaling,
|
||||
min_x
|
||||
-. {
|
||||
int.to_float(canvas_width) /. scaling -. { max_x -. min_x }
|
||||
}
|
||||
/. 2.0,
|
||||
min_y,
|
||||
)
|
||||
}
|
||||
False -> {
|
||||
let scaling = int.to_float(canvas_width) /. { max_x -. min_x }
|
||||
#(
|
||||
scaling,
|
||||
min_x,
|
||||
min_y
|
||||
-. {
|
||||
int.to_float(canvas_height) /. scaling -. { max_y -. min_y }
|
||||
}
|
||||
/. 2.0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
let location =
|
||||
Complex(
|
||||
real: int.to_float(x) /. sizing_factor +. x_offset,
|
||||
imaginary: int.to_float(y) /. sizing_factor +. y_offset,
|
||||
)
|
||||
let value = get_mandelbrot_value(location, 1000.0, Complex(0.0, 0.0), 0)
|
||||
case value {
|
||||
Converges -> set_pixel(ctx, x, y, "#000")
|
||||
Diverges(value) ->
|
||||
set_pixel(ctx, x, y, value_to_color(colorscheme, value))
|
||||
}
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
let ctx = get_context("mandelbrot")
|
||||
let query_string = get_query_string()
|
||||
|
||||
let colorscheme = case query_string {
|
||||
"?trans" -> Trans
|
||||
"?lesbian" -> Lesbian
|
||||
"?rainbow" | _ -> Rainbow
|
||||
}
|
||||
|
||||
io.debug(colorscheme)
|
||||
|
||||
case colorscheme {
|
||||
Rainbow -> add_class_to_query_selector("a.rainbow", "selected")
|
||||
Trans -> add_class_to_query_selector("a.trans", "selected")
|
||||
Lesbian -> add_class_to_query_selector("a.lesbian", "selected")
|
||||
}
|
||||
|
||||
redraw(ctx, get_inner_width(), get_inner_height(), colorscheme)
|
||||
|
||||
on_resize(fn() {
|
||||
redraw(ctx, get_inner_width(), get_inner_height(), colorscheme)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -112,5 +187,29 @@ type Context2D
|
|||
@external(javascript, "./canvas.mjs", "getContext")
|
||||
fn get_context(id: String) -> Context2D
|
||||
|
||||
@external(javascript, "./canvas.mjs", "getInnerWidth")
|
||||
fn get_inner_width() -> Int
|
||||
|
||||
@external(javascript, "./canvas.mjs", "getInnerHeight")
|
||||
fn get_inner_height() -> Int
|
||||
|
||||
@external(javascript, "./canvas.mjs", "onResize")
|
||||
fn on_resize(callback: fn() -> Nil) -> Nil
|
||||
|
||||
@external(javascript, "./canvas.mjs", "setPixel")
|
||||
fn set_pixel(ctx: Context2D, x: Int, y: Int, color: String) -> Nil
|
||||
|
||||
@external(javascript, "./canvas.mjs", "clear")
|
||||
fn clear(ctx: Context2D) -> Nil
|
||||
|
||||
@external(javascript, "./canvas.mjs", "setSize")
|
||||
fn set_size(ctx: Context2D, width: Int, height: Int) -> Nil
|
||||
|
||||
@external(javascript, "./canvas.mjs", "requestAnimationFrame")
|
||||
fn request_animation_frame(callback: fn() -> Nil) -> Nil
|
||||
|
||||
@external(javascript, "./canvas.mjs", "getQueryString")
|
||||
fn get_query_string() -> String
|
||||
|
||||
@external(javascript, "./canvas.mjs", "addClassToQuerySelector")
|
||||
fn add_class_to_query_selector(query: String, class: String) -> Nil
|
||||
|
|
Loading…
Reference in a new issue