1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
import h from '../Util/DomUtil.js';
import { dist, AABB, QuadTree } from '../Geometry/Geometry.js';
class Canvas {
constructor(parentId) {
const parentElement= document.getElementById(parentId);
this.canvas = h('canvas', parentElement.width, parentElement.height);
this.context = this.canvas.getContext('2d');
/* state */
this.movingPoint = false;
this.selectedPoint = null;
/* callbacks */
this.onMovePoint = (original, now) => console.log(original, now);
/* transform */
this.scale = 1;
this.pan = { x: 0, y: 0 };
/* mouse */
this.mouse = { x: 0, y: 0 };
/* retrieving points */
this.points = [];
this.tree = new QuadTree(this.canvas.width, this.canvas.height);
/* event listeners */
this.canvas.addEventListener('mousemove', e => {
this.mouse.x = e.offsetX;
this.mouse.y = e.offsetY;
if (this.movingPoint) this.render();
});
this.canvas.addEventListener('click', e => {
if (this.movingPoint) {
this.dropPoint();
}
else {
const pt = this.getClickedPoint(
{ x: e.offsetX, y: e.offsetY }, 10
);
if (pt) this.grabPoint(pt);
}
});
/* attach to parent */
parentElement.appendChild(this.canvas);
}
/* transform a point to internal coordinates */
transform(x, y) {
return [
(this.scale * x) - this.pan.x,
(this.scale * y) - this.pan.y,
];
}
/* data updates */
updatePoints(points) {
this.points = [];
this.tree = new QuadTree(this.canvas.width, this.canvas.height);
for (let pt of points) this.insertPoint(pt);
this.render();
}
insertPoint(pt) {
this.tree.insert(pt);
this.points.push(pt);
}
/* drawing */
drawPoint(pt) {
const ct = this.context;
ct.save();
ct.beginPath();
const [x, y] = this.transform(pt.x, pt.y);
console.log(x,y);
ct.arc(x, y, 10/this.scale, 0, 2*Math.PI);
ct.closePath();
ct.fill();
ct.restore();
}
render() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
for (let pt of this.points) this.drawPoint(pt);
if (this.movingPoint) {
const [x, y] = this.transform(this.mouse.x, this.mouse.y);
this.drawPoint({x, y});
}
}
/* handling input */
grabPoint(pt) {
this.tree.root.remove(pt);
this.points = this.points.filter(point => (point.x !== pt.x || point.y !== pt.y));
console.log(this.points);
this.selectedPoint = pt;
this.movingPoint = true;
this.render();
}
dropPoint() {
const [x, y] = this.transform(this.mouse.x, this.mouse.y);
this.onMovePoint(this.selectedPoint, {x, y});
this.movingPoint = false;
this.selectedPoint = null;
}
getClickedPoint(clickLocation, maxDistance) {
const [ x, y ] = this.transform(clickLocation.x, clickLocation.y);
const clickPoint = { x, y };
const closest = this.tree.closest(clickPoint);
if (!closest) return null;
if (dist(closest, clickPoint) < maxDistance) return closest;
return null;
}
}
export default Canvas;
|