mirror of
https://github.com/sigmasternchen/queermandelbrot
synced 2025-03-15 07:58:53 +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>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Queer Mandelbrot</title>
|
<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>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<canvas id="mandelbrot" width="600" height="400"></canvas>
|
<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>
|
<script src="./queermandelbrot.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -2,7 +2,41 @@
|
||||||
export const getContext = (id) =>
|
export const getContext = (id) =>
|
||||||
document.getElementById(id).getContext("2d");
|
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) => {
|
export const setPixel = (ctx, x, y, color) => {
|
||||||
ctx.fillStyle = color;
|
ctx.fillStyle = color;
|
||||||
ctx.fillRect(x, y, 1, 1);
|
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() {
|
fn animate_for_each(
|
||||||
let colorscheme = Lesbian
|
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 min_x = -2.0
|
||||||
let max_x = 1.0
|
let max_x = 1.0
|
||||||
let min_y = -1.0
|
let min_y = -1.0
|
||||||
let max_y = 1.0
|
let max_y = 1.0
|
||||||
|
|
||||||
list.range(0, canvas_width - 1)
|
list.range(0, canvas_width - 1)
|
||||||
|> list.each(fn(x) {
|
|> list.sized_chunk(1)
|
||||||
list.range(0, canvas_height - 1)
|
|> animate_for_each(
|
||||||
|> list.each(fn(y) {
|
fn() {
|
||||||
let location =
|
get_inner_width() == canvas_width && get_inner_height() == canvas_height
|
||||||
Complex(
|
},
|
||||||
real: int.to_float(x)
|
list.each(_, fn(x) {
|
||||||
/. int.to_float(canvas_width)
|
list.range(0, canvas_height - 1)
|
||||||
*. { max_x -. min_x }
|
|> list.each(fn(y) {
|
||||||
+. min_x,
|
let pad_horizontal =
|
||||||
imaginary: int.to_float(y)
|
{ max_x -. min_x } /. { max_y -. min_y }
|
||||||
/. int.to_float(canvas_height)
|
<. int.to_float(canvas_width) /. int.to_float(canvas_height)
|
||||||
*. { max_y -. min_y }
|
|
||||||
+. min_y,
|
let #(sizing_factor, x_offset, y_offset) = case pad_horizontal {
|
||||||
)
|
True -> {
|
||||||
let value = get_mandelbrot_value(location, 1000.0, Complex(0.0, 0.0), 0)
|
let scaling = int.to_float(canvas_height) /. { max_y -. min_y }
|
||||||
case value {
|
#(
|
||||||
Converges -> set_pixel(ctx, x, y, "#000")
|
scaling,
|
||||||
Diverges(value) ->
|
min_x
|
||||||
set_pixel(ctx, x, y, value_to_color(colorscheme, value))
|
-. {
|
||||||
}
|
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")
|
@external(javascript, "./canvas.mjs", "getContext")
|
||||||
fn get_context(id: String) -> Context2D
|
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")
|
@external(javascript, "./canvas.mjs", "setPixel")
|
||||||
fn set_pixel(ctx: Context2D, x: Int, y: Int, color: String) -> Nil
|
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