Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ping pong buffers to svg, add ?v=both #11

Merged
merged 41 commits into from
Dec 4, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
eeb15d4
RED team BLUE team fight fork of does-it-glider
duppypro Nov 8, 2023
349387e
update .gitignore for Mac OS
duppypro Nov 8, 2023
6828165
minor improvement to fight init
duppypro Nov 9, 2023
e8f06c7
firebase function upgrade
duppypro Nov 14, 2023
1ae0058
Update file paths, names and remove cruft
duppypro Nov 17, 2023
3dc49a3
Update LICENSE
duppypro Nov 17, 2023
e3ed2de
Refactor README.md and update project description
duppypro Nov 17, 2023
7c222b4
one more thing... to add to the README
duppypro Nov 17, 2023
b43870a
Update header comment blocks and misc polish
duppypro Nov 17, 2023
5ecb884
WIP moving all possible to .css
duppypro Nov 19, 2023
6a50c0e
WIP css migration, test requestAnimationFrame
duppypro Nov 20, 2023
f28f082
WIP more css experiments
duppypro Nov 21, 2023
7c55322
WebGL FTW!!! WebGL Hello World works. very ugly.
duppypro Nov 22, 2023
f1d5e12
WebGL ANIMATION FTW! (still ugly AF)
duppypro Nov 22, 2023
e6e422d
fixed to set u_resolution properly
duppypro Nov 22, 2023
057c167
divs sized properly for left shader and right svg
duppypro Nov 22, 2023
b721624
WebGL pan and zoom almost but not working
duppypro Nov 23, 2023
a02f631
WIP zoom scale and translate not quite right yet
duppypro Nov 23, 2023
dbbf5ff
Merge branch 'main' into rewrite-01
duppypro Nov 25, 2023
d36f2fc
📦 NEW: made vertex pan and zoom work
duppypro Nov 26, 2023
bf08825
📦 NEW: Zoom and pan/drag working in shaders
duppypro Nov 26, 2023
0c3de7e
WIP random polish
duppypro Nov 27, 2023
04c4c59
WIP forgot stage
duppypro Nov 27, 2023
fa2d064
fixing .gitignore node_modules
duppypro Nov 27, 2023
3d7fe1f
add grid fragment shader and smarter v_gridCoord
duppypro Nov 27, 2023
94427b6
👌 IMPROVE: send grid location as 2nd attribute
duppypro Nov 27, 2023
c4947d2
use grid_width instead of hard code 2048
duppypro Nov 27, 2023
74e8813
👌 IMPROVE: simpler setting shader precision
duppypro Nov 27, 2023
8c31471
👌 IMPROVE: Update d3js import and fix TODO comments
duppypro Nov 27, 2023
c3243a2
add settings.js, top/bottom, reset to glider start
duppypro Nov 28, 2023
c7007c1
👌 IMPROVE: add WebGL version error renders to div
duppypro Nov 28, 2023
51b450e
👍 improved settings.js, SVG works, WebGL brokes
duppypro Nov 29, 2023
aa82b54
😠 SVG till works, WebGL brokes, almost there 🤞
duppypro Nov 29, 2023
0468a74
🐛 FIX: Fixed pan and zoom on WebGL 🎉
duppypro Nov 29, 2023
8da032e
👌 IMPROVE: Both work 🎉, removed unecessary
duppypro Nov 29, 2023
609390b
👌 IMPROVE: add query params
duppypro Nov 30, 2023
9ca4e46
WIP still looking for memory leak
duppypro Dec 1, 2023
f282625
⚠️non-working check point, re-writing play.js
duppypro Dec 2, 2023
3d61f2d
working check point with mediocre performance
duppypro Dec 3, 2023
6851a59
⚠️ working but BUG#9 draw() is called too often
duppypro Dec 3, 2023
09ec2b7
👌 IMPROVE: ⏩⏩ Significant performance improvement
duppypro Dec 4, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
⚠️ working but BUG#9 draw() is called too often
just in case a pan and zoom changes
  • Loading branch information
