'use strict';
let Rac = require('../Rac');
let utils = require('../util/utils');
/**
* Container for information regarding the currently selected `Control`.
*
* @alias Rac.Controller.Selection
*/
class ControlSelection{
constructor(control, pointerCenter) {
// Selected control instance.
this.control = control;
// Copy of the control anchor, so that the control can move tied to
// the drawing, while the interaction range remains fixed.
this.anchorCopy = control.copyAnchor();
// Segment from the captured pointer position to the contro center,
// used to attach the control to the point where interaction started.
// Pointer is at `segment.start` and control center is at `segment.end`.
this.pointerOffset = pointerCenter.segmentToPoint(control.center());
}
drawSelection(pointerCenter) {
this.control.drawSelection(pointerCenter, this.anchorCopy, this.pointerOffset);
}
}
/**
* The `Controller` is the object that manages the control system for an
* instance of `Rac`.
*
* The instance contains the `pointerStyle` and settings shared between all
* controls like the `knobRadius`. It also keeps the list of all managed
* controls and the currenty selected control.
*
* ⚠️ The API for controls is **planned to change** in a future minor release. ⚠️
*
* @alias Rac.Controller
*/
class Controller {
static Selection = ControlSelection;
/**
* Builds a new `Controller` with the given `Rac` instance.
*/
constructor(rac) {
/**
* Instance of `Rac` used for drawing and passed along to any created
* object.
*
* @type {Rac}
*/
this.rac = rac;
/**
* Distance at which the pointer is considered to interact with a
* control knob. Also used by controls for drawing.
*
* @type {number}
*/
this.knobRadius = 22;
// Collection of all controls that are drawn with `drawControls()`
// and evaluated for selection with the `pointer...()` functions.
this.controls = [];
// TODO: separate lastControl from lastPointer
// Last `Point` of the position when the pointer was pressed, or last
// Control interacted with. Set to `null` when there has been no
// interaction yet and while there is a selected control.
this.lastPointer = null;
// Style used for visual elements related to selection and pointer
// interaction.
this.pointerStyle = null;
/**
* Selection information for the currently selected control, or `null`
* when there is no selection.
* @type {?Rac.Controller.Selection}
*/
this.selection = null;
} // constructor
// Call to signal the pointer being pressed. If the ponter hits a control
// it will be considered selected. When a control is selected a copy of its
// anchor is stored as to allow interaction with a fixed anchor.
pointerPressed(pointerCenter) {
this.lastPointer = null;
// Test pointer hit
const selected = this.controls.find( item => {
const controlCenter = item.center();
if (controlCenter === null) { return false; }
if (controlCenter.distanceToPoint(pointerCenter) <= this.knobRadius) {
return true;
}
return false;
});
if (selected === undefined) {
return;
}
this.selection = new Controller.Selection(selected, pointerCenter);
}
// Call to signal the pointer being dragged. As the pointer moves the
// selected control is updated with a new `distance`.
pointerDragged(pointerCenter){
if (this.selection === null) {
return;
}
let control = this.selection.control;
let anchorCopy = this.selection.anchorCopy;
// Center of dragged control in the pointer current position
let currentPointerControlCenter = this.selection.pointerOffset
.withStartPoint(pointerCenter)
.endPoint();
control.updateWithPointer(currentPointerControlCenter, anchorCopy);
}
// Call to signal the pointer being released. Upon release the selected
// control is cleared.
pointerReleased(pointerCenter) {
if (this.selection === null) {
this.lastPointer = pointerCenter;
return;
}
this.lastPointer = this.selection.control;
this.selection = null;
}
// Draws controls and the visuals of pointer and control selection. Usually
// called at the end of `draw` so that controls sits on top of the drawing.
drawControls() {
let pointerStyle = this.pointerStyle;
// Last pointer or control
if (this.lastPointer instanceof Rac.Point) {
this.lastPointer.arc(12).draw(pointerStyle);
}
if (this.lastPointer instanceof Rac.Control) {
// TODO: implement last selected control state
}
// Pointer pressed
let pointerCenter = this.rac.Point.pointer();
if (this.rac.drawer.p5.mouseIsPressed) {
if (this.selection === null) {
pointerCenter.arc(10).draw(pointerStyle);
} else {
pointerCenter.arc(5).draw(pointerStyle);
}
}
// All controls in display
this.controls.forEach(item => item.draw());
if (this.selection !== null) {
this.selection.drawSelection(pointerCenter);
}
}
} // class Controller
module.exports = Controller;