1 <!DOCTYPE HTML PUBLIC
"-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2 <html xmlns=
"http://www.w3.org/1999/xhtml">
4 <title>forcefield
</title>
5 <style type=
"text/css">
7 background-color: #232220;
13 <canvas id=
"can"></canvas>
16 <script type=
"text/javascript">
18 /** rnd() random value from 0 to 1
19 * rnd(n) random from 0 to n
20 * rnd(n, m) random between n and m
21 * rnd(array) random element
24 switch (arguments
.length
) {
28 if (a
instanceof Array
)
29 return a
[Math
.floor(Math
.random() * a
.length
)];
31 return a
* Math
.random();
32 default: // case 2 actually
33 return a
+ (b
- a
) * Math
.random();
38 return Math
.floor(rnd
.apply(null, arguments
));
44 <script type=
"text/javascript">
46 function Point(x
, y
) {
51 Point
.prototype.toString = function () { return this.x
+ ',' + this.y
}
53 Point
.prototype.equal = function (p
) {
54 return this.x
== p
.x
&& this.y
== p
.y
;
57 Point
.prototype.times = function (n
) {
58 return new Point(this.x
* n
, this.y
* n
);
61 Point
.prototype.add = function (dx
, dy
) {
62 return new Point(this.x
+ dx
, this.y
+ dy
);
65 Point
.prototype.plus = function (p
) {
66 return new Point(this.x
+ p
.x
, this.y
+ p
.y
);
72 <script type=
"text/javascript">
74 function GridPoint(i
, j
) {
79 var sq32
= Math
.sqrt(3) / 2;
81 function world(i
, j
) {
82 if (arguments
.length
== 1) {
87 return new Point(j
* sq32
, i
- j
/ 2);
91 if (arguments
.length
== 1) {
98 return new GridPoint(y
+ j
/ 2, j
);
101 function MakeData(extremes
, initFn
) {
103 var minmax
= extremes
.reduce(function(current
, point
) {
107 return current
== null
110 Math
.min(current
[0], i
),
111 Math
.min(current
[1], j
),
112 Math
.max(current
[2], i
),
113 Math
.max(current
[3], j
)
117 var mini
= minmax
[0];
118 var minj
= minmax
[1];
119 var maxi
= minmax
[2];
120 var maxj
= minmax
[3];
122 // x, y (getStore(origin) -> 0 0)
123 var origin
= [-mini
, -minj
];
126 var n
= maxi
- mini
+ 1;
127 var m
= maxj
- minj
+ 1;
136 row
.push(initFn(new GridPoint(i
- origin
[1], j
- origin
[0])));
143 return [origin
, data
];
146 function MakeHexStore(extremes
, initFn
, cloneFn
) {
147 var originData
= MakeData(extremes
, initFn
);
149 return new HexStore(originData
[0], originData
[1], initFn
, cloneFn
);
152 function HexStore(origin
, data
, initFn
, cloneFn
) {
153 this.origin
= origin
;
155 this.initFn
= initFn
;
156 this.cloneFn
= cloneFn
;
159 HexStore
.prototype.set = function (pos
, value
) {
160 var n
= this.data
.length
;
161 var m
= this.data
[0].length
;
163 var xo
= this.origin
[0];
164 var yo
= this.origin
[1];
169 if (i
>= 0 && j
>= 0 && i
< n
&& j
< m
) this.data
[i
][j
] = value
;
172 HexStore
.prototype.forEach = function (processFn
) {
174 var n
= this.data
.length
;
175 var m
= this.data
[0].length
;
177 var xo
= this.origin
[0];
178 var yo
= this.origin
[1];
184 if (this.data
[i
][j
] != null)
185 processFn(this.data
[i
][j
], new GridPoint(i
- yo
, j
- xo
));
191 [/*down-right*/ 1, 0],
195 [/*down-left*/ -1, -1],
199 function Adjacent(dir
, cell
) {
204 HexStore
.prototype.adjacent = function (pos
) {
206 var i
= pos
.i
+ this.origin
[1];
207 var j
= pos
.j
+ this.origin
[0];
209 var data
= this.data
;
210 var n
= this.data
.length
;
211 var m
= this.data
[0].length
;
213 return dirsdeltas
.map(function (d
, dir
) {
220 return new Adjacent(dir
, (id
>= 0 && jd
>= 0 && id
< n
&& jd
< m
) ? data
[id
][jd
] : null);
225 HexStore
.prototype.copy = function () {
227 var n
= this.data
.length
;
228 var m
= this.data
[0].length
;
232 for (var i
= 0; i
< n
; ++i
) {
234 for (var j
= 0; j
< m
; ++j
) {
235 var val
= this.data
[i
][j
];
236 row
.push(val
== null ? null : this.cloneFn(val
));
241 return new HexStore(this.origin
, copy
, this.initFn
, this.cloneFn
);
244 HexStore
.prototype.nextGen = function (cellCellStoreStoreFunc
) {
246 var newStore
= this.copy();
248 var n
= this.data
.length
;
249 var m
= this.data
[0].length
;
251 var xo
= this.origin
[0];
252 var yo
= this.origin
[1];
254 for (var i
= 0; i
< n
; ++i
) {
255 for (var j
= 0; j
< m
; ++j
) {
256 var pos
= new GridPoint(i
- yo
, j
- xo
);
257 cellCellStoreStoreFunc(this.data
[i
][j
], newStore
.data
[i
][j
], pos
, this, newStore
);
265 <!-- Fullscreen canvas and drawhex -->
266 <script type=
"text/javascript">
267 function fullscreenCanvas(id
) {
268 var c
= window
.document
.getElementById(id
);
269 var ctx
= c
.getContext('2d');
271 ctx
.canvas
.width
= window
.innerWidth
;
272 ctx
.canvas
.height
= window
.innerHeight
;
273 ctx
.canvas
.style
.position
= 'absolute';
274 ctx
.canvas
.style
.top
= 0;
275 ctx
.canvas
.style
.left
= 0;
280 var ctx
= fullscreenCanvas('can');
281 var w
= ctx
.canvas
.width
;
282 var h
= ctx
.canvas
.height
;
284 var q
= 1 / Math
.sqrt(3);
286 ctx
.pathHex = function (h
) {
291 this.moveTo(2 * dx
, 0);
292 this.lineTo(dx
, -dy
);
293 this.lineTo(-dx
, -dy
);
294 this.lineTo(-2 * dx
, 0);
295 this.lineTo(-dx
, dy
);
300 ctx
.fillHex = function (x
, y
, h
) {
302 this.translate(x
, y
);
308 ctx
.strokeHex = function (x
, y
, h
) {
310 this.translate(x
, y
);
316 ctx
.translate(ctx
.canvas
.width
/ 2, ctx
.canvas
.height
/ 2);
321 <script type=
"text/javascript">
322 /* Hexagonal area with same tiling properties as underlying cells.
324 function SuperCell(x
, y
, rank
) {
330 SuperCell
.prototype.getCorners = function () {
333 return [new Point(2 * r
, r
- 1),
336 new Point(r
- 1, 2 * r
),
337 new Point(-r
+ 1, r
+ 1),
339 new Point(-2 * r
, -r
),
340 new Point(-2 * r
, -r
- 1),
341 new Point(-r
- 1, -2 * r
),
342 new Point(-r
, -2 * r
),
344 new Point(r
+ 1, -r
+ 1)
348 SuperCell
.prototype.contains = function (point
) {
349 var x
= point
.j
- this.x
;
350 var y
= point
.i
- this.y
;
352 var a
= this.rank
* 3 + 1;
353 var b
= this.rank
* 3;
355 var leftup
= 2 * x
+ b
;
356 var top
= (x
+ a
) / 2;
358 var rightdown
= 2 * x
- a
;
359 var bottom
= (x
- b
) / 2;
360 var leftdown
= -x
- a
;
372 <script type=
"text/javascript">
375 function hcy2rgb(h
, c
, y
, a
) {
384 var k
= (1 - Math
.abs((h
% 2) - 1));
386 var K
= h
< 1 ? r
+ k
* g
395 if (y
<= 0 || y
>= 1) cmax
= 0;
396 else cmax
*= K
< y
? (y
- 1) / (K
- 1) : K
> y
? y
/ K
: 1;
398 c
= Math
.min(c
, cmax
);
401 var rgb
= h
< 1 ? [c
, x
, 0]
408 var m
= y
- (r
* rgb
[0] + g
* rgb
[1] + b
* rgb
[2]);
410 var rgbdata
= [rgb
[0] + m
, rgb
[1] + m
, rgb
[2] + m
];
411 return 'rgba(' + (rgbdata
[0] * 255).toFixed(0) + ',' + (rgbdata
[1] * 255).toFixed(0) + ',' + (rgbdata
[2] * 255).toFixed(0) + ', ' + (a
|| 1) + ')';
417 <script type=
"text/javascript">
420 var supercell
= new SuperCell(0, 0, rank
);
421 var extremes
= supercell
.getCorners();
423 function CellValue(value
) {
427 function CloneCellValue(arg
) {
428 return new CellValue(arg
.value
);
434 function MakeCellValue(arg
) {
435 if (supercell
.contains(arg
)) {
437 var a
= rnd(2 * Math
.PI
);
438 return new CellValue(new Point(d
* cos(a
), d
* sin(a
)));
444 var store
= new MakeHexStore(supercell
.getCorners(), MakeCellValue
, CloneCellValue
);
446 function draw(store
) {
450 function cellColor(val
, pos
) {
451 if (val
!= null && val
instanceof CellValue
) {
452 var a
= ((720 + Math
.atan2(val
.value
.x
, val
.value
.y
) * 180 / Math
.PI
) % 360).toFixed();
453 var alpha
= (amin
+ (amax
- amin
) * (val
.value
.x
* val
.value
.x
+ val
.value
.y
* val
.value
.y
));
455 return hcy2rgb(a
, 1, .5, alpha
);
458 return 'transparent';
461 function drawCell(cell
, pos
) {
462 var screen
= world(pos
);
466 ctx
.fillStyle
= ctx
.strokeStyle
= cellColor(cell
, pos
);
471 ctx
.fillHex(0, 0, 1);
476 var a
= Math
.atan2(cell
.value
.y
, cell
.value
.x
);
477 var dx
= Math
.cos(a
);
478 var dy
= Math
.sin(a
);
480 ctx
.lineTo(dx
/ 2, dy
/ 2);
485 ctx
.clearRect(-ctx
.canvas
.width
/ 2, -ctx
.canvas
.height
/ 2, ctx
.canvas
.width
, ctx
.canvas
.height
);
488 ctx
.scale(1.3*rank
,1.3* rank
);
489 var lw
= ctx
.lineWidth
= 1.5 / rank
;
490 store
.forEach(drawCell
);
494 for (var i
= 0; i
< frameNum
; ++i
) {
495 var n
= (frameNum
+ frameNo
- i
) % frameNum
;
496 var frame
= frames
[n
];
502 var q
= 1 - i
/ frameNum
;
503 ctx
.strokeStyle
= 'rgba(255,255,255,' + q
+ ')';
505 ctx
.lineTo(frame
.x
, frame
.y
);
510 ctx
.moveTo(frame
.x
, frame
.y
);
518 var a
= 4 * Math
.PI
/ 3;
519 var da
= [a
, a
+ x
, a
+ 2 * x
, a
+ 3 * x
, a
+ 4 * x
, a
+ 5 * x
];
520 var ddd
= rnd(Math
.PI
*2);
522 var csr
= new Point(0, 0);
525 var frames
= new Array(frameNum
);
527 function fl(n
) { return Math
.floor(n
); }
530 function dist(p
, q
) {
531 var dx
= (p
.x
- q
.x
) ;
532 var dy
= (p
.y
- q
.y
) ;
533 return Math
.sqrt(dx
* dx
+ dy
* dy
);
536 function fade(t
) { return t
* t
* t
* (t
* (t
* 6 - 15) + 10); }
539 var force
= new Point(0, 0);
544 var x1
, x2
, x3
, y1
, y2
, y3
;
546 if (p
.j
- ix
<= p
.i
- iy
) {
563 store
= store
.nextGen(function (oldCell
, newCell
, pos
, oldStore
, newStore
) {
565 if (oldCell
!= null) {
566 var zz
= oldStore
.adjacent(pos
);
567 var adj
= zz
.filter(function (a
) { return a
.cell
!= null });
568 var extravalue
= null;
570 if (adj
.length
== 6) {
571 if (pos
.j
== 0 && pos
.i
== 10) {
572 extravalue
= 0;//ddd;
573 } else if (pos
.j
== 10 && pos
.i
== 10) {
574 extravalue
= ddd
- Math
.PI
/ 3;
575 } else if (pos
.j
== 10 && pos
.i
== 0) {
576 extravalue
= ddd
- 2 * Math
.PI
/ 3;
577 } else if (pos
.j
== 0 && pos
.i
== -10) {
578 extravalue
= ddd
- 3 * Math
.PI
/ 3;
579 } else if (pos
.j
== -10 && pos
.i
== -10) {
580 extravalue
= ddd
- 4 * Math
.PI
/ 3;
581 } else if (pos
.j
== -10 && pos
.i
== 0) {
582 extravalue
= ddd
- 5 * Math
.PI
/ 3;
585 .reduce(function (sum
, el
) { return sum
== null ? el
.cell
.value
: sum
.plus(el
.cell
.value
) }, null)
586 .times(1 / adj
.length
);
590 var windMaxAmount
= .02*sin(3*ddd
)*sin(5*ddd
)*sin(7*ddd
);
591 var windAngle
= ddd
* 2;
592 var wind
= new Point(windMaxAmount
* cos(windAngle
), windMaxAmount
* sin(windAngle
));
594 newCell
.value
= newCell
.value
.plus(wind
);
596 if (extravalue
!= null) {
597 var p
= new Point(cos(extravalue
), sin(extravalue
));
598 var q
= sin(ddd
/ 5) * sin(ddd
/ 5);
600 newCell
.value
= newCell
.value
.times(q
).plus(p
.times(1 - q
));
604 var a
= Math
.atan2(-p
.y
, -p
.x
);
607 newCell
.value
= new Point(d
* cos(a
), d
* sin(a
));
610 total
+= newCell
.value
.x
* newCell
.value
.x
+ newCell
.value
.y
* newCell
.value
.y
;
613 if ((pos
.j
== x1
&& pos
.i
== y1
) || (pos
.j
== x2
&& pos
.i
== y2
) || (pos
.j
== x3
&& pos
.i
== y3
)) {
614 var d
= dist(world(pos
), csr
);
616 force
= force
.plus(newCell
.value
.times(k
));
621 csr
= csr
.plus(force
);
622 frames
[(++frameNo
) % frameNum
] = csr
;
627 window
.requestAnimationFrame(tick
);
635 <script type=
"text/javascript">
637 function InputManager(mapping
) {
640 this.handler = function(event
) { $.action
= mapping
[event
.which
] };
642 window
.addEventListener('keydown', this.handler
, false);