From d1ce91d2aa6f02706a11dd533daeb86ab8523f21 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Mon, 24 Jun 2024 19:37:57 -0700 Subject: [PATCH 01/16] Get a Ganja.js visualization running in Blink --- engine-proto/ganja-test/ganja-test.html | 51 ++++++++++++++++++ engine-proto/ganja-test/ganja-test.jl | 71 +++++++++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 engine-proto/ganja-test/ganja-test.html create mode 100644 engine-proto/ganja-test/ganja-test.jl diff --git a/engine-proto/ganja-test/ganja-test.html b/engine-proto/ganja-test/ganja-test.html new file mode 100644 index 0000000..f1ecf45 --- /dev/null +++ b/engine-proto/ganja-test/ganja-test.html @@ -0,0 +1,51 @@ + + + + + + + + + + diff --git a/engine-proto/ganja-test/ganja-test.jl b/engine-proto/ganja-test/ganja-test.jl new file mode 100644 index 0000000..ea8b334 --- /dev/null +++ b/engine-proto/ganja-test/ganja-test.jl @@ -0,0 +1,71 @@ +using Blink +import Blink: JSString + +# === styling utility === + +style!(w, stylesheet) = @js win begin + @var style = document.createElement("style"); + style.appendChild(document.createTextNode($stylesheet)); + document.head.appendChild(style); +end + +# === page source === + +stylesheet = """ +body { + background-color: #ffe0f0; +} + +/* needed to keep Ganja canvas from blowing up */ +canvas { + min-width: 600px; + max-width: 600px; + min-height: 600px; + max-height: 600px; +} +""" + +# the "points spheres plane" example from the Ganja coffee shop +# +# https://enkimute.github.io/ganja.js/examples/coffeeshop.html#cga3d_points_spheres_planes +# +sphere_example = """ +Algebra(4, 1, ()=>{ + // We start by defining a null basis, and upcasting for points + var ni = 1e4+1e5, no = .5e5-.5e4; + var up = (x)=> no + x + .5*x*x*ni; + + // Next we'll define 4 points + var p1 = up(1e1), p2 = up(1e2), p3 = up(-1e3), p4 = up(-1e2); + + // The outer product can be used to construct the sphere through + // any four points. + var s = ()=>p1^p2^p3^p4; + + // The outer product between any three points and infinity is a plane. + var p = ()=>p1^p2^p3^ni; + + // Graph the items. + document.body.appendChild(this.graph([ + 0x00FF0000, p1, "p1", p2, "p2", p3, "p3", p4, "p4", // points + 0xE0008800, p, "p", // plane + 0xE00000FF, s, "s" // sphere + ], {conformal: true, gl: true, grid: true})); +}); +""" + +# === page construction === + +# create window and open developer console +win = Window() +opentools(win) + +# set stylesheet +style!(win, stylesheet) + +# load Ganja.js +loadjs!(win, "https://unpkg.com/ganja.js") + +# launch Ganja visualization +body!(win, "", async=false) +js(win, JSString(sphere_example)) From 3c344815196a63cec0e5887239db65facbf77426 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Tue, 25 Jun 2024 01:54:01 -0700 Subject: [PATCH 02/16] Get familiar with Ganja.js inline syntax --- engine-proto/ganja-test/ganja-test.html | 50 ++++++++++++------------- engine-proto/ganja-test/ganja-test.jl | 46 ++++++++++++----------- 2 files changed, 47 insertions(+), 49 deletions(-) diff --git a/engine-proto/ganja-test/ganja-test.html b/engine-proto/ganja-test/ganja-test.html index f1ecf45..26eba9d 100644 --- a/engine-proto/ganja-test/ganja-test.html +++ b/engine-proto/ganja-test/ganja-test.html @@ -18,34 +18,30 @@ diff --git a/engine-proto/ganja-test/ganja-test.jl b/engine-proto/ganja-test/ganja-test.jl index ea8b334..155caa9 100644 --- a/engine-proto/ganja-test/ganja-test.jl +++ b/engine-proto/ganja-test/ganja-test.jl @@ -30,28 +30,30 @@ canvas { # https://enkimute.github.io/ganja.js/examples/coffeeshop.html#cga3d_points_spheres_planes # sphere_example = """ -Algebra(4, 1, ()=>{ - // We start by defining a null basis, and upcasting for points - var ni = 1e4+1e5, no = .5e5-.5e4; - var up = (x)=> no + x + .5*x*x*ni; - - // Next we'll define 4 points - var p1 = up(1e1), p2 = up(1e2), p3 = up(-1e3), p4 = up(-1e2); - - // The outer product can be used to construct the sphere through - // any four points. - var s = ()=>p1^p2^p3^p4; - - // The outer product between any three points and infinity is a plane. - var p = ()=>p1^p2^p3^ni; - - // Graph the items. - document.body.appendChild(this.graph([ - 0x00FF0000, p1, "p1", p2, "p2", p3, "p3", p4, "p4", // points - 0xE0008800, p, "p", // plane - 0xE00000FF, s, "s" // sphere - ], {conformal: true, gl: true, grid: true})); -}); +// in the default view, e4 + e5 is the point at infinity +CGA3 = Algebra(4, 1); +v1 = CGA3.inline(() => 1e1 + 1e5)(); +v2 = CGA3.inline(() => 1e2 + 1e5)(); +v3 = CGA3.inline(() => 1e3 + 1e5)(); +w1 = CGA3.inline(() => 1e1 - Math.sqrt(0.2)*1e4 + Math.sqrt(1.2)*1e5)(); +w2 = CGA3.inline(() => 1e2 - Math.sqrt(0.2)*1e4 + Math.sqrt(1.2)*1e5)(); +w3 = CGA3.inline(() => 1e3 - Math.sqrt(0.2)*1e4 + Math.sqrt(1.2)*1e5)(); +s = CGA3.inline(() => -Math.sqrt(1.2)*1e4 + Math.sqrt(0.2)*1e5); + +document.body.appendChild(CGA3.graph( + [ + 0xff00b0, v1, + 0x00ffb0, v2, + 0x00b0ff, v3, + 0x800040, w1, + 0x008040, w2, + 0x004080, w3, + 0xd0e0f0, s + ], + { + conformal: true, gl: true, grid: true + } +)); """ # === page construction === From 3b10c95d5ff92fb7f31475734c7a237f3d780780 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Tue, 25 Jun 2024 02:07:39 -0700 Subject: [PATCH 03/16] Clean up examples Declare JavaScript variables. Revise Julia comments to match new code. --- engine-proto/ganja-test/ganja-test.html | 16 ++++++++-------- engine-proto/ganja-test/ganja-test.jl | 24 ++++++++++-------------- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/engine-proto/ganja-test/ganja-test.html b/engine-proto/ganja-test/ganja-test.html index 26eba9d..f1aeea7 100644 --- a/engine-proto/ganja-test/ganja-test.html +++ b/engine-proto/ganja-test/ganja-test.html @@ -19,14 +19,14 @@ +

