3 <html lang=
"en" xmlns=
"http://www.w3.org/1999/xhtml">
5 <meta charset=
"utf-8" />
7 <style type=
"text/css">
9 background-color: #262926;
15 <!-- Fullscreen canvas -->
16 <script type=
"text/javascript">
17 function fullscreenCanvas() {
18 var c
= window
.document
.createElement('canvas');
19 window
.document
.body
.appendChild(c
);
21 var ctx
= c
.getContext('2d');
23 ctx
.canvas
.width
= window
.innerWidth
;
24 ctx
.canvas
.height
= window
.innerHeight
;
25 ctx
.canvas
.style
.position
= 'absolute';
26 ctx
.canvas
.style
.top
= 0;
27 ctx
.canvas
.style
.left
= 0;
33 <script type=
"text/javascript">
39 function hcy2rgb(h
, c
, y
) {
43 var k
= (1 - Math
.abs((h
% 2) - 1));
45 var K
= h
< 1 ? r
+ k
* g
54 if (y
<= 0 || y
>= 1) cmax
= 0;
55 else cmax
*= K
< y
? (y
- 1) / (K
- 1) : K
> y
? y
/ K
: 1;
56 c
= Math
.min(c
, cmax
);
59 var rgb
= h
< 1 ? [c
, x
, 0]
66 var m
= y
- (r
* rgb
[0] + g
* rgb
[1] + b
* rgb
[2]);
68 return [rgb
[0] + m
, rgb
[1] + m
, rgb
[2] + m
];
71 function hcyCol(h
, c
, y
)
73 var col
= hcy2rgb(h
% 360, c
, y
);
74 ctx
.fillStyle
= 'rgb(' + (255 * col
[0] | 0) + ', ' + (255 * col
[1] | 0) + ',' + (255 * col
[2] | 0) + ')';
79 <script type=
"text/javascript">
83 this.x
= x
; this.y
= y
;
86 function DerivedPoint(p
, dx
, dy
)
93 Object
.defineProperty(DerivedPoint
.prototype, 'x', {
94 get: function () { return this.point
.x
+ this.dx
; },
95 set: function (v
) { this.point
.x
= v
- this.dx
}
98 Object
.defineProperty(DerivedPoint
.prototype, 'y', {
99 get: function () { return this.point
.y
+ this.dy
; },
100 set: function (v
) { this.point
.y
= v
- this.dy
}
103 Point
.prototype.distanceLessThan = function (maxdist
) {
108 var distSq
= (p
.x
- p1
.x
) * (p
.x
- p1
.x
) + (p
.y
- p1
.y
) * (p
.y
- p1
.y
);
109 return distSq
< maxdist
;
113 function Line(p1
, p2
) {
114 this.nodes
= [p1
, p2
];
119 <script type=
"text/javascript">
121 var ctx
= fullscreenCanvas();
124 var zoom
= ctx
.canvas
.width
< ctx
.canvas
.height
125 ? ctx
.canvas
.width
/ extent
126 : ctx
.canvas
.height
/ extent
;
128 ctx
.translate(ctx
.canvas
.width
/ 2, ctx
.canvas
.height
/ 2);
129 ctx
.scale(zoom
, -zoom
);
130 ctx
.lineJoin
= 'round';
131 ctx
.strokeStyle
= '#999';
132 ctx
.fillStyle
= '#777';
133 ctx
.lineWidth
= 1 / zoom
;
136 var w2
= a
/ Math
.sqrt(3);
138 var pa
= new Point(w2
, 0);
139 var pb
= new Point(w2
/ 2, a
/ 2);
143 var p3
= new DerivedPoint(pa
, -3 * w2
/ 2, a
/ 2);
144 var p4
= new DerivedPoint(pb
, -3 * w2
/ 2, -a
/ 2);
145 var p5
= new DerivedPoint(pa
, -3 * w2
/ 2, -a
/ 2);
146 var p6
= new DerivedPoint(pb
, 0, -a
);
157 var cursor
= new Point(-extent
*2, -extent
*2);
159 var prevClickTime
= 0;
160 var lastClickTime
= 0;
161 var dblClickTime
= 250;
163 function onmousemove(e
) {
164 cursor
= new Point(cursor
.x
= (e
.clientX
- ctx
.canvas
.width
/ 2) / zoom
, (ctx
.canvas
.height
/ 2 - e
.clientY
) / zoom
);
167 var p
= lines
[selected
[0]].nodes
[selected
[1]];
175 function findNode(cursor
)
177 for (var i
= 0; i
< lines
.length
; ++i
)
179 var node
= findIndex(lines
[i
].nodes
, cursor
.distanceLessThan(10 / zoom
));
188 function onmousedown(e
) {
189 var node
= findNode(cursor
);
191 if (e
.button
== 1 && node
&& lines
[node
[0]].nodes
.length
> 2 && node
[1]!=0 && node
[1]!=lines
[node
[0]].nodes
.lenght
) {
192 lines
[node
[0]].nodes
.splice(node
[1], 1);
198 prevClickTime
= lastClickTime
;
199 lastClickTime
= (new Date()).getTime();
204 function onmouseup() {
207 var time
= (new Date()).getTime();
208 var dbclick
= (time
- prevClickTime
) < dblClickTime
;
211 var node
= findNode(cursor
);
216 var line
= lines
[node
[0]];
217 var i
= node
[1] == 0 ? 1 : node
[1];
219 var newNode
= new Point(line
.nodes
[i
].x
- (line
.nodes
[i
].x
- line
.nodes
[i
-1].x
) / 2,
220 line
.nodes
[i
].y
- (line
.nodes
[i
].y
- line
.nodes
[i
-1].y
) / 2);
222 line
.nodes
.splice(i
, 0, newNode
);
227 function findIndex(array
, predicate
) {
228 for(var i
=0;i
<array
.length
;++i
)
229 if (predicate(array
[i
], i
, array
))
233 function drawLine(line
) {
236 for (var n
= 0; n
< k
; ++n
) {
237 for (var m
= 0; m
< k
; ++m
) {
238 var dx
= (n
- k
/ 2) * 3 * w2
/ 2;
239 var dy
= (m
- k
/ 2) * a
+ ((1 + n
) % 2) * a
/ 2;
242 ctx
.moveTo(line
.nodes
[0].x
+ dx
, line
.nodes
[0].y
+ dy
);
244 for (var i
= 1; i
< line
.nodes
.length
; ++i
)
245 ctx
.lineTo(line
.nodes
[i
].x
+ dx
, line
.nodes
[i
].y
+ dy
);
253 ctx
.clearRect(-extent
*2, -extent
*2, extent
* 4, extent
* 4);
255 ctx
.strokeStyle
= '#777';
256 ctx
.fillStyle
= 'rgba(200, 20,20,.3)';
260 for (var i
= 0; i
< n
; ++i
) {
261 for (var j
= 0; j
< m
; ++j
)
264 var dx
= (i
- n
/ 2) * 3 * w2
/ 2;
265 var dy
= (j
- m
/ 2) * a
+ ((1 + i
) % 2) * a
/ 2;
267 ctx
.fillStyle
= hcyCol(i
*50+j
*10, .5, .5);
269 ctx
.translate(dx
, dy
);
274 lines
.forEach(function (line
) {
276 ctx
.moveTo(line
.nodes
[0].x
, line
.nodes
[0].y
);
280 for (var k
= first
? 1 : 0; k
< line
.nodes
.length
; ++k
)
281 ctx
.lineTo(line
.nodes
[k
].x
, line
.nodes
[k
].y
);
293 ctx
.strokeStyle
= 'white';
294 ctx
.fillStyle
= 'white';
295 lines
.forEach(function (line
) {
298 ctx
.moveTo(line
.nodes
[0].x
, line
.nodes
[0].y
);
299 for (var i
= 1; i
< line
.nodes
.length
; ++i
)
300 ctx
.lineTo(line
.nodes
[i
].x
, line
.nodes
[i
].y
);
304 line
.nodes
.forEach(function(node
) {
305 ctx
.fillRect(node
.x
- 1.5 / zoom
, node
.y
- 1.5 / zoom
, 3 / zoom
, 3 / zoom
);
309 var node
= findNode(cursor
);
312 ctx
.fillRect(lines
[selected
[0]].nodes
[selected
[1]].x
- 5 / zoom
, lines
[selected
[0]].nodes
[selected
[1]].y
- 5 / zoom
, 10 / zoom
, 10 / zoom
);
316 ctx
.strokeRect(lines
[node
[0]].nodes
[node
[1]].x
- 5 / zoom
, lines
[node
[0]].nodes
[node
[1]].y
- 5 / zoom
, 10 / zoom
, 10 / zoom
);
319 window
.requestAnimationFrame(frame
);
322 ctx
.canvas
.addEventListener('mousemove', onmousemove
);
323 ctx
.canvas
.addEventListener('mousedown', onmousedown
);
325 ctx
.canvas
.addEventListener('mouseup', onmouseup
);
327 window
.requestAnimationFrame(frame
);