Skip to content

Commit fdb3894

Browse files
committedMay 23, 2018
Simplify implementation, reuse new api to remove old implementation
1 parent 2202eb8 commit fdb3894

File tree

3 files changed

+68
-182
lines changed

3 files changed

+68
-182
lines changed
 

‎client/paper/entry.js

+16-102
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import Matrix from 'node-matrices';
22
import { projectPoint } from '../utils';
33
import { fillQuadTex, fillTriTex } from './canvasUtils';
4-
import WhiskerFactory from './whisker';
4+
import Whisker from './whisker';
55

66
(function(workerContext) {
77
if (workerContext.paper) return;
@@ -12,15 +12,13 @@ import WhiskerFactory from './whisker';
1212
messageCallbacks[event.data.messageId](event.data.receiveData);
1313
});
1414

15-
const whiskerFactory = new WhiskerFactory(workerContext);
16-
1715
workerContext.paper = {
1816
get(name, data, callback) {
1917
if (name === 'whisker') {
20-
const whisker = whiskerFactory.createWhisker(data);
18+
const whisker = new Whisker(data);
2119

2220
if (callback) {
23-
whisker.then(callback);
21+
callback(whisker);
2422
return;
2523
}
2624
return whisker;
@@ -136,104 +134,20 @@ import WhiskerFactory from './whisker';
136134
callback,
137135
whiskerPointCallback,
138136
} = {}) => {
139-
// eslint-disable-next-line no-console
140-
console.warn('paper.whenPointsAt is deprecated, use the new whisker api instead');
141-
142-
whiskerLength = whiskerLength || 0.7;
143-
paperNumber = paperNumber || (await workerContext.paper.get('number'));
144-
requiredData = requiredData || [];
145-
const supporterCanvas = await workerContext.paper.get('supporterCanvas', { id: 'whisker' });
146-
const supporterCtx = supporterCanvas.getContext('2d');
147-
let pointAtData = null;
148-
let lastWhiskerEnd = null;
149-
150-
// Adapted from https://stackoverflow.com/questions/9043805/test-if-two-lines-intersect-javascript-function
151-
function intersects(v1, v2, v3, v4) {
152-
const det = (v2.x - v1.x) * (v4.y - v3.y) - (v4.x - v3.x) * (v2.y - v1.y);
153-
if (det === 0) {
154-
return false;
155-
} else {
156-
const lambda = ((v4.y - v3.y) * (v4.x - v1.x) + (v3.x - v4.x) * (v4.y - v1.y)) / det;
157-
const gamma = ((v1.y - v2.y) * (v4.x - v1.x) + (v2.x - v1.x) * (v4.y - v1.y)) / det;
158-
return 0 < lambda && lambda < 1 && (0 < gamma && gamma < 1);
159-
}
137+
const whisker = new Whisker({
138+
direction,
139+
whiskerLength,
140+
paperNumber,
141+
requiredData,
142+
});
143+
144+
if (callback) {
145+
whisker.on('paperAdded', callback);
146+
whisker.on('paperRemoved', () => callback(null));
160147
}
161-
function intersectsPaper(whiskerStart, whiskerEnd, paper) {
162-
return (
163-
(intersects(whiskerStart, whiskerEnd, paper.points.topLeft, paper.points.topRight) ||
164-
intersects(whiskerStart, whiskerEnd, paper.points.topRight, paper.points.bottomRight) ||
165-
intersects(whiskerStart, whiskerEnd, paper.points.bottomRight, paper.points.bottomLeft) ||
166-
intersects(whiskerStart, whiskerEnd, paper.points.bottomLeft, paper.points.topLeft)) &&
167-
requiredData.every(name => paper.data[name] !== undefined)
168-
);
169-
}
170-
171-
setInterval(async () => {
172-
const papers = await workerContext.paper.get('papers');
173-
const points = papers[paperNumber].points;
174-
175-
let segment = [points.topLeft, points.topRight];
176-
if (direction === 'right') segment = [points.topRight, points.bottomRight];
177-
if (direction === 'down') segment = [points.bottomRight, points.bottomLeft];
178-
if (direction === 'left') segment = [points.bottomLeft, points.topLeft];
179-
180-
const whiskerStart = {
181-
x: (segment[0].x + segment[1].x) / 2,
182-
y: (segment[0].y + segment[1].y) / 2,
183-
};
184-
const whiskerEnd = {
185-
x: whiskerStart.x + (segment[1].y - segment[0].y) * whiskerLength,
186-
y: whiskerStart.y - (segment[1].x - segment[0].x) * whiskerLength,
187-
};
188-
189-
if (
190-
!pointAtData ||
191-
!papers[pointAtData.paperNumber] ||
192-
// Try keeping `pointAtData` stable if possible.
193-
!intersectsPaper(whiskerStart, whiskerEnd, papers[pointAtData.paperNumber])
194-
) {
195-
let newPointAtData = null;
196-
Object.keys(papers).forEach(otherPaperNumber => {
197-
if (otherPaperNumber === paperNumber) return;
198-
if (intersectsPaper(whiskerStart, whiskerEnd, papers[otherPaperNumber])) {
199-
newPointAtData = { paperNumber: otherPaperNumber, paper: papers[otherPaperNumber] };
200-
}
201-
});
202-
if (newPointAtData !== pointAtData) {
203-
pointAtData = newPointAtData;
204-
if (callback) callback(pointAtData);
205-
}
206-
}
207148

208-
supporterCtx.clearRect(0, 0, supporterCanvas.width, supporterCanvas.height);
209-
supporterCtx.fillStyle = supporterCtx.strokeStyle = pointAtData
210-
? 'rgb(0, 255, 0)'
211-
: 'rgb(255, 0, 0)';
212-
supporterCtx.beginPath();
213-
supporterCtx.moveTo(whiskerStart.x, whiskerStart.y);
214-
supporterCtx.lineTo(whiskerEnd.x, whiskerEnd.y);
215-
supporterCtx.stroke();
216-
217-
const dotFraction = (Date.now() / 600) % 1;
218-
supporterCtx.beginPath();
219-
supporterCtx.arc(
220-
whiskerEnd.x * dotFraction + whiskerStart.x * (1 - dotFraction),
221-
whiskerEnd.y * dotFraction + whiskerStart.y * (1 - dotFraction),
222-
2,
223-
0,
224-
2 * Math.PI
225-
);
226-
supporterCtx.fill();
227-
supporterCtx.commit();
228-
229-
if (
230-
!lastWhiskerEnd ||
231-
lastWhiskerEnd.x !== whiskerEnd.x ||
232-
lastWhiskerEnd.y !== whiskerEnd.y
233-
) {
234-
lastWhiskerEnd = whiskerEnd;
235-
if (whiskerPointCallback) whiskerPointCallback(whiskerEnd.x, whiskerEnd.y);
236-
}
237-
}, 10);
149+
if (whiskerPointCallback) {
150+
whisker.on('whiskerMoved', whiskerPointCallback);
151+
}
238152
};
239153
})(self);