diff --git a/engine-proto/ganja-test/ganja-test.jl b/engine-proto/ganja-test/ganja-test.jl index 9ea621b..0808089 100644 --- a/engine-proto/ganja-test/ganja-test.jl +++ b/engine-proto/ganja-test/ganja-test.jl @@ -25,31 +25,51 @@ canvas { } """ +controls = """ +

+""" + graph_script = """ // in the default view, e4 + e5 is the point at infinity let CGA3 = Algebra(4, 1); -let v1 = CGA3.inline(() => 1e1 + 1e5)(); -let v2 = CGA3.inline(() => 1e2 + 1e5)(); -let v3 = CGA3.inline(() => 1e3 + 1e5)(); -let w1 = CGA3.inline(() => 1e1 - Math.sqrt(0.2)*1e4 + Math.sqrt(1.2)*1e5)(); -let w2 = CGA3.inline(() => 1e2 - Math.sqrt(0.2)*1e4 + Math.sqrt(1.2)*1e5)(); -let w3 = CGA3.inline(() => 1e3 - Math.sqrt(0.2)*1e4 + Math.sqrt(1.2)*1e5)(); +let v = [ + CGA3.inline(() => 1e1 + 1e5)(), + CGA3.inline(() => 1e2 + 1e5)(), + CGA3.inline(() => 1e3 + 1e5)() +]; +let w = [ + CGA3.inline(() => 1e1 - Math.sqrt(0.2)*1e4 + Math.sqrt(1.2)*1e5)(), + CGA3.inline(() => 1e2 - Math.sqrt(0.2)*1e4 + Math.sqrt(1.2)*1e5)(), + CGA3.inline(() => 1e3 - Math.sqrt(0.2)*1e4 + Math.sqrt(1.2)*1e5)() +]; let s = CGA3.inline(() => -Math.sqrt(1.2)*1e4 + Math.sqrt(0.2)*1e5); -document.body.appendChild(CGA3.graph( - [ - 0xff00b0, v1, - 0x00ffb0, v2, - 0x00b0ff, v3, - 0x800040, w1, - 0x008040, w2, - 0x004080, w3, - 0xd0e0f0, s - ], +// create scene function +let scene = () => [ + 0xff00b0, v[0], + 0x00ffb0, v[1], + 0x00b0ff, v[2], + 0x800040, w[0], + 0x008040, w[1], + 0x004080, w[2], + 0xd0e0f0, s +]; + +// initialize graph +let gr = CGA3.graph( + scene, { conformal: true, gl: true, grid: true } -)); +) +document.body.appendChild(gr); + +// connect flip button +function flipPoint() { + v[0] = CGA3.Dual(CGA3.Mul(s, v[0])); + requestAnimationFrame(gr.update.bind(gr, scene)); +} +document.querySelector('#flip-button').addEventListener('click', flipPoint); """ # === page construction === @@ -65,5 +85,5 @@ style!(win, stylesheet) loadjs!(win, "https://unpkg.com/ganja.js") # launch Ganja visualization -body!(win, "", async=false) +body!(win, controls, async=false) js(win, JSString(graph_script)) From 06a9dda5bb58079db955675a8ef7a4ab9fed4945 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Tue, 25 Jun 2024 13:40:40 -0700 Subject: [PATCH 05/16] Play with reflections Try configuration of five tangent spheres. --- engine-proto/ganja-test/ganja-test.html | 36 +++++++++++++------------ engine-proto/ganja-test/ganja-test.jl | 36 +++++++++++++------------ 2 files changed, 38 insertions(+), 34 deletions(-) diff --git a/engine-proto/ganja-test/ganja-test.html b/engine-proto/ganja-test/ganja-test.html index e1da682..e0d59bb 100644 --- a/engine-proto/ganja-test/ganja-test.html +++ b/engine-proto/ganja-test/ganja-test.html @@ -17,31 +17,25 @@ -

