d7dbee4c05
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.
223 lines
5.7 KiB
Julia
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) |