about summary refs log tree commit diff stats
path: root/app/assets/javascripts/display2.js
diff options
context:
space:
mode:
authorStar Rauchenberger <fefferburbia@gmail.com>2023-11-30 13:29:08 -0500
committerStar Rauchenberger <fefferburbia@gmail.com>2023-11-30 13:29:08 -0500
commit0929719a845897cc8567cf972e07a69a71f0fa6f (patch)
tree2b6f69c1d906abb6e0abf8a0f1d51725bc78087d /app/assets/javascripts/display2.js
parent01c1947537e4e23ded0c16812a7cd9d49ad88356 (diff)
downloadwittle-0929719a845897cc8567cf972e07a69a71f0fa6f.tar.gz
wittle-0929719a845897cc8567cf972e07a69a71f0fa6f.tar.bz2
wittle-0929719a845897cc8567cf972e07a69a71f0fa6f.zip
Migrate to a full rails app
Diffstat (limited to 'app/assets/javascripts/display2.js')
-rw-r--r--app/assets/javascripts/display2.js316
1 files changed, 316 insertions, 0 deletions
diff --git a/app/assets/javascripts/display2.js b/app/assets/javascripts/display2.js new file mode 100644 index 0000000..ddf3968 --- /dev/null +++ b/app/assets/javascripts/display2.js
@@ -0,0 +1,316 @@
1var SYM_TYPE_NONE = 0
2var SYM_TYPE_HORIZONTAL = 1
3var SYM_TYPE_VERTICAL = 2
4var SYM_TYPE_ROTATIONAL = 3
5var SYM_TYPE_ROTATE_LEFT = 4
6var SYM_TYPE_ROTATE_RIGHT = 5
7var SYM_TYPE_FLIP_XY = 6
8var SYM_TYPE_FLIP_NEG_XY = 7
9var SYM_TYPE_PARALLEL_H = 8
10var SYM_TYPE_PARALLEL_V = 9
11var SYM_TYPE_PARALLEL_H_FLIP = 10
12var SYM_TYPE_PARALLEL_V_FLIP = 11
13var SYM_TYPE_PILLAR_PARALLEL = 12
14var SYM_TYPE_PILLAR_HORIZONTAL = 13
15var SYM_TYPE_PILLAR_VERTICAL = 14
16var SYM_TYPE_PILLAR_ROTATIONAL = 15
17
18namespace(function() {
19
20window.draw = function(puzzle, target='puzzle') {
21 if (puzzle == null) return
22 var svg = document.getElementById(target)
23 console.info('Drawing', puzzle, 'into', svg)
24 while (svg.firstChild) svg.removeChild(svg.firstChild)
25
26 // Prevent context menu popups within the puzzle
27 svg.oncontextmenu = function(event) {
28 event.preventDefault()
29 }
30
31 if (puzzle.pillar === true) {
32 // 41*width + 30*2 (padding) + 10*2 (border)
33 var pixelWidth = 41 * puzzle.width + 80
34 } else {
35 // 41*(width-1) + 24 (extra edge) + 30*2 (padding) + 10*2 (border)
36 var pixelWidth = 41 * puzzle.width + 63
37 }
38 var pixelHeight = 41 * puzzle.height + 63
39 svg.setAttribute('viewbox', '0 0 ' + pixelWidth + ' ' + pixelHeight)
40 svg.setAttribute('width', pixelWidth)
41 svg.setAttribute('height', pixelHeight)
42
43 var rect = createElement('rect')
44 svg.appendChild(rect)
45 rect.setAttribute('stroke-width', 10)
46 rect.setAttribute('stroke', window.BORDER)
47 rect.setAttribute('fill', window.OUTER_BACKGROUND)
48 // Accounting for the border thickness
49 rect.setAttribute('x', 5)
50 rect.setAttribute('y', 5)
51 rect.setAttribute('width', pixelWidth - 10) // Removing border
52 rect.setAttribute('height', pixelHeight - 10) // Removing border
53
54 drawCenters(puzzle, svg)
55 drawGrid(puzzle, svg, target)
56 drawStartAndEnd(puzzle, svg)
57 // Draw cell symbols after so they overlap the lines, if necessary
58 drawSymbols(puzzle, svg, target)
59
60 // For pillar puzzles, add faders for the left and right sides
61 if (puzzle.pillar === true) {
62 var defs = window.createElement('defs')
63 defs.id = 'cursorPos'
64 defs.innerHTML = '' +
65 '<linearGradient id="fadeInLeft">\n' +
66 ' <stop offset="0%" stop-opacity="1.0" stop-color="' + window.OUTER_BACKGROUND + '"></stop>\n' +
67 ' <stop offset="25%" stop-opacity="1.0" stop-color="' + window.OUTER_BACKGROUND + '"></stop>\n' +
68 ' <stop offset="100%" stop-opacity="0.0" stop-color="' + window.OUTER_BACKGROUND + '"></stop>\n' +
69 '</linearGradient>\n' +
70 '<linearGradient id="fadeOutRight">\n' +
71 ' <stop offset="0%" stop-opacity="0.0" stop-color="' + window.OUTER_BACKGROUND + '"></stop>\n' +
72 ' <stop offset="100%" stop-opacity="1.0" stop-color="' + window.OUTER_BACKGROUND + '"></stop>\n' +
73 '</linearGradient>\n'
74 svg.appendChild(defs)
75
76 var leftBox = window.createElement('rect')
77 leftBox.setAttribute('x', 16)
78 leftBox.setAttribute('y', 10)
79 leftBox.setAttribute('width', 48)
80 leftBox.setAttribute('height', 41 * puzzle.height + 43)
81 leftBox.setAttribute('fill', 'url(#fadeInLeft)')
82 leftBox.setAttribute('style', 'pointer-events: none')
83 svg.appendChild(leftBox)
84
85 var rightBox = window.createElement('rect')
86 rightBox.setAttribute('x', 41 * puzzle.width + 22)
87 rightBox.setAttribute('y', 10)
88 rightBox.setAttribute('width', 30)
89 rightBox.setAttribute('height', 41 * puzzle.height + 43)
90 rightBox.setAttribute('fill', 'url(#fadeOutRight)')
91 rightBox.setAttribute('style', 'pointer-events: none')
92 svg.appendChild(rightBox)
93 }
94}
95
96function drawCenters(puzzle, svg) {
97 // @Hack that I am not fixing. This switches the puzzle's grid to a floodfilled grid
98 // where null represents cells which are part of the outside
99 var savedGrid = puzzle.switchToMaskedGrid()
100 if (puzzle.pillar === true) {
101 for (var y=1; y<puzzle.height; y += 2) {
102 if (puzzle.getCell(-1, y) == null) continue // Cell borders the outside
103
104 var rect = createElement('rect')
105 rect.setAttribute('x', 28)
106 rect.setAttribute('y', 41 * y + 11)
107 rect.setAttribute('width', 24)
108 rect.setAttribute('height', 82)
109 rect.setAttribute('fill', window.BACKGROUND)
110 svg.appendChild(rect)
111 }
112 }
113
114 for (var x=1; x<puzzle.width; x += 2) {
115 for (var y=1; y<puzzle.height; y += 2) {
116 if (puzzle.grid[x][y] == null) continue // Cell borders the outside
117
118 var rect = createElement('rect')
119 rect.setAttribute('x', 41 * x + 11)
120 rect.setAttribute('y', 41 * y + 11)
121 rect.setAttribute('width', 82)
122 rect.setAttribute('height', 82)
123 rect.setAttribute('fill', window.BACKGROUND)
124 rect.setAttribute('shape-rendering', 'crispedges') // Otherwise they don't meet behind gaps
125 svg.appendChild(rect)
126 }
127 }
128 puzzle.grid = savedGrid
129}
130
131function drawGrid(puzzle, svg, target) {
132 for (var x=0; x<puzzle.width; x++) {
133 for (var y=0; y<puzzle.height; y++) {
134 var cell = puzzle.grid[x][y]
135 if (cell != null && cell.gap === window.GAP_FULL) continue
136 if (cell != null && cell.gap === window.GAP_BREAK) {
137 var params = {
138 'width':58,
139 'height':58,
140 'x': x*41 + 23,
141 'y': y*41 + 23,
142 'class': target + '_' + x + '_' + y,
143 'type': 'gap',
144 }
145 if (x%2 === 0 && y%2 === 1) params.rot = 1
146 drawSymbolWithSvg(svg, params)
147 continue
148 }
149
150 var line = createElement('line')
151 line.setAttribute('stroke-width', 24)
152 line.setAttribute('stroke-linecap', 'round')
153 line.setAttribute('stroke', window.FOREGROUND)
154 if (x%2 === 1 && y%2 === 0) { // Horizontal
155 if (cell.gap === window.GAP_BREAK) continue
156 line.setAttribute('x1', (x-1)*41 + 52)
157 // Adjust the length if it's a pillar -- the grid is not as wide!
158 if (puzzle.pillar === true && x === puzzle.width - 1) {
159 line.setAttribute('x2', (x+1)*41 + 40)
160 } else {
161 line.setAttribute('x2', (x+1)*41 + 52)
162 }
163 line.setAttribute('y1', y*41 + 52)
164 line.setAttribute('y2', y*41 + 52)
165 svg.appendChild(line)
166 } else if (x%2 === 0 && y%2 === 1) { // Vertical
167 if (cell.gap === window.GAP_BREAK) continue
168 line.setAttribute('x1', x*41 + 52)
169 line.setAttribute('x2', x*41 + 52)
170 line.setAttribute('y1', (y-1)*41 + 52)
171 line.setAttribute('y2', (y+1)*41 + 52)
172 svg.appendChild(line)
173 } else if (x%2 === 0 && y%2 === 0) { // Intersection
174 var surroundingLines = 0
175 if (cell.end != null) surroundingLines++
176 var leftCell = puzzle.getCell(x - 1, y)
177 if (leftCell != null && leftCell.gap !== window.GAP_FULL) surroundingLines++
178 var rightCell = puzzle.getCell(x + 1, y)
179 if (rightCell != null && rightCell.gap !== window.GAP_FULL) surroundingLines++
180 var topCell = puzzle.getCell(x, y - 1)
181 if (topCell != null && topCell.gap !== window.GAP_FULL) surroundingLines++
182 var bottomCell = puzzle.getCell(x, y + 1)
183 if (bottomCell != null && bottomCell.gap !== window.GAP_FULL) surroundingLines++
184
185 if (surroundingLines === 1) {
186 // Add square caps for dead ends which are non-endpoints
187 var rect = createElement('rect')
188 rect.setAttribute('x', x*41 + 40)
189 rect.setAttribute('y', y*41 + 40)
190 rect.setAttribute('width', 24)
191 rect.setAttribute('height', 24)
192 rect.setAttribute('fill', window.FOREGROUND)
193 svg.appendChild(rect)
194 } else if (surroundingLines > 1) {
195 // Add rounding for other intersections (handling gap-only corners)
196 var circ = createElement('circle')
197 circ.setAttribute('cx', x*41 + 52)
198 circ.setAttribute('cy', y*41 + 52)
199 circ.setAttribute('r', 12)
200 circ.setAttribute('fill', window.FOREGROUND)
201 svg.appendChild(circ)
202 }
203 }
204 }
205 }
206 // Determine if left-side needs a 'wrap indicator'
207 if (puzzle.pillar === true) {
208 var x = 0;
209 for (var y=0; y<puzzle.height; y+=2) {
210 var cell = puzzle.getCell(x-1, y)
211 if (cell == null || cell.gap === window.GAP_FULL) continue
212 var line = createElement('line')
213 line.setAttribute('stroke-width', 24)
214 line.setAttribute('stroke-linecap', 'round')
215 line.setAttribute('stroke', window.FOREGROUND)
216 line.setAttribute('x1', x*41 + 40)
217 line.setAttribute('x2', x*41 + 52)
218 line.setAttribute('y1', y*41 + 52)
219 line.setAttribute('y2', y*41 + 52)
220 svg.appendChild(line)
221 }
222 }
223}
224
225function drawSymbols(puzzle, svg, target) {
226 for (var x=0; x<puzzle.width; x++) {
227 for (var y=0; y<puzzle.height; y++) {
228 var cell = puzzle.grid[x][y]
229 if (cell == null) continue
230 var params = {
231 'width':58,
232 'height':58,
233 'x': x*41 + 23,
234 'y': y*41 + 23,
235 'class': target + '_' + x + '_' + y,
236 }
237 if (cell.dot > window.DOT_NONE) {
238 params.type = 'dot'
239 if (cell.dot === window.DOT_BLACK) params.color = 'black'
240 else if (cell.dot === window.DOT_BLUE) params.color = window.LINE_PRIMARY
241 else if (cell.dot === window.DOT_YELLOW) params.color = window.LINE_SECONDARY
242 else if (cell.dot === window.DOT_INVISIBLE) {
243 params.color = window.FOREGROUND
244 // This makes the invisible dots visible, but only while we're in the editor.
245 if (document.getElementById('metaButtons') != null) {
246 params.stroke = 'black'
247 params.strokeWidth = '2px'
248 }
249 }
250 drawSymbolWithSvg(svg, params)
251 } else if (cell.gap === window.GAP_BREAK) {
252 // Gaps were handled above, while drawing the grid.
253 } else if (x%2 === 1 && y%2 === 1) {
254 // Generic draw for all other elements
255 Object.assign(params, cell)
256 window.drawSymbolWithSvg(svg, params, puzzle.settings.CUSTOM_MECHANICS)
257 }
258 }
259 }
260}
261
262function drawStartAndEnd(puzzle, svg) {
263 for (var x=0; x<puzzle.width; x++) {
264 for (var y=0; y<puzzle.height; y++) {
265 var cell = puzzle.grid[x][y]
266 if (cell == null) continue
267 if (cell.end != null) {
268 window.drawSymbolWithSvg(svg, {
269 'type': 'end',
270 'width': 58,
271 'height': 58,
272 'dir': cell.end,
273 'x': x*41 + 23,
274 'y': y*41 + 23,
275 })
276 }
277
278 if (cell.start === true) {
279 var symStart = null
280 if (puzzle.symType != SYM_TYPE_NONE) {
281 var sym = puzzle.getSymmetricalPos(x, y)
282 window.drawSymbolWithSvg(svg, {
283 'type': 'start',
284 'width': 58,
285 'height': 58,
286 'x': sym.x*41 + 23,
287 'y': sym.y*41 + 23,
288 })
289 symStart = svg.lastChild
290 symStart.style.display = 'none'
291 symStart.id = 'symStart_' + svg.id + '_' + x + '_' + y
292 }
293
294 window.drawSymbolWithSvg(svg, {
295 'type': 'start',
296 'width': 58,
297 'height': 58,
298 'x': x*41 + 23,
299 'y': y*41 + 23,
300 })
301 var start = svg.lastChild
302 start.id = 'start_' + svg.id + '_' + x + '_' + y
303
304 // ;(function(a){}(a))
305 // This syntax is used to forcibly copy all of the arguments
306 ;(function(puzzle, x, y, start, symStart) {
307 start.onpointerdown = function(event) {
308 window.trace(event, puzzle, {'x':x, 'y':y}, start, symStart)
309 }
310 }(puzzle, x, y, start, symStart))
311 }
312 }
313 }
314}
315
316})