dyna3/engine-proto/alg-test/ConstructionViewer.jl
Aaron Fenyes d7dbee4c05 Stow algebraic engine prototype
We're using the Gram matrix engine for the next stage of development,
so the algebraic engine shouldn't be at the top level anymore.
2024-07-28 20:50:04 -07:00

223 lines
5.7 KiB
Julia

module Viewer
using Blink
using Colors
using Printf
using Main.Engine
export ConstructionViewer, display!, opentools!, closetools!
# === Blink utilities ===
append_to_head!(w, type, content) = @js w begin
@var element = document.createElement($type)
element.appendChild(document.createTextNode($content))
document.head.appendChild(element)
end
style!(w, stylesheet) = append_to_head!(w, "style", stylesheet)
script!(w, code) = append_to_head!(w, "script", code)
# === construction viewer ===
mutable struct ConstructionViewer
win::Window
function ConstructionViewer()
# create window and open developer console
win = Window(Blink.Dict(:width => 620, :height => 830))
# set stylesheet
style!(win, """
body {
background-color: #ccc;
}
/* the maximum dimensions keep Ganja from blowing up the canvas */
#view {
display: block;
width: 600px;
height: 600px;
margin-top: 10px;
margin-left: 10px;
border-radius: 10px;
background-color: #f0f0f0;
}
#control-panel {
width: 600px;
height: 200px;
box-sizing: border-box;
padding: 5px 10px 5px 10px;
margin-top: 10px;
margin-left: 10px;
overflow-y: scroll;
border-radius: 10px;
background-color: #f0f0f0;
}
#control-panel > div {
margin-top: 5px;
padding: 4px;
border-radius: 5px;
border: solid;
font-family: monospace;
}
""")
# load Ganja.js. for an automatically updated web-hosted version, load from
#
# https://unpkg.com/ganja.js
#
# instead
loadjs!(win, "http://localhost:8000/ganja-1.0.204.js")
# create global functions and variables
script!(win, """
// create algebra
var CGA3 = Algebra(4, 1);
// initialize element list and palette
var elements = [];
var palette = [];
// declare handles for the view and its options
var view;
var viewOpt;
// declare handles for the controls
var controlPanel;
var visToggles;
// create scene function
function scene() {
commands = [];
for (let n = 0; n < elements.length; ++n) {
if (visToggles[n].checked) {
commands.push(palette[n], elements[n]);
}
}
return commands;
}
function updateView() {
requestAnimationFrame(view.update.bind(view, scene));
}
""")
@js win begin
# create view
viewOpt = Dict(
:conformal => true,
:gl => true,
:devicePixelRatio => window.devicePixelRatio
)
view = CGA3.graph(scene, viewOpt)
view.setAttribute(:id, "view")
view.removeAttribute(:style)
document.body.replaceChildren(view)
# create control panel
controlPanel = document.createElement(:div)
controlPanel.setAttribute(:id, "control-panel")
document.body.appendChild(controlPanel)
end
new(win)
end
end
mprod(v, w) =
v[1]*w[1] + v[2]*w[2] + v[3]*w[3] + v[4]*w[4] - v[5]*w[5]
function display!(viewer::ConstructionViewer, elements::Matrix)
# load elements
elements_full = []
for elt in eachcol(Engine.unmix * elements)
if mprod(elt, elt) < 0.5
elt_full = [0; elt; fill(0, 26)]
else
# `elt` is a spacelike vector, representing a generalized sphere, so we
# take its Hodge dual before passing it to Ganja.js. the dual represents
# the same generalized sphere, but Ganja.js only displays planes when
# they're represented by vectors in grade 4 rather than grade 1
elt_full = [fill(0, 26); -elt[5]; -elt[4]; elt[3]; -elt[2]; elt[1]; 0]
end
push!(elements_full, elt_full)
end
@js viewer.win elements = $elements_full.map((elt) -> @new CGA3(elt))
# generate palette. this is Gadfly's `default_discrete_colors` palette,
# available under the MIT license
palette = distinguishable_colors(
length(elements_full),
[LCHab(70, 60, 240)],
transform = c -> deuteranopic(c, 0.5),
lchoices = Float64[65, 70, 75, 80],
cchoices = Float64[0, 50, 60, 70],
hchoices = range(0, stop=330, length=24)
)
palette_packed = [RGB24(c).color for c in palette]
@js viewer.win palette = $palette_packed
# create visibility toggles
@js viewer.win begin
controlPanel.replaceChildren()
visToggles = []
end
for (elt, c) in zip(eachcol(elements), palette)
vec_str = join(map(t -> @sprintf("%.3f", t), elt), ", ")
color_str = "#$(hex(c))"
style_str = "background-color: $color_str; border-color: $color_str;"
@js viewer.win begin
@var toggle = document.createElement(:div)
toggle.setAttribute(:style, $style_str)
toggle.checked = true
toggle.addEventListener(
"click",
() -> begin
toggle.checked = !toggle.checked
toggle.style.backgroundColor = toggle.checked ? $color_str : "inherit";
updateView()
end
)
toggle.appendChild(document.createTextNode($vec_str))
visToggles.push(toggle);
controlPanel.appendChild(toggle);
end
end
# update view
@js viewer.win updateView()
end
function opentools!(viewer::ConstructionViewer)
size(viewer.win, 1240, 830)
opentools(viewer.win)
end
function closetools!(viewer::ConstructionViewer)
closetools(viewer.win)
size(viewer.win, 620, 830)
end
end
# ~~~ sandbox setup ~~~
elements = let
a = sqrt(BigFloat(3)/2)
sqrt(0.5) * BigFloat[
1 1 -1 -1 0
1 -1 1 -1 0
1 -1 -1 1 0
0.5 0.5 0.5 0.5 1+a
0.5 0.5 0.5 0.5 1-a
]
end
# show construction
viewer = Viewer.ConstructionViewer()
Viewer.display!(viewer, elements)