‎client/paper/whisker.js

+52-64
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,48 @@
1+
/*global paper, Promise*/
12
import EventEmitter from 'events';
3+
import uniqueId from 'lodash/uniqueId';
24

3-
export default class WhiskerFactory {
4-
constructor(workerContext) {
5-
this.whiskers = [];
6-
this.workerContext = workerContext;
7-
this.update = this.update.bind(this);
8-
}
9-
10-
async createWhisker(options) {
11-
const whisker = new Whisker({
12-
...options,
13-
paperNumber: options.paperNumber || (await this.workerContext.paper.get('number')),
14-
workerContext: this.workerContext,
15-
whiskerFactory: this,
16-
});
17-
18-
if (this.whiskers.length === 0) {
19-
setInterval(this.update, 10);
20-
}
21-
22-
if (!this.canvas) {
23-
this.canvas = await this.workerContext.paper.get('supporterCanvas', { id: 'whisker' });
24-
this.ctx = this.canvas.getContext('2d');
25-
}
26-
27-
this.whiskers.push(whisker);
28-
29-
return whisker;
30-
}
31-
32-
async destroyWhisker(whisker) {
33-
this.whiskers = this.whiskers.filter(other => other !== whisker);
34-
35-
if (this.whiskers.length === 0) {
36-
clearInterval(this.update);
37-
}
38-
}
39-
40-
async update() {
41-
const papers = await this.workerContext.paper.get('papers');
42-
43-
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
44-
this.whiskers.forEach(whisker => whisker.update(papers, this.ctx));
45-
this.ctx.commit();
46-
}
47-
}
48-
49-
class Whisker extends EventEmitter {
5+
export default class Whisker extends EventEmitter {
506
constructor({
51-
whiskerFactory,
52-
paperNumber,
7+
paperNumber = paper.get('number'),
538
direction = 'up',
549
whiskerLength = 0.7,
5510
requiredData = [],
5611
color = 'rgb(255, 0, 0)',
5712
}) {
5813
super();
59-
60-
this.paperNumber = paperNumber;
6114
this.direction = direction;
6215
this.whiskerLength = whiskerLength;
6316
this.requiredData = requiredData;
6417
this.color = color;
6518

6619
this._pointAtData = null;
6720
this._lastWhiskerEnd = null;
68-
this._whiskerFactory = whiskerFactory;
21+
22+
this.update = this.update.bind(this);
23+
24+
Promise.all([
25+
paper.get('supporterCanvas', { id: uniqueId('whiskerCanvas') }),
26+
paperNumber,
27+
]).then(([canvas, number]) => {
28+
if (this._destroyed) {
29+
return;
30+
}
31+
32+
this._canvas = canvas;
33+
this._ctx = canvas.getContext('2d');
34+
this.paperNumber = number;
35+
36+
this._updateInterval = setInterval(this.update, 10);
37+
});
6938
}
7039

71-
update(papers, ctx) {
40+
async update() {
41+
if (!this._ctx) {
42+
return;
43+
}
44+
45+
const papers = await paper.get('papers');
7246
const points = papers[this.paperNumber].points;
7347

7448
let segment = [points.topLeft, points.topRight];
@@ -93,7 +67,7 @@ class Whisker extends EventEmitter {
9367
) {
9468
let newPointAtData = null;
9569
Object.keys(papers).forEach(otherPaperNumber => {
96-
if (otherPaperNumber === this.paperNumber) return;
70+
if (otherPaperNumber == this.paperNumber.toString()) return;
9771
if (this._intersectsPaper(whiskerStart, whiskerEnd, papers[otherPaperNumber])) {
9872
newPointAtData = { paperNumber: otherPaperNumber, paper: papers[otherPaperNumber] };
9973
}
@@ -111,26 +85,26 @@ class Whisker extends EventEmitter {
11185
}
11286

11387
// render whisker with dot animation
114-
ctx.clearRect(0, 0, ctx.width, ctx.height);
115-
ctx.fillStyle = ctx.strokeStyle = this.color;
116-
ctx.beginPath();
117-
ctx.moveTo(whiskerStart.x, whiskerStart.y);
118-
ctx.lineTo(whiskerEnd.x, whiskerEnd.y);
119-
ctx.stroke();
88+
this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
89+
this._ctx.fillStyle = this._ctx.strokeStyle = this.color;
90+
this._ctx.beginPath();
91+
this._ctx.moveTo(whiskerStart.x, whiskerStart.y);
92+
this._ctx.lineTo(whiskerEnd.x, whiskerEnd.y);
93+
this._ctx.stroke();
12094

12195
const dotFraction = Math.abs(Math.sin(Date.now() / 600));
12296

12397
// only show dot when whisker is connected to other paper
12498
if (this._pointAtData) {
125-
ctx.beginPath();
126-
ctx.arc(
99+
this._ctx.beginPath();
100+
this._ctx.arc(
127101
whiskerEnd.x * dotFraction + whiskerStart.x * (1 - dotFraction),
128102
whiskerEnd.y * dotFraction + whiskerStart.y * (1 - dotFraction),
129103
2,
130104
0,
131105
2 * Math.PI
132106
);
133-
ctx.fill();
107+
this._ctx.fill();
134108
}
135109

136110
if (
@@ -141,6 +115,8 @@ class Whisker extends EventEmitter {
141115
this._lastWhiskerEnd = whiskerEnd;
142116
this.emit('whiskerMoved', { x: Math.round(whiskerEnd.x), y: Math.round(whiskerEnd.y) });
143117
}
118+
119+
this._ctx.commit();
144120
}
145121

146122
_intersectsPaper(whiskerStart, whiskerEnd, paper) {
@@ -154,7 +130,19 @@ class Whisker extends EventEmitter {
154130
}
155131

156132
destroy() {
157-
this._whiskerFactory.destroyWhisker(this);
133+
this._destroyed = true;
134+
135+
if (this._ctx) {
136+
this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
137+
this._ctx.commit();
138+
this._ctx = null;
139+
}
140+
141+
// TODO: cleanup canvas, currently not possible to destroy requested canvas
142+
143+
if (this._updateInterval !== undefined) {
144+
clearInterval(this._updateInterval);
145+
}
158146
}
159147
}
160148

‎docs/api.md

-16
Original file line numberDiff line numberDiff line change
@@ -180,22 +180,6 @@ If you don't need a whisker any more you can remove it:
180180
whisker.destroy()
181181
```
182182

183-
*deprecated* old api:
184-
185-
```js
186-
paper.whenPointsAt({
187-
callback: ({paperNumber, paper}) => {
188-
/* do something */
189-
},
190-
191-
// optional
192-
direction, // "up" (default), "down", "left", "right"
193-
whiskerLength, // as fraction of the side (default 0.7)
194-
requiredData, // array of data fields that must be present in the other paper
195-
paperNumber, // paper number to do this for (default is own paper number)
196-
});
197-
```
198-
199183
## Camera Access
200184

201185
You can draw a region of the camera picture to another destination region (using arbitrary quadrilaterals).

0 commit comments

Comments
 (0)