+

diff --git a/engine-proto/ganja-test/ganja-test.jl b/engine-proto/ganja-test/ganja-test.jl index 0808089..a5d7f6b 100644 --- a/engine-proto/ganja-test/ganja-test.jl +++ b/engine-proto/ganja-test/ganja-test.jl @@ -26,33 +26,27 @@ canvas { """ controls = """ -

+

""" graph_script = """ // in the default view, e4 + e5 is the point at infinity let CGA3 = Algebra(4, 1); let v = [ - CGA3.inline(() => 1e1 + 1e5)(), - CGA3.inline(() => 1e2 + 1e5)(), - CGA3.inline(() => 1e3 + 1e5)() + CGA3.inline(() => Math.sqrt(0.5)*( 1e1 + 1e2 + 1e3 + 1e5))(), + CGA3.inline(() => Math.sqrt(0.5)*( 1e1 - 1e2 - 1e3 + 1e5))(), + CGA3.inline(() => Math.sqrt(0.5)*(-1e1 + 1e2 - 1e3 + 1e5))(), + CGA3.inline(() => Math.sqrt(0.5)*(-1e1 - 1e2 + 1e3 + 1e5))(), + CGA3.inline(() => -Math.sqrt(3)*1e4 + Math.sqrt(2)*1e5)() ]; -let w = [ - CGA3.inline(() => 1e1 - Math.sqrt(0.2)*1e4 + Math.sqrt(1.2)*1e5)(), - CGA3.inline(() => 1e2 - Math.sqrt(0.2)*1e4 + Math.sqrt(1.2)*1e5)(), - CGA3.inline(() => 1e3 - Math.sqrt(0.2)*1e4 + Math.sqrt(1.2)*1e5)() -]; -let s = CGA3.inline(() => -Math.sqrt(1.2)*1e4 + Math.sqrt(0.2)*1e5); // create scene function let scene = () => [ 0xff00b0, v[0], 0x00ffb0, v[1], 0x00b0ff, v[2], - 0x800040, w[0], - 0x008040, w[1], - 0x004080, w[2], - 0xd0e0f0, s + 0x8040ff, v[3], + 0xc0c0c0, v[4] ]; // initialize graph @@ -65,11 +59,19 @@ let gr = CGA3.graph( document.body.appendChild(gr); // connect flip button -function flipPoint() { - v[0] = CGA3.Dual(CGA3.Mul(s, v[0])); +function flip() { + for (let n = 0; n < 4; ++n) { + // reflect + v[n] = CGA3.Mul(CGA3.Mul(v[4], v[n]), v[4]); + + // de-noise + for (let k = 6; k < v[n].length; ++k) { + v[n][k] = 0; + } + } requestAnimationFrame(gr.update.bind(gr, scene)); } -document.querySelector('#flip-button').addEventListener('click', flipPoint); +document.querySelector('#flip-button').addEventListener('click', flip); """ # === page construction === From b7b5b9386b4cf9ca4f4f213bdc7f4066a868eadf Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Tue, 25 Jun 2024 16:30:19 -0700 Subject: [PATCH 06/16] Load elements from Julia into Ganja.js --- engine-proto/ganja-test/ganja-test.jl | 159 ++++++++++++++------------ 1 file changed, 89 insertions(+), 70 deletions(-) diff --git a/engine-proto/ganja-test/ganja-test.jl b/engine-proto/ganja-test/ganja-test.jl index a5d7f6b..69b768f 100644 --- a/engine-proto/ganja-test/ganja-test.jl +++ b/engine-proto/ganja-test/ganja-test.jl @@ -1,17 +1,30 @@ using Blink -import Blink: JSString -# === styling utility === +# === utilities === -style!(w, stylesheet) = @js win begin - @var style = document.createElement("style"); - style.appendChild(document.createTextNode($stylesheet)); - document.head.appendChild(style); +append_to_head!(w, type, content) = @js win begin + @var element = document.createElement($type) + element.appendChild(document.createTextNode($content)) + document.head.appendChild(element) end -# === page source === +style!(w, stylesheet) = append_to_head!(w, "style", stylesheet) -stylesheet = """ +script!(w, code) = append_to_head!(w, "script", code) + +function add_element!(vec) + full_vec = [0; vec; fill(0, 26)] + @js win elements.push(@new CGA3($full_vec)) +end + +# === build page === + +# create window and open developer console +win = Window() +opentools(win) + +# set stylesheet +style!(win, """ body { background-color: #ffe0f0; } @@ -23,69 +36,75 @@ canvas { min-height: 600px; max-height: 600px; } -""" - -controls = """ -

-""" - -graph_script = """ -// in the default view, e4 + e5 is the point at infinity -let CGA3 = Algebra(4, 1); -let v = [ - CGA3.inline(() => Math.sqrt(0.5)*( 1e1 + 1e2 + 1e3 + 1e5))(), - CGA3.inline(() => Math.sqrt(0.5)*( 1e1 - 1e2 - 1e3 + 1e5))(), - CGA3.inline(() => Math.sqrt(0.5)*(-1e1 + 1e2 - 1e3 + 1e5))(), - CGA3.inline(() => Math.sqrt(0.5)*(-1e1 - 1e2 + 1e3 + 1e5))(), - CGA3.inline(() => -Math.sqrt(3)*1e4 + Math.sqrt(2)*1e5)() -]; - -// create scene function -let scene = () => [ - 0xff00b0, v[0], - 0x00ffb0, v[1], - 0x00b0ff, v[2], - 0x8040ff, v[3], - 0xc0c0c0, v[4] -]; - -// initialize graph -let gr = CGA3.graph( - scene, - { - conformal: true, gl: true, grid: true - } -) -document.body.appendChild(gr); - -// connect flip button -function flip() { - for (let n = 0; n < 4; ++n) { - // reflect - v[n] = CGA3.Mul(CGA3.Mul(v[4], v[n]), v[4]); - - // de-noise - for (let k = 6; k < v[n].length; ++k) { - v[n][k] = 0; - } - } - requestAnimationFrame(gr.update.bind(gr, scene)); -} -document.querySelector('#flip-button').addEventListener('click', flip); -""" - -# === page construction === - -# create window and open developer console -win = Window() -opentools(win) - -# set stylesheet -style!(win, stylesheet) +""") # load Ganja.js loadjs!(win, "https://unpkg.com/ganja.js") -# launch Ganja visualization -body!(win, controls, async=false) -js(win, JSString(graph_script)) +# create global functions and variables +script!(win, """ + // create algebra + var CGA3 = Algebra(4, 1); + + // in the default view, e4 + e5 is the point at infinity + var elements = []; + + // create scene function + let scene = () => [ + 0xff00b0, elements[0], + 0x00ffb0, elements[1], + 0x00b0ff, elements[2], + 0x8040ff, elements[3], + 0xc0c0c0, elements[4] + ]; + + // declare visualization handle + var graph; + + function flip() { + for (let n = 0; n < 4; ++n) { + // reflect + elements[n] = CGA3.Mul(CGA3.Mul(elements[4], elements[n]), elements[4]); + + // de-noise + for (let k = 6; k < elements[n].length; ++k) { + elements[n][k] = 0; + } + } + requestAnimationFrame(graph.update.bind(graph, scene)); + } +""") + +# set up controls +body!(win, """ +

+""", async=false) + +# === set up visualization === + +# list elements. in the default view, e4 + e5 is the point at infinity +elements = sqrt(0.5) * BigFloat[ + 1 1 -1 -1 0; + 1 -1 1 -1 0; + 1 -1 -1 1 0; + 0 0 0 0 -sqrt(6); + 1 1 1 1 2 +] + +# load elements +for vec in eachcol(elements) + add_element!(vec) +end + +# initialize visualization +@js win begin + graph = CGA3.graph( + scene, + Dict( + "conformal" => true, + "gl" => true, + "grid" => true + ) + ) + document.body.appendChild(graph); +end \ No newline at end of file From 182b5bb9f69f7e7bbbc524da85eb43fa62549510 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Tue, 25 Jun 2024 17:57:16 -0700 Subject: [PATCH 07/16] Generate palette automatically --- engine-proto/ganja-test/ganja-test.html | 44 ++++++++++++-------- engine-proto/ganja-test/ganja-test.jl | 53 ++++++++++++++++--------- 2 files changed, 63 insertions(+), 34 deletions(-) diff --git a/engine-proto/ganja-test/ganja-test.html b/engine-proto/ganja-test/ganja-test.html index e0d59bb..d6efc90 100644 --- a/engine-proto/ganja-test/ganja-test.html +++ b/engine-proto/ganja-test/ganja-test.html @@ -21,43 +21,55 @@ diff --git a/engine-proto/ganja-test/ganja-test.jl b/engine-proto/ganja-test/ganja-test.jl index 69b768f..b944eae 100644 --- a/engine-proto/ganja-test/ganja-test.jl +++ b/engine-proto/ganja-test/ganja-test.jl @@ -1,4 +1,5 @@ using Blink +using Colors # === utilities === @@ -13,8 +14,22 @@ style!(w, stylesheet) = append_to_head!(w, "style", stylesheet) script!(w, code) = append_to_head!(w, "script", code) function add_element!(vec) + # add element full_vec = [0; vec; fill(0, 26)] - @js win elements.push(@new CGA3($full_vec)) + n = @js win elements.push(@new CGA3($full_vec)) + + # generate palette. this is Gadfly's `default_discrete_colors` palette, + # available under the MIT license + palette = distinguishable_colors( + n, + [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 win palette = $palette_packed end # === build page === @@ -45,26 +60,28 @@ loadjs!(win, "https://unpkg.com/ganja.js") script!(win, """ // create algebra var CGA3 = Algebra(4, 1); - - // in the default view, e4 + e5 is the point at infinity + + // initialize element list and palette var elements = []; - - // create scene function - let scene = () => [ - 0xff00b0, elements[0], - 0x00ffb0, elements[1], - 0x00b0ff, elements[2], - 0x8040ff, elements[3], - 0xc0c0c0, elements[4] - ]; - + var palette = []; + // declare visualization handle var graph; - + + // create scene function + function scene() { + commands = []; + for (let n = 0; n < elements.length; ++n) { + commands.push(palette[n], elements[n]); + } + return commands; + } + function flip() { - for (let n = 0; n < 4; ++n) { + let last = elements.length - 1; + for (let n = 0; n < last; ++n) { // reflect - elements[n] = CGA3.Mul(CGA3.Mul(elements[4], elements[n]), elements[4]); + elements[n] = CGA3.Mul(CGA3.Mul(elements[last], elements[n]), elements[last]); // de-noise for (let k = 6; k < elements[n].length; ++k) { @@ -78,7 +95,7 @@ script!(win, """ # set up controls body!(win, """

-""", async=false) +""", async = false) # === set up visualization === @@ -106,5 +123,5 @@ end "grid" => true ) ) - document.body.appendChild(graph); + document.body.appendChild(graph) end \ No newline at end of file From 665cb30ce0bf4bc442f5fcfbca24b51d73c1d116 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Tue, 25 Jun 2024 23:31:00 -0700 Subject: [PATCH 08/16] Correct indentation of CSS --- engine-proto/ganja-test/ganja-test.jl | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/engine-proto/ganja-test/ganja-test.jl b/engine-proto/ganja-test/ganja-test.jl index b944eae..19578bd 100644 --- a/engine-proto/ganja-test/ganja-test.jl +++ b/engine-proto/ganja-test/ganja-test.jl @@ -40,17 +40,17 @@ opentools(win) # set stylesheet style!(win, """ -body { - background-color: #ffe0f0; -} + body { + background-color: #ffe0f0; + } -/* needed to keep Ganja canvas from blowing up */ -canvas { - min-width: 600px; - max-width: 600px; - min-height: 600px; - max-height: 600px; -} + /* needed to keep Ganja canvas from blowing up */ + canvas { + min-width: 600px; + max-width: 600px; + min-height: 600px; + max-height: 600px; + } """) # load Ganja.js From a3b1f4920c24c401c240308582009f28580bc9fe Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Wed, 26 Jun 2024 00:41:21 -0700 Subject: [PATCH 09/16] Build construction viewer module --- engine-proto/ConstructionViewer.jl | 123 ++++++++++++++++++++++++++ engine-proto/ganja-test/ganja-test.jl | 2 +- 2 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 engine-proto/ConstructionViewer.jl diff --git a/engine-proto/ConstructionViewer.jl b/engine-proto/ConstructionViewer.jl new file mode 100644 index 0000000..efa7293 --- /dev/null +++ b/engine-proto/ConstructionViewer.jl @@ -0,0 +1,123 @@ +module Viewer + +using Blink +using Colors + +export ConstructionViewer, display! + +# === 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() + opentools(win) + + # set stylesheet + style!(win, """ + /* needed to keep Ganja canvas from blowing up */ + canvas { + min-width: 600px; + max-width: 600px; + min-height: 600px; + max-height: 600px; + } + """) + + # load Ganja.js + loadjs!(win, "https://unpkg.com/ganja.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 visualization handle + var graph; + + // create scene function + function scene() { + commands = []; + for (let n = 0; n < elements.length; ++n) { + commands.push(palette[n], elements[n]); + } + return commands; + } + """) + + # create view + @js win begin + graph = CGA3.graph( + scene, + Dict( + "conformal" => true, + "gl" => true, + "grid" => true + ) + ) + document.body.replaceChildren(graph) + end + + new(win) + end +end + +function display!(viewer::ConstructionViewer, elements::Matrix) + # load elements + elements_full = [ + [0; elt; fill(0, 26)] + for elt in eachcol(elements) + ] + @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 + + # update view + @js viewer.win requestAnimationFrame(graph.update.bind(graph, scene)); +end + +end + +# ~~~ sandbox setup ~~~ + +# in the default view, e4 + e5 is the point at infinity +elements = sqrt(0.5) * BigFloat[ + 1 1 -1 -1 0; + 1 -1 1 -1 0; + 1 -1 -1 1 0; + 0 0 0 0 -sqrt(6); + 1 1 1 1 2 +] + +# show construction +viewer = Viewer.ConstructionViewer() +Viewer.display!(viewer, elements) \ No newline at end of file diff --git a/engine-proto/ganja-test/ganja-test.jl b/engine-proto/ganja-test/ganja-test.jl index 19578bd..6c55061 100644 --- a/engine-proto/ganja-test/ganja-test.jl +++ b/engine-proto/ganja-test/ganja-test.jl @@ -3,7 +3,7 @@ using Colors # === utilities === -append_to_head!(w, type, content) = @js win begin +append_to_head!(w, type, content) = @js w begin @var element = document.createElement($type) element.appendChild(document.createTextNode($content)) document.head.appendChild(element) From 4a28a47520f1bd6a6291052a1db39e5f9a04e647 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Wed, 26 Jun 2024 01:06:27 -0700 Subject: [PATCH 10/16] Update namespace of AbstractAlgebra.Rationals --- engine-proto/gram-test/descent-test.jl | 4 ++-- engine-proto/gram-test/gram-test.jl | 2 +- engine-proto/gram-test/overlap-test.jl | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/engine-proto/gram-test/descent-test.jl b/engine-proto/gram-test/descent-test.jl index 9b6fb7a..ecf0b3e 100644 --- a/engine-proto/gram-test/descent-test.jl +++ b/engine-proto/gram-test/descent-test.jl @@ -108,11 +108,11 @@ println("\nLoss: ", loss, "\n") # === algebraic check === -R, gens = polynomial_ring(Generic.Rationals{BigInt}(), ["x", "t₁", "t₂", "t₃"]) +R, gens = polynomial_ring(AbstractAlgebra.Rationals{BigInt}(), ["x", "t₁", "t₂", "t₃"]) x = gens[1] t = gens[2:4] -S, u = polynomial_ring(Generic.Rationals{BigInt}(), "u") +S, u = polynomial_ring(AbstractAlgebra.Rationals{BigInt}(), "u") M = matrix_space(R, 7, 7) gram_symb = M(R[ diff --git a/engine-proto/gram-test/gram-test.jl b/engine-proto/gram-test/gram-test.jl index 4b2f859..0e88ff4 100644 --- a/engine-proto/gram-test/gram-test.jl +++ b/engine-proto/gram-test/gram-test.jl @@ -11,7 +11,7 @@ function printbad(msg) println(" ", msg) end -F, gens = rational_function_field(Generic.Rationals{BigInt}(), ["a₁", "a₂", "b₁", "b₂", "c₁", "c₂"]) +F, gens = rational_function_field(AbstractAlgebra.Rationals{BigInt}(), ["a₁", "a₂", "b₁", "b₂", "c₁", "c₂"]) a = gens[1:2] b = gens[3:4] c = gens[5:6] diff --git a/engine-proto/gram-test/overlap-test.jl b/engine-proto/gram-test/overlap-test.jl index beeaeca..e75531a 100644 --- a/engine-proto/gram-test/overlap-test.jl +++ b/engine-proto/gram-test/overlap-test.jl @@ -11,7 +11,7 @@ function printbad(msg) println(" ", msg) end -F, gens = rational_function_field(Generic.Rationals{BigInt}(), ["x", "t₁", "t₂", "t₃"]) +F, gens = rational_function_field(AbstractAlgebra.Rationals{BigInt}(), ["x", "t₁", "t₂", "t₃"]) x = gens[1] t = gens[2:4] From 2b6c4f4720212da7449e0b03f1b6566cebef6028 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Wed, 26 Jun 2024 11:28:47 -0700 Subject: [PATCH 11/16] Avoid naming conflict with identity transformation --- engine-proto/gram-test/descent-test.jl | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/engine-proto/gram-test/descent-test.jl b/engine-proto/gram-test/descent-test.jl index ecf0b3e..d895764 100644 --- a/engine-proto/gram-test/descent-test.jl +++ b/engine-proto/gram-test/descent-test.jl @@ -10,10 +10,10 @@ using PolynomialRoots # the difference between the matrices `target` and `attempt`, projected onto the # subspace of matrices whose entries vanish at each empty index of `target` function proj_diff(target, attempt) - I, J, values = findnz(target) + J, K, values = findnz(target) result = zeros(size(target)...) - for (i, j, val) in zip(I, J, values) - result[i, j] = val - attempt[i, j] + for (j, k, val) in zip(J, K, values) + result[j, k] = val - attempt[j, k] end result end @@ -26,19 +26,19 @@ Q = diagm([-1, 1, 1, 1, 1]) # initialize the partial gram matrix for an arrangement of seven spheres in # which spheres 1 through 5 are mutually tangent, and spheres 3 through 7 are # also mutually tangent -I = Int64[] J = Int64[] +K = Int64[] values = BigFloat[] -for i in 1:7 - for j in 1:7 - if (i <= 5 && j <= 5) || (i >= 3 && j >= 3) - push!(I, i) +for j in 1:7 + for k in 1:7 + if (j <= 5 && k <= 5) || (j >= 3 && k >= 3) push!(J, j) - push!(values, i == j ? 1 : -1) + push!(K, k) + push!(values, j == k ? 1 : -1) end end end -gram = sparse(I, J, values) +gram = sparse(J, K, values) # set the independent variable # From c933e07312c0dc3b6c8523eeb811d14b92e7a6bc Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Wed, 26 Jun 2024 11:39:34 -0700 Subject: [PATCH 12/16] Switch to Ganja.js basis ordering --- engine-proto/gram-test/descent-test.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/engine-proto/gram-test/descent-test.jl b/engine-proto/gram-test/descent-test.jl index d895764..2f20143 100644 --- a/engine-proto/gram-test/descent-test.jl +++ b/engine-proto/gram-test/descent-test.jl @@ -21,7 +21,7 @@ end # === example === # the Lorentz form -Q = diagm([-1, 1, 1, 1, 1]) +Q = diagm([1, 1, 1, 1, -1]) # initialize the partial gram matrix for an arrangement of seven spheres in # which spheres 1 through 5 are mutually tangent, and spheres 3 through 7 are @@ -54,11 +54,11 @@ gram[1, 6] = gram[6, 1] # in this initial guess, the mutual tangency condition is satisfied for spheres # 1 through 5 guess = sqrt(0.5) * BigFloat[ - 1 1 1 1 2 0.2 0.1; - 0 0 0 0 -sqrt(6) 0.3 -0.2; 1 1 -1 -1 0 -0.1 0.3; 1 -1 1 -1 0 -0.5 0.4; - 1 -1 -1 1 0 0.1 -0.2 + 1 -1 -1 1 0 0.1 -0.2; + 0 0 0 0 -sqrt(6) 0.3 -0.2; + 1 1 1 1 2 0.2 0.1; ] # search parameters From 7aaf134a3672ac712744ce475b7ea5810a4655c3 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Wed, 26 Jun 2024 13:15:54 -0700 Subject: [PATCH 13/16] Size the viewer window automatically --- engine-proto/ConstructionViewer.jl | 40 +++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/engine-proto/ConstructionViewer.jl b/engine-proto/ConstructionViewer.jl index efa7293..563857a 100644 --- a/engine-proto/ConstructionViewer.jl +++ b/engine-proto/ConstructionViewer.jl @@ -3,7 +3,7 @@ module Viewer using Blink using Colors -export ConstructionViewer, display! +export ConstructionViewer, display!, opentools!, closetools! # === Blink utilities === @@ -24,17 +24,23 @@ mutable struct ConstructionViewer function ConstructionViewer() # create window and open developer console - win = Window() - opentools(win) + win = Window(Blink.Dict(:width => 620, :height => 620)) # set stylesheet style!(win, """ - /* needed to keep Ganja canvas from blowing up */ + body { + background-color: #c8c0d0; + } + + /* maximum dimensions are needed to keep Ganja canvas from blowing up */ canvas { min-width: 600px; max-width: 600px; min-height: 600px; max-height: 600px; + margin-top: 10px; + margin-left: 10px; + border-radius: 10px; } """) @@ -50,8 +56,9 @@ mutable struct ConstructionViewer var elements = []; var palette = []; - // declare visualization handle + // declare handles for the visualization and its options var graph; + var graphOpt; // create scene function function scene() { @@ -65,14 +72,13 @@ mutable struct ConstructionViewer # create view @js win begin - graph = CGA3.graph( - scene, - Dict( - "conformal" => true, - "gl" => true, - "grid" => true - ) + graphOpt = Dict( + :conformal => true, + :gl => true, + :grid => true, + :devicePixelRatio => window.devicePixelRatio ) + graph = CGA3.graph(scene, graphOpt) document.body.replaceChildren(graph) end @@ -105,6 +111,16 @@ function display!(viewer::ConstructionViewer, elements::Matrix) @js viewer.win requestAnimationFrame(graph.update.bind(graph, scene)); end +function opentools!(viewer::ConstructionViewer) + size(viewer.win, 1240, 620) + opentools(viewer.win) +end + +function closetools!(viewer::ConstructionViewer) + closetools(viewer.win) + size(viewer.win, 620, 620) +end + end # ~~~ sandbox setup ~~~ From 3eb4fc6c91a58bc7c6a43a311847d4e89b1d857a Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Wed, 26 Jun 2024 15:24:31 -0700 Subject: [PATCH 14/16] Add element visibility controls --- engine-proto/ConstructionViewer.jl | 110 +++++++++++++++++++++++------ 1 file changed, 90 insertions(+), 20 deletions(-) diff --git a/engine-proto/ConstructionViewer.jl b/engine-proto/ConstructionViewer.jl index 563857a..2c51ce6 100644 --- a/engine-proto/ConstructionViewer.jl +++ b/engine-proto/ConstructionViewer.jl @@ -2,6 +2,7 @@ module Viewer using Blink using Colors +using Printf export ConstructionViewer, display!, opentools!, closetools! @@ -24,23 +25,41 @@ mutable struct ConstructionViewer function ConstructionViewer() # create window and open developer console - win = Window(Blink.Dict(:width => 620, :height => 620)) + win = Window(Blink.Dict(:width => 620, :height => 830)) # set stylesheet style!(win, """ body { - background-color: #c8c0d0; + background-color: #ccc; } - /* maximum dimensions are needed to keep Ganja canvas from blowing up */ - canvas { - min-width: 600px; - max-width: 600px; - min-height: 600px; - max-height: 600px; + /* 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; + border-radius: 10px; + background-color: #f0f0f0; + } + + #control-panel > div { + margin-top: 5px; + padding: 2px; + border-radius: 5px; + font-family: monospace; } """) @@ -56,30 +75,46 @@ mutable struct ConstructionViewer var elements = []; var palette = []; - // declare handles for the visualization and its options - var graph; - var graphOpt; + // declare handles for the view and its options + var view; + var viewOpt; + + // declare handles for the controls + var controlPanel; + var visControls; // create scene function function scene() { commands = []; for (let n = 0; n < elements.length; ++n) { - commands.push(palette[n], elements[n]); + if (visControls[n].checked) { + commands.push(palette[n], elements[n]); + } } return commands; } + + function updateView() { + requestAnimationFrame(view.update.bind(view, scene)); + } """) - # create view @js win begin - graphOpt = Dict( + # create view + viewOpt = Dict( :conformal => true, :gl => true, - :grid => true, :devicePixelRatio => window.devicePixelRatio ) - graph = CGA3.graph(scene, graphOpt) - document.body.replaceChildren(graph) + 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) @@ -107,18 +142,53 @@ function display!(viewer::ConstructionViewer, elements::Matrix) palette_packed = [RGB24(c).color for c in palette] @js viewer.win palette = $palette_packed + # generate visibility controls + @js viewer.win begin + controlPanel.replaceChildren() + visControls = [] + end + for n in 1:size(elements, 2) + index_str = string(n) + vec_str = join(map(t -> @sprintf("%.3f", t), elements[:, n]), ", ") + style_str = "background-color: #$(hex(palette[n]));" + println(style_str) + @js viewer.win begin + # create container + @var container = document.createElement(:div) + container.setAttribute(:style, $style_str) + + # create checkbox + @var checkbox = document.createElement(:input) + checkbox.setAttribute(:type, "checkbox") + checkbox.setAttribute(:id, $index_str) + checkbox.setAttribute(:checked, "true") + checkbox.addEventListener(:input, updateView) + visControls.push(checkbox) + container.appendChild(checkbox) + + # create label + @var label = document.createElement(:label); + label.setAttribute(:for, $index_str) + label.appendChild(document.createTextNode($vec_str)) + container.appendChild(label) + + # add the control to the control panel + controlPanel.appendChild(container) + end + end + # update view - @js viewer.win requestAnimationFrame(graph.update.bind(graph, scene)); + @js viewer.win updateView() end function opentools!(viewer::ConstructionViewer) - size(viewer.win, 1240, 620) + size(viewer.win, 1240, 830) opentools(viewer.win) end function closetools!(viewer::ConstructionViewer) closetools(viewer.win) - size(viewer.win, 620, 620) + size(viewer.win, 620, 830) end end From 5ea32ac53cfac68e1267d8863338c32695214c05 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Wed, 26 Jun 2024 15:51:57 -0700 Subject: [PATCH 15/16] Streamline visibility controls --- engine-proto/ConstructionViewer.jl | 55 +++++++++++++----------------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/engine-proto/ConstructionViewer.jl b/engine-proto/ConstructionViewer.jl index 2c51ce6..2486cc9 100644 --- a/engine-proto/ConstructionViewer.jl +++ b/engine-proto/ConstructionViewer.jl @@ -57,8 +57,9 @@ mutable struct ConstructionViewer #control-panel > div { margin-top: 5px; - padding: 2px; + padding: 4px; border-radius: 5px; + border: solid; font-family: monospace; } """) @@ -81,13 +82,13 @@ mutable struct ConstructionViewer // declare handles for the controls var controlPanel; - var visControls; + var visToggles; // create scene function function scene() { commands = []; for (let n = 0; n < elements.length; ++n) { - if (visControls[n].checked) { + if (visToggles[n].checked) { commands.push(palette[n], elements[n]); } } @@ -142,38 +143,30 @@ function display!(viewer::ConstructionViewer, elements::Matrix) palette_packed = [RGB24(c).color for c in palette] @js viewer.win palette = $palette_packed - # generate visibility controls + # create visibility toggles @js viewer.win begin controlPanel.replaceChildren() - visControls = [] + visToggles = [] end - for n in 1:size(elements, 2) - index_str = string(n) - vec_str = join(map(t -> @sprintf("%.3f", t), elements[:, n]), ", ") - style_str = "background-color: #$(hex(palette[n]));" - println(style_str) + 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 - # create container - @var container = document.createElement(:div) - container.setAttribute(:style, $style_str) - - # create checkbox - @var checkbox = document.createElement(:input) - checkbox.setAttribute(:type, "checkbox") - checkbox.setAttribute(:id, $index_str) - checkbox.setAttribute(:checked, "true") - checkbox.addEventListener(:input, updateView) - visControls.push(checkbox) - container.appendChild(checkbox) - - # create label - @var label = document.createElement(:label); - label.setAttribute(:for, $index_str) - label.appendChild(document.createTextNode($vec_str)) - container.appendChild(label) - - # add the control to the control panel - controlPanel.appendChild(container) + @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 From 05a824834d84a48018556678f412ef7a73318abe Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Wed, 26 Jun 2024 15:56:51 -0700 Subject: [PATCH 16/16] Let visibility controls scroll --- engine-proto/ConstructionViewer.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/engine-proto/ConstructionViewer.jl b/engine-proto/ConstructionViewer.jl index 2486cc9..7cb0450 100644 --- a/engine-proto/ConstructionViewer.jl +++ b/engine-proto/ConstructionViewer.jl @@ -51,6 +51,7 @@ mutable struct ConstructionViewer padding: 5px 10px 5px 10px; margin-top: 10px; margin-left: 10px; + overflow-y: scroll; border-radius: 10px; background-color: #f0f0f0; }