duppypro committed Dec 3, 2023
commit 6851a5924ee6290667dece4b71fbc2a8239491bc
22 changes: 20 additions & 2 deletions public/css/does-it-glider.css
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,19 @@
text-align: left;
}

@keyframes rainbow
@keyframes rainbow-color
{
0% {color: red;}
14% {color: orange;}
28% {color: yellow;}
42% {color: lime;}
57% {color: blue;}
71% {color: indigo;}
85% {color: violet;}
100% {color: red;}
}

@keyframes rainbow-fill
{
0% {fill: red;}
14% {fill: orange;}
Expand Down Expand Up @@ -126,7 +138,13 @@
stroke-width: 1.5;
}

/* .cell.⬜, */
.cell.glider
{
animation: rainbow 333ms linear infinite;
animation: rainbow-fill 333ms linear infinite;
}

.title
{
animation: rainbow-color 16ms linear infinite;
}
32 changes: 9 additions & 23 deletions public/src/does-it-glider/draw.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,28 @@
let draw_count = 0, sum_total = 0, sum_remove = 0
let min = Infinity, max = 0
let total = 0, time = 0
const draw_times = Array(200).fill(0)
const len = draw_times.length

const COLOR_TO_CLASS = {
'⬛': false,
'o': false,
'.': false,
'⬜': '⬜',
'⬜':'⬜',
'b': '⬜',
'X': '⬜',
'🟥': '🟥',
'🟥':'🟥',
'R': '🟥',
'🟦': '🟦',
'🟦':'🟦',
'B': '🟦',
}
// modify the DOM from a 2D array of Conway's Game of Life (gol_state)
export const draw = (g, state, cell_px) => { // HACK - do better than passing cell_px down, add some class
let startTime = performance.now()
const startTime = performance.now()

// render/draw each live cell in state as a white rect in the svg
// clear all of the old rects first
let all = g.selectAll('rect.cell')
const all = g.selectAll('rect.cell')
all.remove()
let removeTime = performance.now() - startTime
const removeTime = performance.now() - startTime
// loop over 2D array state
for(let y = 0; y < state.length; y++) {
for(let x = 0; x < state[0].length; x++) {
Expand All @@ -48,21 +46,9 @@ export const draw = (g, state, cell_px) => { // HACK - do better than passing ce
}
}

draw_times[draw_count] = {
total: performance.now() - startTime,
remove: removeTime,
}
total += time = draw_times[draw_count].total
sum_total += time
sum_remove += draw_times[draw_count].remove
if (time < min) min = time
if (time > max) max = time
draw_count++
if (draw_count >= len) {
console.log(`total: ${(sum_total / len).toFixed(2)}ms, remove: ${(sum_remove / len).toFixed(2) }ms, min: ${min.toFixed(2)}ms, max: ${max.toFixed(2)}ms`)
draw_count = 0
sum_total = sum_remove = 0
min = Infinity, max = 0
const draw_time = performance.now() - startTime
if (draw_time > 33.33) {
console.log(`draw time: ${draw_time.toFixed(3)}ms`)
}
}

Expand Down
102 changes: 66 additions & 36 deletions public/src/does-it-glider/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ if (use_gl) {
webgl_div = app.mynew('div.bottom')
.style('background', '#e600ffff') // should never see this
}

app.selectAll('.top,.bottom') // styles in common for both divs
.style('overflow', 'hidden') // tested, this is needed to avoid scroll bars
.style('position', 'relative')
Expand All @@ -65,15 +65,15 @@ app.selectAll('.top,.bottom') // styles in common for both divs
let touch_target = app.append('span')
.classed('touch-target', true)

const _title = 'Does it Glider?'
const _sub_title = 'Tap here to paste Wordle score.'
const _title = 'Does it Glider?'
const _sub_title = 'Tap here to paste Wordle score.'
// max width reference: '##################################'
// abbove is max width on smallest mobile (iPhone SE)

touch_target.append('div')
.attr('class', 'title')
.html(_title)

touch_target.append('div')
.attr('class', 'title sub-title')
.html(_sub_title)
Expand Down Expand Up @@ -155,24 +155,25 @@ add_seed(seed, grid_ping) // grid_ping is grid_pong 1 tick earlier
// we will ping pong between them

let tick = 0
let num_ticks = Math.round((settings.BEAT/4) / (1000 / 60)) // BEATmsec / (1000msec/60frame) -> num_ticks has units of frames
let num_ticks = Math.round((settings.BEAT / 1) / (1000 / 60)) // BEATmsec / (1000msec/60frame) -> num_ticks has units of frames
let pause_for_new = Math.round(1.333 * 60) // to pause for N seconds, set N sec * 60 frames/sec then round() so that mod (%) works
let ping_pong = true
const event_loop = () => {
if (pause_for_new == 0) { // apply the rules to the state
if (tick % num_ticks == 0) { // only apply rules every num_ticks frames
ping_pong = !ping_pong
if (ping_pong) { // TODO why wasn't [grid_ping, grid_pong] = [grid_pong, grid_ping] working?
apply_rules(grid_ping, grid_pong)
apply_rules(grid_pong, grid_ping) // ping_pong is true, grid_ping gets the new state
} else {
apply_rules(grid_pong, grid_ping)
apply_rules(grid_ping, grid_pong)
}
ping_pong = !ping_pong
}
} else {
pause_for_new-- // HACK, there must be a better way to pause for new and also draw only n frames
}
if (ping_pong) {
draw(grid_sel, grid_ping, settings.CELL_PX) // redraw every frame for smooth pan and zoom
// BUG #9 recreating rects every frame is doesn't allow animations to run *AND* it uses too much CPU
} else {
draw(grid_sel, grid_pong, settings.CELL_PX) // redraw every frame for smooth pan and zoom
}
Expand All @@ -185,15 +186,17 @@ let life_seed = []

const parse_clipboard = (pasted_clipboard) => {
let pasted_lines = []
pasted_lines = pasted_clipboard.split(/\r\n|\r|\n/ug)
// for importing RLE patterns
pasted_lines = pasted_clipboard.replace(/\$|!/ug, '\n') // '$' used as end of line and '!' used as end of seed in RLE format
pasted_lines = pasted_lines.split(/\r\n|\r|\n/ug)

// filter pasted_lines for only lines that are length 5
// and contain only '⬜', '🟨', '🟩', or '⬛'
// and contain only '⬜', '🟨', '🟩', or '⬛' (or their aliases)
let wordle_guesses = []
wordle_guesses = pasted_lines
.filter(line => line.match(/^(⬜|🟨|🟩|⬛|🟦|🟧|o|b|R|B|X|\.)+$/ug))
.filter(line => line.match(/^(⬜|🟨|🟩|⬛|🟦|🟧|o|b|R|B|X|\.){5,5}$/ug))
// this is only the lines with exactly 5 wordle squares
console.log(`filtered wordle_guesses: ${wordle_guesses}`)
console.log(`filtered wordle_guesses:\n${wordle_guesses.join('\n')}`)

// convert all '🟨'|'🟩' in wordle_guesses to '⬜' and '⬜'|'⬛' to '⬛'
life_seed = []
Expand All @@ -202,22 +205,24 @@ const parse_clipboard = (pasted_clipboard) => {
guess
// need an intermediate character to avoid double replacement
// try red team 🟥 blue team 🟦 fight idea
.replace(/🟨|🟧/g, 'b') // hits in wrong location are red team
.replace(/🟩|🟦/g, 'b') // hits in correct location are blue team
.replace(/⬜|⬛/g, '⬛')
.replace(/X/g, 'b')
.replace(/\./g, '⬛')
.replace(/⬜|⬛/ug, '⬛')
.replace(/\./ug, '⬛')
.replace(/o/ug, '⬛')
.replace(/🟨|🟧/ug, '⬜') // hits in wrong location are red team
.replace(/🟩|🟦/ug, '⬜') // hits in correct location are blue team
.replace(/X/ug, '⬜')
.replace(/b/ug, '⬜')
)
)
console.log(`life_seed:\n ${life_seed}`)
console.log(`life_seed:\n${life_seed.join('\n')}`)

let beat_pasted = settings.BEAT
let beat_wordle_guesses = settings.BEAT
let beat_life_seed = settings.BEAT
// draw/render pasted_lines in the .paste-line divs
const draw_pasted_lines = () => {
console.log(`draw_pasted_lines()`)
const transition = app
const last_line = pasted_lines.length - 1
app
.selectAll('.paste-line')
.data(pasted_lines)
.join(
Expand All @@ -230,54 +235,79 @@ const parse_clipboard = (pasted_clipboard) => {
.html(line => line || '&nbsp;')
.transition().duration(beat_pasted)
.remove()
.on('end', draw_wordle_guesses())
.on('end',
(_d, i) => { if (i == last_line) draw_wordle_guesses() }
)
}

const draw_wordle_guesses = () => {
const transiton = app
const last_line = wordle_guesses.length - 1
app
.selectAll('.paste-line')
.data(wordle_guesses)
// d3.data() stores the array life_seed on the parent DOM element
// then d3.join() compares new data to previous data
// and calls enter, update, or exit on each element of data array
// as appropriate for diff of new data comapred to previous data
.join(
enter => enter.mynew('div').classed('paste-line', true),
enter => enter.append('div').classed('paste-line', true),
update => update,
exit => exit
.remove()
) //join returns enter and update merged
.html(d => d)
.transition().duration(beat_wordle_guesses)
.remove()
.on('end', () => {
console.log(`draw_wordle_guesses() end event`)
draw_life_seed()
})
.on('end',
(_d, i) => {
if (i == last_line) draw_life_seed()
}
)
}

const draw_life_seed = () => {
console.log(`draw_life_seed()`)
const transition = app
const last_line = life_seed.length - 1
console.log(`draw_life_seed: last_line = ${last_line}`)
app
.selectAll('.paste-line')
.data(life_seed)
.join(
enter => enter.append('div').classed('paste-line', true),
update => update,
exit => exit
.remove()
) //join returns enter and update merged
.html(d => d)
.transition().duration(beat_life_seed)
.remove()
.on('end', (a, b, c, d) => {
console.log(`count these end events:\n${a}\n${b}\n${c}\n${d}`)
load_new_state(life_seed || seed)
}) // BUG #8 this transition ends for each line, I want to wait for the last
.on('end',
(_d, i) => {
console.log(`.data(life_seed): [${i}] = ${_d}\n`)
if (i == last_line)
load_new_state(life_seed || seed)
}
) // BUG #8 this transition ends for each line, I want to wait for the last
}

const load_new_state = (life_seed) => {
console.time('load_new_state')

let grid
// clear the state
grid_ping = Array.from({ length: grid_h }, () => Array.from({ length: grid_w }, () => '⬛'))
if (ping_pong) {
grid = grid_ping // if ping_pong is true, then we just finished applying rule to grid_ping, replace it
} else {
grid = grid_pong
}
// clear the grid in place
for (let row of grid) {
for (let i = 0; i < row.length; i++) {
row[i] = '⬛'
}
}
// copy the life_seed into the center of the state
add_seed(life_seed, grid_ping)
pause_for_new = 2 * 60
add_seed(life_seed, grid)
pause_for_new = Math.round(1.333 * 60) // secs * frames/sec => units of frames

console.timeEnd('load_new_state')
}
Expand Down
4 changes: 2 additions & 2 deletions public/src/does-it-glider/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
// XXX
export const settings = {
// game of life configuration
GRID_WIDTH: 512, // in cells
GRID_HEIGHT: 512, // in cells
GRID_WIDTH: 192, // in cells
GRID_HEIGHT: 192, // in cells
WRAP_GRID: false,

// shader source constants
Expand Down