Skip to content

Commit 50ccb8b

Browse files
committed
#1240 - Fixes click events for touch devices
Details: - Moves mouseIsOnNode, getQuadNodes and getNodeAtPosition from bindEventHandlers to new private methods - Tweaks mouse events forged from TouchCaptor to make them identifiable - Updates createMouseListener so that it checks for node under the mouse when the event comes from the TouchCaptor
1 parent 3a6d26c commit 50ccb8b

File tree

2 files changed

+68
-51
lines changed

2 files changed

+68
-51
lines changed

src/core/captors/touch.ts

+4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ const DRAG_TIMEOUT = 200;
1313
const TOUCH_INERTIA_RATIO = 3;
1414
const TOUCH_INERTIA_DURATION = 200;
1515

16+
export type FakeSigmaMouseEvent = MouseEvent & { isFakeSigmaMouseEvent?: true };
17+
1618
/**
1719
* Event types.
1820
*/
@@ -79,6 +81,8 @@ export default class TouchCaptor extends Captor<TouchCaptorEvents> {
7981
ctrlKey: e.ctrlKey,
8082
});
8183

84+
(mouseEvent as FakeSigmaMouseEvent).isFakeSigmaMouseEvent = true;
85+
8286
(emitter || this.container).dispatchEvent(mouseEvent);
8387
}
8488

src/sigma.ts

+64-51
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ import { edgeLabelsToDisplayFromNodes, LabelGrid } from "./core/labels";
4040
import { Settings, DEFAULT_SETTINGS, validateSettings } from "./settings";
4141
import { INodeProgram } from "./rendering/webgl/programs/common/node";
4242
import { IEdgeProgram } from "./rendering/webgl/programs/common/edge";
43-
import TouchCaptor from "./core/captors/touch";
43+
import TouchCaptor, { FakeSigmaMouseEvent } from "./core/captors/touch";
4444
import { identity, multiplyVec2 } from "./utils/matrices";
4545
import { doEdgeCollideWithPoint, isPixelColored } from "./utils/edge-collisions";
4646

@@ -368,6 +368,62 @@ export default class Sigma extends TypedEventEmitter<SigmaEvents> {
368368
return this;
369369
}
370370

