Compare commits

...

8 commits

5 changed files with 241 additions and 49 deletions

37
.github/workflows/deploy.yml vendored Normal file
View 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 }}

View file

@ -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
View file

@ -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>

View file

@ -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);

View file

@ -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