first commit
This commit is contained in:
commit
3714d6e1fc
13 changed files with 10781 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
.make.*
|
||||||
|
elm-stuff/
|
||||||
|
error.txt
|
2
.watchmakerc
Normal file
2
.watchmakerc
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
extensions:
|
||||||
|
- .elm
|
11
Makefile
Normal file
11
Makefile
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
DIRNAME=$(notdir $(CURDIR))
|
||||||
|
|
||||||
|
ELMS=$(wildcard src/*.elm)
|
||||||
|
|
||||||
|
app.js: src/App.elm $(ELMS)
|
||||||
|
-elm make $< --output=$@ 2> error.txt
|
||||||
|
@cat error.txt
|
||||||
|
|
||||||
|
upload: app.js index.html style.css
|
||||||
|
rsync -avz . clpland:~/domains/somethingorotherwhatever.com/html/$(DIRNAME)
|
||||||
|
@echo "Uploaded to https://somethingorotherwhatever.com/$(DIRNAME)"
|
29
elm.json
Normal file
29
elm.json
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"type": "application",
|
||||||
|
"source-directories": [
|
||||||
|
"src"
|
||||||
|
],
|
||||||
|
"elm-version": "0.19.1",
|
||||||
|
"dependencies": {
|
||||||
|
"direct": {
|
||||||
|
"elm/browser": "1.0.2",
|
||||||
|
"elm/core": "1.0.5",
|
||||||
|
"elm/html": "1.0.0",
|
||||||
|
"elm/json": "1.1.3",
|
||||||
|
"elm/parser": "1.1.0",
|
||||||
|
"elm/random": "1.0.0",
|
||||||
|
"elm/svg": "1.0.1",
|
||||||
|
"elm-community/list-extra": "8.7.0",
|
||||||
|
"elm-community/random-extra": "3.2.0"
|
||||||
|
},
|
||||||
|
"indirect": {
|
||||||
|
"elm/time": "1.0.0",
|
||||||
|
"elm/url": "1.0.0",
|
||||||
|
"elm/virtual-dom": "1.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test-dependencies": {
|
||||||
|
"direct": {},
|
||||||
|
"indirect": {}
|
||||||
|
}
|
||||||
|
}
|
23
index.html
Normal file
23
index.html
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
|
<title>Elm app by clp</title>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<h1>Elm app by clp</h1>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<p>This is an app which will either load succesfully, and you'll wonder whether you saw this text at all, or fail ignominiously, showing you only this text.</p>
|
||||||
|
<p>On balance of probabilities: I'm sorry I couldn't be bothered to make this work for you.</p>
|
||||||
|
</main>
|
||||||
|
<footer>Made by <a href="https://somethingorotherwhatever.com">clp</a></footer>
|
||||||
|
|
||||||
|
<script src="app.js?1"></script>
|
||||||
|
<script src="load-app.js" type="module"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
11
load-app.js
Normal file
11
load-app.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import show_error from './show-error.mjs';
|
||||||
|
import './svg-events.mjs';
|
||||||
|
async function init_app() {
|
||||||
|
const compilation_error = await show_error;
|
||||||
|
if(compilation_error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const app = Elm.App.init({node: document.body, flags: {}});
|
||||||
|
}
|
||||||
|
|
||||||
|
init_app();
|
22
show-error.mjs
Normal file
22
show-error.mjs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
export default fetch('/error.txt').then(r=>{
|
||||||
|
if(r.ok) {
|
||||||
|
return r.text();
|
||||||
|
} else {
|
||||||
|
throw('');
|
||||||
|
}
|
||||||
|
}).then(text => {
|
||||||
|
if(!text) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
document.body.innerHTML = '';
|
||||||
|
document.body.classList.add('compilation-error');
|
||||||
|
const error_show = document.createElement('pre');
|
||||||
|
error_show.setAttribute('id','build-error');
|
||||||
|
error_show.style.background = 'black';
|
||||||
|
error_show.style.color = 'white';
|
||||||
|
error_show.style.padding = '1em';
|
||||||
|
error_show.style['font-size'] = '16px';
|
||||||
|
error_show.textContent = text;
|
||||||
|
document.body.appendChild(error_show);
|
||||||
|
return true;
|
||||||
|
}).catch(e => false);
|
1184
src/App.elm
Normal file
1184
src/App.elm
Normal file
File diff suppressed because it is too large
Load diff
17
src/Util.elm
Normal file
17
src/Util.elm
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
module Util exposing (..)
|
||||||
|
|
||||||
|
fi = String.fromInt
|
||||||
|
ff = String.fromFloat
|
||||||
|
tf = toFloat
|
||||||
|
|
||||||
|
pairMap : (a -> b) -> a -> (a,b)
|
||||||
|
pairMap fn a = (a, fn a)
|
||||||
|
|
||||||
|
third : (a,b,c) -> c
|
||||||
|
third (a,b,c) = c
|
||||||
|
|
||||||
|
maybeIf : (a -> Bool) -> Maybe a -> Maybe a
|
||||||
|
maybeIf condition = Maybe.andThen (\a -> if condition a then Just a else Nothing)
|
||||||
|
|
||||||
|
capitalise : String -> String
|
||||||
|
capitalise str = (String.left 1 str |> String.toUpper) ++ (String.dropLeft 1 str)
|
60
src/Vector.elm
Normal file
60
src/Vector.elm
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
module Vector exposing (..)
|
||||||
|
|
||||||
|
import Tuple exposing (pair, first, second)
|
||||||
|
import Util exposing (maybeIf)
|
||||||
|
|
||||||
|
type alias Vector = (Float, Float)
|
||||||
|
|
||||||
|
type alias LineSegment = (Vector, Vector)
|
||||||
|
|
||||||
|
add (x1,y1) (x2,y2) = (x1+x2, y1+y2)
|
||||||
|
sub (x1,y1) (x2,y2) = (x1-x2, y1-y2)
|
||||||
|
len (x,y) = sqrt (x*x + y*y)
|
||||||
|
smul s (x,y) = (s*x, s*y)
|
||||||
|
distance v1 v2 = sub v1 v2 |> len
|
||||||
|
|
||||||
|
midpoint (x1,y1) (x2,y2) = ((x1+x2)/2, (y1+y2)/2)
|
||||||
|
|
||||||
|
normalise (x,y) =
|
||||||
|
let
|
||||||
|
d = len (x,y)
|
||||||
|
in
|
||||||
|
(x/d, y/d)
|
||||||
|
|
||||||
|
normal (x,y) = normalise (-y,x)
|
||||||
|
|
||||||
|
dot (x1,y1) (x2,y2) = x1*x2 + y1*y2
|
||||||
|
|
||||||
|
sum : List Vector -> Vector
|
||||||
|
sum = List.foldl add (0,0)
|
||||||
|
|
||||||
|
point_line_distance p (p1,p2) =
|
||||||
|
let
|
||||||
|
v1 = sub p2 p1
|
||||||
|
v2 = sub p p1
|
||||||
|
n = normal v1
|
||||||
|
d1 = len v1
|
||||||
|
alpha = (dot v1 v2) / d1
|
||||||
|
d = len (sub p (add p1 (smul alpha v1)))
|
||||||
|
in
|
||||||
|
if alpha<0 then len (sub p p1) else if alpha>d1 then len (sub p p2) else d
|
||||||
|
|
||||||
|
closest : (a -> Vector) -> Vector -> List a -> Maybe (Int, Float, a)
|
||||||
|
closest get_position p1 = List.map (\a -> (a, get_position a |> sub p1 |> len)) >> List.indexedMap (\i (a,d) -> (i,d,a)) >> List.sortBy (\(i,d,a) -> d) >> List.head
|
||||||
|
|
||||||
|
closest_within : (a -> Float) -> (a -> Vector) -> Vector -> List a -> Maybe (Int, Float, a)
|
||||||
|
closest_within get_limit get_position p1 things =
|
||||||
|
closest get_position p1 things
|
||||||
|
|> maybeIf (\(i,d,a) -> d < get_limit a)
|
||||||
|
|
||||||
|
{- The closest point to the given segment. Returns how far along the segment the point is (0 to 1), and the point -}
|
||||||
|
closest_point_on_segment : LineSegment -> Vector -> (Float,Vector)
|
||||||
|
closest_point_on_segment (p1,p2) p =
|
||||||
|
let
|
||||||
|
v = sub p2 p1
|
||||||
|
vp = sub p p1
|
||||||
|
l = len v
|
||||||
|
d = (dot v vp) / ((len v)^2)
|
||||||
|
t = clamp 0 1 d
|
||||||
|
in
|
||||||
|
(d, add p1 (smul t v))
|
55
style.css
Normal file
55
style.css
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
body {
|
||||||
|
display: grid;
|
||||||
|
grid-template:
|
||||||
|
"svg" 1fr
|
||||||
|
"controls" 10em
|
||||||
|
;
|
||||||
|
height: 100svh;
|
||||||
|
margin: 0;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
&.compilation-error {
|
||||||
|
grid-template: "error" 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
& #debug-log {
|
||||||
|
background: #f4f4f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
& svg {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
& text {
|
||||||
|
user-select: none;
|
||||||
|
paint-order: stroke fill;
|
||||||
|
stroke-linejoin: round;
|
||||||
|
stroke-linecap: round;
|
||||||
|
fill: white;
|
||||||
|
stroke: black;
|
||||||
|
stroke-width: 0.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
& #obstacle-outlines circle {
|
||||||
|
filter: blur(0.5px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& #controls {
|
||||||
|
display: grid;
|
||||||
|
grid-auto-flow: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .road path {
|
||||||
|
stroke-linejoin: round;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
& #recipe-list {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
max-height: 100%;
|
||||||
|
overflow: scroll;
|
||||||
|
}
|
||||||
|
}
|
62
svg-events.mjs
Normal file
62
svg-events.mjs
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
function getsvg(event) {
|
||||||
|
let t = event.target;
|
||||||
|
while(t && t.tagName.toLowerCase()!='svg') {
|
||||||
|
t = t.parentElement;
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getcoords(event) {
|
||||||
|
const t = getsvg(event);
|
||||||
|
const point = t.createSVGPoint()
|
||||||
|
point.x = event.clientX
|
||||||
|
point.y = event.clientY
|
||||||
|
const position = point.matrixTransform(t.getScreenCTM().inverse())
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
var observer = new MutationObserver(function (mutations) {
|
||||||
|
mutations.forEach(function (mutation) {
|
||||||
|
if (mutation.type === 'childList') {
|
||||||
|
Array
|
||||||
|
.from(mutation.addedNodes)
|
||||||
|
.forEach(function (node) {
|
||||||
|
if(node.nodeType != document.ELEMENT_NODE || node.tagName.toLowerCase() !== 'svg') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
node.addEventListener('pointermove', function (event) {
|
||||||
|
const t = getsvg(event);
|
||||||
|
if(!t) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const position = getcoords(event);
|
||||||
|
const svgMoveEvent = new CustomEvent('svgmove', {
|
||||||
|
detail: {x: position.x, y: position.y},
|
||||||
|
});
|
||||||
|
t.dispatchEvent(svgMoveEvent);
|
||||||
|
});
|
||||||
|
function svg_touch_event(name) {
|
||||||
|
node.addEventListener(name, function(event) {
|
||||||
|
const t = getsvg(event);
|
||||||
|
if(!t) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
event.preventDefault();
|
||||||
|
//event.stopPropagation();
|
||||||
|
const touches = Array.from(event.changedTouches).map(touch => {
|
||||||
|
const position = getcoords(touch);
|
||||||
|
return {identifier: touch.identifier, position: position}
|
||||||
|
});
|
||||||
|
const touchEvent = new CustomEvent('svg'+name, {
|
||||||
|
detail: touches
|
||||||
|
});
|
||||||
|
t.dispatchEvent(touchEvent);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
['touchstart','touchmove','touchend'].forEach(svg_touch_event)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(document.body, { childList: true, subtree: true });
|
Loading…
Add table
Add a link
Reference in a new issue