371+
/**
372+
* Method that checks whether or not a node collides with a given position.
373+
*/
374+
private mouseIsOnNode({ x, y }: Coordinates, { x: nodeX, y: nodeY }: Coordinates, size: number): boolean {
375+
return (
376+
x > nodeX - size &&
377+
x < nodeX + size &&
378+
y > nodeY - size &&
379+
y < nodeY + size &&
380+
Math.sqrt(Math.pow(x - nodeX, 2) + Math.pow(y - nodeY, 2)) < size
381+
);
382+
}
383+
384+
/**
385+
* Method that returns all nodes in quad at a given position.
386+
*/
387+
private getQuadNodes(position: Coordinates): string[] {
388+
const mouseGraphPosition = this.viewportToFramedGraph(position);
389+
390+
return this.quadtree.point(mouseGraphPosition.x, 1 - mouseGraphPosition.y);
391+
}
392+
393+
/**
394+
* Method that returns the closest node to a given position.
395+
*/
396+
private getNodeAtPosition(position: Coordinates): string | null {
397+
const { x, y } = position;
398+
const quadNodes = this.getQuadNodes(position);
399+
400+
// We will hover the node whose center is closest to mouse
401+
let minDistance = Infinity,
402+
nodeAtPosition = null;
403+
404+
for (let i = 0, l = quadNodes.length; i < l; i++) {
405+
const node = quadNodes[i];
406+
407+
const data = this.nodeDataCache[node];
408+
409+
const nodePosition = this.framedGraphToViewport(data);
410+
411+
const size = this.scaleSize(data.size);
412+
413+
if (!data.hidden && this.mouseIsOnNode(position, nodePosition, size)) {
414+
const distance = Math.sqrt(Math.pow(x - nodePosition.x, 2) + Math.pow(y - nodePosition.y, 2));
415+
416+
// TODO: sort by min size also for cases where center is the same
417+
if (distance < minDistance) {
418+
minDistance = distance;
419+
nodeAtPosition = node;
420+
}
421+
}
422+
}
423+
424+
return nodeAtPosition;
425+
}
426+
371427
/**
372428
* Method binding event handlers.
373429
*
@@ -382,62 +438,16 @@ export default class Sigma extends TypedEventEmitter<SigmaEvents> {
382438

383439
window.addEventListener("resize", this.activeListeners.handleResize);
384440

385-
// Function checking if the mouse is on the given node
386-
const mouseIsOnNode = (mouseX: number, mouseY: number, nodeX: number, nodeY: number, size: number): boolean => {
387-
return (
388-
mouseX > nodeX - size &&
389-
mouseX < nodeX + size &&
390-
mouseY > nodeY - size &&
391-
mouseY < nodeY + size &&
392-
Math.sqrt(Math.pow(mouseX - nodeX, 2) + Math.pow(mouseY - nodeY, 2)) < size
393-
);
394-
};
395-
396-
// Function returning the nodes in the mouse's quad
397-
const getQuadNodes = (mouseX: number, mouseY: number) => {
398-
const mouseGraphPosition = this.viewportToFramedGraph({ x: mouseX, y: mouseY });
399-
400-
// TODO: minus 1? lol
401-
return this.quadtree.point(mouseGraphPosition.x, 1 - mouseGraphPosition.y);
402-
};
403-
404441
// Handling mouse move
405442
this.activeListeners.handleMove = (e: MouseCoords): void => {
406-
// NOTE: for the canvas renderer, testing the pixel's alpha should
407-
// give some boost but this slows things down for WebGL empirically.
408-
409-
const quadNodes = getQuadNodes(e.x, e.y);
410-
411443
const baseEvent = {
412444
event: e,
413445
preventSigmaDefault(): void {
414446
this.event.preventSigmaDefault();
415447
},
416448
};
417449

418-
// We will hover the node whose center is closest to mouse
419-
let minDistance = Infinity,
420-
nodeToHover = null;
421-
422-
for (let i = 0, l = quadNodes.length; i < l; i++) {
423-
const node = quadNodes[i];
424-
425-
const data = this.nodeDataCache[node];
426-
427-
const pos = this.framedGraphToViewport(data);
428-
429-
const size = this.scaleSize(data.size);
430-
431-
if (!data.hidden && mouseIsOnNode(e.x, e.y, pos.x, pos.y, size)) {
432-
const distance = Math.sqrt(Math.pow(e.x - pos.x, 2) + Math.pow(e.y - pos.y, 2));
433-
434-
// TODO: sort by min size also for cases where center is the same
435-
if (distance < minDistance) {
436-
minDistance = distance;
437-
nodeToHover = node;
438-
}
439-
}
440-
}
450+
const nodeToHover = this.getNodeAtPosition(e);
441451

442452
if (nodeToHover && this.hoveredNode !== nodeToHover && !this.nodeDataCache[nodeToHover].hidden) {
443453
// Handling passing from one node to the other directly
@@ -457,7 +467,7 @@ export default class Sigma extends TypedEventEmitter<SigmaEvents> {
457467

458468
const size = this.scaleSize(data.size);
459469

460-
if (!mouseIsOnNode(e.x, e.y, pos.x, pos.y, size)) {
470+
if (!this.mouseIsOnNode(e, pos, size)) {
461471
const node = this.hoveredNode;
462472
this.hoveredNode = null;
463473

@@ -487,10 +497,13 @@ export default class Sigma extends TypedEventEmitter<SigmaEvents> {
487497
},
488498
};
489499

490-
if (this.hoveredNode)
500+
const isFakeSigmaMouseEvent = (e.original as FakeSigmaMouseEvent).isFakeSigmaMouseEvent;
501+
const nodeAtPosition = isFakeSigmaMouseEvent ? this.getNodeAtPosition(e) : this.hoveredNode;
502+
503+
if (nodeAtPosition)
491504
return this.emit(`${eventType}Node`, {
492505
...baseEvent,
493-
node: this.hoveredNode,
506+
node: nodeAtPosition,
494507
});
495508

496509
if (eventType === "wheel" ? this.settings.enableEdgeWheelEvents : this.settings.enableEdgeClickEvents) {

0 commit comments

Comments
 (0)