plotboilerplate
Version:
A simple javascript plotting boilerplate for 2d stuff.
756 lines (705 loc) • 32.1 kB
text/typescript
/**
* Interfaces and class for automatically handling Bézier curves.
*
* @requires AlloyFinger
* @requires BezierPath
* @requires CubicBezierPath
* @requires KeyHandler
* @requires MouseHandler
* @requires PlotBoilerplate
* @requires VertEvent
* @requires Vertex
* @requires XMouseEvent
* @requires XYCoords
*
*
* @author Ikaros Kappler
* @date 2020-07-31
* @modified 2020-08-03 Ported this class from vanilla JS to Typescript.
* @modified 2020-08-12 Added a distance check before handling the click/tap event.
* @modified 2021-01-03 Changed property to `autoAdjustPaths` in the HandlerOptions interface (typo).
* @modified 2021-01-03 Added following new functions: `addPathVertexDragStartListeners`, `removePathVertexDragStartListeners`, `addPathVertexDragEndListeners` and `removePathVertexDragEndListeners`.
* @modified 2021-03-31 Fixed the issue with the new AlloyFinger (Typescript).
* @modified 2022-02-03 Changing the element to catch events (eventCatcher instead of canvas).
* @modified 2024-03-10 Fixing some types for Typescript 5 compatibility.
* @modified 2025-04-14 Added the `BezierPathInteractionHelper.drawHandleLines` method.
* @modified 2025-04-14 Fixing correct event types for touch events in `BezierPathInteractionHelper`.
* @modified 2025-04-14 BezierPathInteractionHelper: Changed default value of `HelperOptions.autoAdjustPaths` from `true` to `false`.
* @modified 2025-05-05 Added optional params `draw` and `fill` to BezierPathInteractionHelper.drawHandleLines` method.
* @modified 2025-05-05 Class `BezierPathInteractionHelper` now implementing `IShapeInteractionHelper`.
* @modified 2025-05-07 Tweaking performance of `BezierPathInteractionHelper`: only triggering redraw now when mouse move within given detect range.
* @version 1.2.1
*
* @file BezierPathInteractionHelper
* @public
**/
// I would like to use AlloyFinger from the node_modules, but it seems
// AlloyTeam has forgotten to publish their d.ts file.
import AlloyFinger from "alloyfinger-typescript";
import { BezierPath } from "../../BezierPath";
import { CubicBezierCurve } from "../../CubicBezierCurve";
import { KeyHandler } from "../../KeyHandler";
import { MouseHandler } from "../../MouseHandler";
import { PlotBoilerplate } from "../../PlotBoilerplate";
import { VertListener } from "../../VertexListeners";
import { Vertex } from "../../Vertex";
import { XMouseEvent } from "../../MouseHandler";
import { DrawLib, IShapeInteractionHelper, XYCoords } from "../../interfaces";
import { AFTouchEvent } from "alloyfinger-typescript/src/cjs/alloy_finger";
/**
* Handler type for mouse-pointer-moved listeners.
*/
type OnPointerMoved = (pathIndex: number, pathPoint: Vertex | null, pointerPos: Vertex | null, t: number) => void;
/**
* Handler type for vertex-inserted listeners.
*/
type OnVertexInserted = (pathIndex: number, insertIndex: number, newPath: BezierPath, oldPath: BezierPath) => void;
/**
* Handler type for vertex-removed listeners.
*/
type OnVerticesDeleted = (
pathIndex: number,
removedVertexIndices: Array<number>,
newPath: BezierPath,
oldPath: BezierPath
) => void;
/**
* Handler type for path-removed listeners.
*/
type OnPathRemoved = (pathIndex: number, oldPath: BezierPath) => void;
/**
* Options passed to the constructor.
*/
interface HelperOptions {
autoAdjustPaths?: boolean; // default true
allowPathRemoval?: boolean; // default true
maxDetectDistance?: number; // default Number.MAX_VALUE
onPointerMoved?: OnPointerMoved;
onVertexInserted?: OnVertexInserted;
onVerticesDeleted?: OnVerticesDeleted;
onPathRemoved?: OnPathRemoved;
}
/**
* @classdesc A helper for adding vertices to and remove vertices from Bézier paths.
* By default the 'delete' key is used to remove vertices or paths.
*
* For convenience this helper is capable of handling multiple paths which are kept
* in an array.
*
* [Demo](https://www.plotboilerplate.io/repo/demos/23-bezier-point-distance/ "Demo")
*
* @public
**/
export class BezierPathInteractionHelper implements IShapeInteractionHelper {
/**
* @member {PlotBoilerplate} pb
* @memberof BezierPathInteractionHelper
* @type {PlotBoilerplate}
* @instance
*/
private pb: PlotBoilerplate;
private paths: Array<BezierPath>;
private onPointerMoved: OnPointerMoved;
private onVertexInserted: OnVertexInserted;
private onVerticesDeleted: OnVerticesDeleted;
private onPathRemoved: OnPathRemoved;
private autoAdjustPaths: boolean;
private allowPathRemoval: boolean;
private maxDetectDistance: number;
private mouseIsOver: boolean;
private currentPathIndex: number;
private currentDistance: number;
private currentT: number;
private currentA: Vertex; // Position on the curve
private currentB: Vertex; // mouse/touch position
private _mouseHandler: MouseHandler;
private _touchHandler: AlloyFinger;
private _keyHandler: KeyHandler;
private _mouseEnterListener: () => void;
private _mouseLeaveListener: () => void;
/**
* Pre: all paths must have been added to the PlotBoilerplate's drawable buffer (use the add(Drawable) function).
*
* The move callback accepts four params:
* * The point on the closest curve (Vertex)
* * The mouse or touch position (Vertex)
* * The curve position (float t)
* * The curve index on the array (integer)
*
*
* @constructor
* @name BezierPathInteractionHelper
* @param {PlotBoilerplate} pb
* @param {Array<BezierPath>} paths
* @param {boolean} options.autoAdjustPaths - If true then inner path points will be auto-adjusted to keep the curve smooth.
* @param {boolean} options.allowPathRemoval - If true then full paths can be removed (by removing selected vertices).
* @param {number} maxDetectDistance - The max detection distance. No events will be fired if the mouse/touch pointer is outside this range (default is Number.MAX_VALUE).
* @param {function(number,Vertex,Vertex,number)} options.onPointerMoved (pathIndex,pathPoint,pointer,t)
* @param {function(number,number,BezierPath,BezierPath)} options.onVertexInserted (pathIndex,insertIndex,newPath,oldPath)
* @param {function(number,number[],BezierPath,BezierPath)} options.onVerticesDeleted (pathIndex,removedVertexIndices,newPath,oldPath)
* @param {function(number,BezierPath)} options.onPathRemoved (pathIndex,oldPath)
**/
constructor(pb: PlotBoilerplate, paths: Array<BezierPath>, options: HelperOptions) {
options = options || {};
this.pb = pb;
this.paths = [];
this.onPointerMoved =
typeof options.onPointerMoved === "function"
? options.onPointerMoved
: (i: number, a: Vertex | null, b: Vertex | null, t: number) => {};
this.onVertexInserted =
typeof options.onVertexInserted === "function"
? options.onVertexInserted
: (i: number, j: number, n: BezierPath, o: BezierPath) => {};
this.onVerticesDeleted =
typeof options.onVerticesDeleted === "function"
? options.onVerticesDeleted
: (i: number, r: Array<number>, n: BezierPath, o: BezierPath) => {};
this.onPathRemoved = typeof options.onPathRemoved === "function" ? options.onPathRemoved : (i: number, o: BezierPath) => {};
this.autoAdjustPaths = typeof options.autoAdjustPaths === "boolean" ? options.autoAdjustPaths : false; // true;
this.allowPathRemoval = typeof options.allowPathRemoval === "boolean" ? options.allowPathRemoval : true;
this.maxDetectDistance = typeof options.maxDetectDistance === "number" ? options.maxDetectDistance : Number.MAX_VALUE;
this.mouseIsOver = false;
this.currentPathIndex = -1;
this.currentDistance = Number.MAX_VALUE;
this.currentT = 0.0;
this.currentA = new Vertex(0, 0); // Position on the curve
this.currentB = new Vertex(0, 0); // Mouse/Touch position
// Rebuild the array to avoid outside manipulations.
for (var i = 0; i < paths.length; i++) {
this.addPath(paths[i]);
}
this._mouseEnterListener = () => {
this.mouseIsOver = true;
};
this._mouseLeaveListener = () => {
this.mouseIsOver = false;
this._clearMoveEvent();
};
this._mouseHandler = this._installMouseListener();
this._touchHandler = this._installTouchListener();
this._keyHandler = this._installKeyListener();
// Paths might have changed by auto-adjustment.
if (this.autoAdjustPaths) {
pb.redraw();
}
}
/**
* Manually add a path to this helper.
* Note that if `autoAdjustPaths==true` then listeners will be installed to the path's vertices to
* keep the path smooth at all times.
*
* @method addPath
* @instance
* @memberof BezierPathInteractionHelper
* @param {BezierPath} path - The path to add.
* @return {boolean} Duplicate path instances cannot be added; function will return false if path already exists.
**/
addPath(path: BezierPath): boolean {
const pathIndex: number = this._locatePath(path);
if (pathIndex != -1) return false;
this.paths.push(path);
if (this.autoAdjustPaths) BezierPathInteractionHelper.setPathAutoAdjust(path);
return true;
}
/**
* Manually remove a path from this helper.
* Note that this method ignores the `allowPathRemoval` option.
*
* @method removePath
* @instance
* @memberof BezierPathInteractionHelper
* @param {BezierPath} path - The path to remove.
* @return {boolean} Returns false if the path could not be found.
**/
removePath(path: BezierPath): boolean {
const pathIndex: number = this._locatePath(path);
if (pathIndex == -1) return false;
this.removePathAt(pathIndex);
return true;
}
/**
* Remove the path at the given index.
*
* @method removePathAt
* @instance
* @memberof BezierPathInteractionHelper
* @param {number} pathIndex - The index of the path (must be inside bounds, see `this.paths` array).
* @return {void}
**/
removePathAt(pathIndex: number): void {
const path: BezierPath = this.paths[pathIndex];
this.paths = this.paths.filter((value: BezierPath, index: number) => index != pathIndex);
this._removeDefaultPathListeners(path);
this.pb.remove(path, false, true); // Remove with vertices
this.onPathRemoved(pathIndex, path);
}
/**
* Update the inner status by running the distance calculation again with the current settings.
*
* Call this if any of the properties changed (like maxDetecDistance).
*
* @method update
* @instance
* @memberof BezierPathInteractionHelper
* @return {void}
**/
update(): void {
// Just re-run the calculation with the recent mouse/touch position
this._handleMoveEvent(this.currentB.x, this.currentB.y);
}
/**
* Draw grey handle lines.
*
* @param {DrawLib<any>} draw - (optional) The draw library to use. If not provided then `pb.draw` will be used.
* @param {DrawLib<any>} fill - (optional) The fill library to use. If not provided then `pb.fill` will be used.
*/
drawHandleLines(draw?: DrawLib<any>, fill?: DrawLib<any>) {
draw = draw || this.pb.draw;
fill = fill || this.pb.fill;
this.paths.forEach((path: BezierPath) => {
path.bezierCurves.forEach((curve: CubicBezierCurve) => {
this.pb.draw.line(curve.startPoint, curve.startControlPoint, "rgba(64,192,128,0.333)", 1.0, {
dashOffset: 0.0,
dashArray: [4, 2]
});
this.pb.draw.line(curve.endPoint, curve.endControlPoint, "rgba(64,192,128,0.333)", 1.0, {
dashOffset: 0.0,
dashArray: [4, 2]
});
});
});
}
/**
* This function should invalidate any installed listeners and invalidate this object.
* After calling this function the object might not hold valid data any more and
* should not be used any more.
*
* @method destroy
* @instance
* @memberof BezierPathInteractionHelper
* @return {void}
**/
destroy(): void {
for (var i = 0; i < this.paths.length; i++) {
this._removeDefaultPathListeners(this.paths[i]);
// removePathVertexDragStartListeners(this.paths[i], listener);
}
this.paths = [];
this.pb.eventCatcher.removeEventListener("mouseenter", this._mouseEnterListener);
this.pb.eventCatcher.removeEventListener("mouseleave", this._mouseLeaveListener);
this._mouseHandler.destroy();
this._keyHandler.destroy();
}
// +---------------------------------------------------------------------------------
// | A helper function to locate a given path instance inside the array.
// |
// | @return The index of the path or -1 if not found.
// +-------------------------------
private _locatePath(path: BezierPath): number {
for (var i = 0; i < this.paths.length; i++) {
if (this.paths[i] == path) return i;
}
return -1;
}
// +---------------------------------------------------------------------------------
// | Handle deletion of any selecte vertex and/or paths.
// | Note that this function will trigger a `redraw`.
// +-------------------------------
private _handleDelete(): void {
const pathDeleteIndices: Array<number> = this._handleSingleVertexDelete();
// Remove enqueued paths
if (this.allowPathRemoval) {
// Remove paths starting with the last (!) index.
for (var i = pathDeleteIndices.length - 1; i >= 0; i--) {
this.removePathAt(pathDeleteIndices[i]);
}
}
this.pb.redraw();
}
// +---------------------------------------------------------------------------------
// | This function removes all selected vertices on the paths without deleting
// | full paths (at least two path vertices remaining).
// |
// | Returned (sorted) array contains indices of those paths that should
// | be deleted completely.
// +-------------------------------
private _handleSingleVertexDelete(): Array<number> {
// Check all path points (on all paths) for deletion.
// Note: whole paths are not meant to be removed this way.
// Keep track of their indices (ascending order) for later removal.
const pathDeleteIndices: Array<number> = [];
for (var p = 0; p < this.paths.length; p++) {
const allVerticesSelected: boolean = this._handleDeleteOnPath(p);
if (allVerticesSelected) pathDeleteIndices.push(p);
}
return pathDeleteIndices;
}
// +---------------------------------------------------------------------------------
// | This function removes all selected vertices on the given path (index)
// | without deleting the full path (at least two path vertices remaining).
// |
// | Returns true if path should be fully removed, false otherwise.
// +-------------------------------
private _handleDeleteOnPath(pathIndex: number): boolean {
const path: BezierPath = this.paths[pathIndex];
const newCurves: Array<Array<Vertex>> = [];
const deletedVertIndices: Array<number> = [];
// Find first non-selected path point
let curveIndex: number = 0;
while (curveIndex < path.bezierCurves.length && path.bezierCurves[curveIndex].startPoint.attr.isSelected) {
deletedVertIndices.push(curveIndex), curveIndex++;
}
// All points selected? Enqueue for deletion.
if (curveIndex == path.bezierCurves.length) {
// Indicate: path removal required.
return true;
}
// Only keep those curves that have no selected path point (=delete selected)
let curStart: Vertex = path.bezierCurves[curveIndex].startPoint;
let curStartControl: Vertex = path.bezierCurves[curveIndex].startControlPoint;
for (var i = curveIndex; i < path.bezierCurves.length; i++) {
if (!path.bezierCurves[i].endPoint.attr.isSelected) {
newCurves.push([
curStart.clone(),
path.bezierCurves[i].endPoint.clone(),
curStartControl.clone(),
path.bezierCurves[i].endControlPoint.clone()
]);
if (i + 1 < path.bezierCurves.length) {
curStart = path.bezierCurves[i].endPoint;
curStartControl = path.bezierCurves[i + 1].startControlPoint;
}
} else {
// TODO: remove drag listener from removed vertex!
deletedVertIndices.push(i);
}
}
// Do not remove the whole path.
// Do not replace the path if no vertices were deleted.
if (newCurves.length != 0 && newCurves.length != path.bezierCurves.length) {
const newPath: BezierPath = BezierPath.fromArray(newCurves);
const oldPath: BezierPath = this.paths[pathIndex];
this._replacePathAt(pathIndex, newPath);
this.onVerticesDeleted(pathIndex, deletedVertIndices, newPath, oldPath);
// Indicate: no path removal required
return false;
} else {
// Indicate full path removal if no curve would be left.
return newCurves.length == 0;
}
}
// +---------------------------------------------------------------------------------
// | This function replaces a path at the given index with a new one (after change
// | of vertex count).
// +-------------------------------
private _replacePathAt(pathIndex: number, newPath: BezierPath): void {
const oldPath: BezierPath = this.paths[pathIndex];
this.pb.remove(oldPath, false, true); // Remove with vertices
oldPath.destroy();
this._removeDefaultPathListeners(oldPath);
BezierPathInteractionHelper.setPathAutoAdjust(newPath);
this.paths[pathIndex] = newPath;
this.pb.add(newPath);
}
// +---------------------------------------------------------------------------------
// | Touch and mouse events should call this fuction when the pointer was moved.
// +-------------------------------
private _handleMoveEvent(posX: number, posY: number): void {
const oldA = this.currentA.clone();
const oldB = this.currentB.clone();
const oldDistance = this.currentDistance;
const point: XYCoords = this.pb.transformMousePosition(posX, posY);
this.currentB.set(point);
this._updateMinDistance();
// Always fire even if nothing visually changed?
if (this.currentDistance <= this.maxDetectDistance && this.mouseIsOver && this.pb.getDraggedElementCount() == 0) {
this.onPointerMoved(this.currentPathIndex, this.currentA, this.currentB, this.currentT);
} else {
this.onPointerMoved(-1, null, null, 0.0);
}
// Only redraw when moving inside the detection distance
if (
(this.currentDistance <= this.maxDetectDistance ||
(this.currentDistance > this.maxDetectDistance && oldDistance <= this.maxDetectDistance)) &&
(!oldA.equals(this.currentA) || !oldB.equals(this.currentB))
) {
this.pb.redraw();
}
}
// +---------------------------------------------------------------------------------
// | This is called when the mouse pointer leaves the canvas or
// | when the touch progress ends.
// +-------------------------------
private _clearMoveEvent(): void {
this.onPointerMoved(-1, null, null, 0.0);
this.pb.redraw();
}
// +---------------------------------------------------------------------------------
// | Called once upon initialization.
// +-------------------------------
private _installTouchListener(): AlloyFinger {
var _self: BezierPathInteractionHelper = this;
const afProps = {
touchStart: function (_event: AFTouchEvent<"touchStart">) {
_self.mouseIsOver = true;
},
touchMove: function (event: AFTouchEvent<"touchMove">) {
if (_self.pb.getDraggedElementCount() == 0 && event.touches.length > 0) {
_self._handleMoveEvent(event.touches[0].clientX, event.touches[0].clientY);
}
},
touchEnd: function (_event: AFTouchEvent<"touchEnd">) {
_self.mouseIsOver = false;
_self._clearMoveEvent();
}
};
if (window["createAlloyFinger" as keyof Object]) {
return (window["createAlloyFinger" as keyof Object] as any)(
this.pb.eventCatcher ? this.pb.eventCatcher : this.pb.eventCatcher,
afProps
);
} else {
return new AlloyFinger(this.pb.eventCatcher ? this.pb.eventCatcher : this.pb.eventCatcher, afProps);
}
}
// +---------------------------------------------------------------------------------
// | Called once upon initialization.
// +-------------------------------
private _installMouseListener(): MouseHandler {
var _self: BezierPathInteractionHelper = this;
var mouseHandler = new MouseHandler(this.pb.eventCatcher)
.up(function (e: XMouseEvent) {
if (e.params.wasDragged) return;
if (_self._keyHandler.isDown("shift")) return;
if (_self.currentDistance > _self.maxDetectDistance || !_self.mouseIsOver) return;
const path: BezierPath = _self.paths[_self.currentPathIndex];
const vertex: Vertex | undefined = _self.pb.getVertexNear(e.params.pos, PlotBoilerplate.DEFAULT_CLICK_TOLERANCE);
if (vertex) return;
// Check if there is already a path point at the given split position
const pathPoint: Vertex = path.getPointAt(_self.currentT);
const pointNear: Vertex | undefined = _self.pb.getVertexNear(_self.pb.revertMousePosition(pathPoint.x, pathPoint.y), 6.0);
if (pointNear) {
for (var i = 0; i < path.bezierCurves.length; i++) {
if (
path.bezierCurves[i].startPoint.distance(pointNear) <= 6.0 ||
path.bezierCurves[i].endPoint.distance(pointNear) <= 6.0
) {
// console.log("There is already a path point near this position.");
return;
}
}
}
//console.log('Inserting vertex at', _self.currentT );
const leftPath: BezierPath = path.getSubPathAt(0.0, _self.currentT);
const rightPath: BezierPath = path.getSubPathAt(_self.currentT, 1.0);
const newCurves: Array<CubicBezierCurve> = [];
for (var i = 0; i < leftPath.bezierCurves.length; i++) {
newCurves.push(leftPath.bezierCurves[i]);
}
for (var i = 0; i < rightPath.bezierCurves.length; i++) {
newCurves.push(rightPath.bezierCurves[i]);
}
const newPath: BezierPath = BezierPath.fromArray(newCurves);
const oldPath: BezierPath = _self.paths[_self.currentPathIndex];
_self._replacePathAt(_self.currentPathIndex, newPath);
_self.onVertexInserted(_self.currentPathIndex, leftPath.bezierCurves.length, newPath, oldPath);
})
.move(function (e: XMouseEvent) {
_self.mouseIsOver = true;
_self._handleMoveEvent(e.params.pos.x, e.params.pos.y);
});
this.pb.eventCatcher.addEventListener("mouseenter", this._mouseEnterListener);
this.pb.eventCatcher.addEventListener("mouseleave", this._mouseLeaveListener);
return mouseHandler;
}
// +---------------------------------------------------------------------------------
// | Called once upon initialization.
// |
// | @return {KeyHandler}
// +-------------------------------
private _installKeyListener(): KeyHandler {
var _self = this;
return new KeyHandler({ trackAll: true }).down("delete", function () {
_self._handleDelete();
});
}
// +---------------------------------------------------------------------------------
// | Removes vertex listeners from all path points.
// |
// | @param {BezierPath} path - The path to remove vertex listeners from.
// +-------------------------------
private _removeDefaultPathListeners(path: BezierPath): void {
BezierPathInteractionHelper.removePathVertexDragListeners(path, this._updateMinDistance);
}
// +---------------------------------------------------------------------------------
// | Update the min distance from point `line.b` to the curve. And redraw.
// +-------------------------------
private _updateMinDistance(): void {
if (this.paths.length == 0) {
return;
}
let pathIndex: number = -1;
let minDist: number = Number.MAX_VALUE;
let closestPoint: Vertex | null = null;
let closestT: number = 0.0;
for (var i = 0; i < this.paths.length; i++) {
let path: BezierPath = this.paths[i];
let t: number = path.getClosestT(this.currentB);
let point: Vertex = path.getPointAt(t);
let dist: number = point.distance(this.currentB);
if (dist < minDist) {
pathIndex = i;
minDist = dist;
closestT = t;
closestPoint = point;
}
}
this.currentT = closestT;
this.currentPathIndex = pathIndex;
this.currentDistance = minDist;
closestPoint && this.currentA.set(closestPoint);
}
// +---------------------------------------------------------------------------------
// | Sets all vertices on the given path to `bezierAutoAdjust=true`.
// |
// | @static
// +-------------------------------
static setPathAutoAdjust(path: BezierPath): void {
for (var i = 0; i < path.bezierCurves.length; i++) {
const curve: CubicBezierCurve = path.bezierCurves[i];
path.adjustPredecessorControlPoint(
i,
true, // obtainHandleLength
false // updateArcLength (we will do this after the loop)
);
if (i > 0 || path.adjustCircular) curve.startPoint.attr.bezierAutoAdjust = true;
}
path.updateArcLengths();
}
/**
* A helper function to add drag-start listeners to all vertices of the given path.
*
* @static
* @method addPathVertexDragStartListeners
* @memberof BezierPathInteractionHelper
* @param {BezierPath} path - The Bézier path to add vertex listeners to.
* @param {function} vertexDragStartListener - The drag listeners to add to each path vertex.
* @return void
**/
static addPathVertexDragStartListeners(path: BezierPath, vertexDragStartListener: VertListener): void {
for (var i = 0; i < path.bezierCurves.length; i++) {
const curve: CubicBezierCurve = path.bezierCurves[i];
curve.startPoint.listeners.addDragStartListener(vertexDragStartListener);
curve.startControlPoint.listeners.addDragStartListener(vertexDragStartListener);
curve.endControlPoint.listeners.addDragStartListener(vertexDragStartListener);
if (i + 1 == path.bezierCurves.length && !path.adjustCircular)
curve.endPoint.listeners.addDragStartListener(vertexDragStartListener);
// if( i+1 == path.bezierCurves.length && !path.adjustCircular )
// BezierPathIntractionHelper._addVertsDragListener( [curve.startPoint, curve.startControlPoint, curve.endPoint, curve.endControlPoint ], vertexDragListener );
}
}
/**
* A helper function to remove drag-start listeners to all vertices of the given path.
*
* @static
* @method removePathVertexDragStartListeners
* @memberof BezierPathInteractionHelper
* @param {BezierPath} path - The Bézier path to remove vertex listeners from.
* @param {function} vertexDragListener - The drag listeners to remove from each path vertex.
* @return void
**/
static removePathVertexDragStartListeners(path: BezierPath, vertexDragStartListener: VertListener): void {
for (var i = 0; i < path.bezierCurves.length; i++) {
const curve: CubicBezierCurve = path.bezierCurves[i];
curve.startPoint.listeners.removeDragStartListener(vertexDragStartListener);
curve.startControlPoint.listeners.removeDragStartListener(vertexDragStartListener);
curve.endControlPoint.listeners.removeDragStartListener(vertexDragStartListener);
if (i + 1 == path.bezierCurves.length && !path.adjustCircular)
curve.endPoint.listeners.removeDragStartListener(vertexDragStartListener);
}
}
/**
* A helper function to add drag listeners to all vertices of the given path.
*
* @static
* @method addPathVertexDragListeners
* @memberof BezierPathInteractionHelper
* @param {BezierPath} path - The Bézier path to add vertex listeners to.
* @param {function} vertexDragListener - The drag listeners to add to each path vertex.
* @return void
**/
static addPathVertexDragListeners(path: BezierPath, vertexDragListener: VertListener): void {
for (var i = 0; i < path.bezierCurves.length; i++) {
const curve: CubicBezierCurve = path.bezierCurves[i];
curve.startPoint.listeners.addDragListener(vertexDragListener);
curve.startControlPoint.listeners.addDragListener(vertexDragListener);
curve.endControlPoint.listeners.addDragListener(vertexDragListener);
if (i + 1 == path.bezierCurves.length && !path.adjustCircular) curve.endPoint.listeners.addDragListener(vertexDragListener);
// if( i+1 == path.bezierCurves.length && !path.adjustCircular )
// BezierPathIntractionHelper._addVertsDragListener( [curve.startPoint, curve.startControlPoint, curve.endPoint, curve.endControlPoint ], vertexDragListener );
}
}
/**
* A helper function to remove drag listeners to all vertices of the given path.
*
* @static
* @method removePathVertexDragListeners
* @memberof BezierPathInteractionHelper
* @param {BezierPath} path - The Bézier path to remove vertex listeners from.
* @param {function} vertexDragListener - The drag listeners to remove from each path vertex.
* @return void
**/
static removePathVertexDragListeners(path: BezierPath, vertexDragListener: VertListener): void {
for (var i = 0; i < path.bezierCurves.length; i++) {
const curve: CubicBezierCurve = path.bezierCurves[i];
curve.startPoint.listeners.removeDragListener(vertexDragListener);
curve.startControlPoint.listeners.removeDragListener(vertexDragListener);
curve.endControlPoint.listeners.removeDragListener(vertexDragListener);
if (i + 1 == path.bezierCurves.length && !path.adjustCircular)
curve.endPoint.listeners.removeDragListener(vertexDragListener);
}
}
/**
* A helper function to add drag-end listeners to all vertices of the given path.
*
* @static
* @method addPathVertexDragEndListeners
* @memberof BezierPathInteractionHelper
* @param {BezierPath} path - The Bézier path to add vertex listeners to.
* @param {function} vertexDragEndListener - The drag listeners to add to each path vertex.
* @return void
**/
static addPathVertexDragEndListeners(path: BezierPath, vertexDragEndListener: VertListener): void {
for (var i = 0; i < path.bezierCurves.length; i++) {
const curve: CubicBezierCurve = path.bezierCurves[i];
curve.startPoint.listeners.addDragEndListener(vertexDragEndListener);
curve.startControlPoint.listeners.addDragEndListener(vertexDragEndListener);
curve.endControlPoint.listeners.addDragEndListener(vertexDragEndListener);
if (i + 1 == path.bezierCurves.length && !path.adjustCircular)
curve.endPoint.listeners.addDragEndListener(vertexDragEndListener);
// if( i+1 == path.bezierCurves.length && !path.adjustCircular )
// BezierPathIntractionHelper._addVertsDragListener( [curve.startPoint, curve.startControlPoint, curve.endPoint, curve.endControlPoint ], vertexDragListener );
}
}
/**
* A helper function to remove drag-end listeners to all vertices of the given path.
*
* @static
* @method removePathVertexDragEndListeners
* @memberof BezierPathInteractionHelper
* @param {BezierPath} path - The Bézier path to remove vertex listeners from.
* @param {function} vertexDragListener - The drag listeners to remove from each path vertex.
* @return void
**/
static removePathVertexDragEndListeners(path: BezierPath, vertexDragEndListener: VertListener): void {
for (var i = 0; i < path.bezierCurves.length; i++) {
const curve: CubicBezierCurve = path.bezierCurves[i];
curve.startPoint.listeners.removeDragEndListener(vertexDragEndListener);
curve.startControlPoint.listeners.removeDragEndListener(vertexDragEndListener);
curve.endControlPoint.listeners.removeDragEndListener(vertexDragEndListener);
if (i + 1 == path.bezierCurves.length && !path.adjustCircular)
curve.endPoint.listeners.removeDragEndListener(vertexDragEndListener);
}
}
}