aboutsummaryrefslogtreecommitdiff
path: root/src/client/util/InteractionUtils.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/util/InteractionUtils.tsx')
-rw-r--r--src/client/util/InteractionUtils.tsx207
1 files changed, 194 insertions, 13 deletions
diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx
index 3a5345c80..04a750f93 100644
--- a/src/client/util/InteractionUtils.tsx
+++ b/src/client/util/InteractionUtils.tsx
@@ -1,4 +1,8 @@
import React = require("react");
+import * as beziercurve from 'bezier-curve';
+import * as fitCurve from 'fit-curve';
+import "./InteractionUtils.scss";
+import { Utils } from "../../Utils";
export namespace InteractionUtils {
export const MOUSETYPE = "mouse";
@@ -23,7 +27,7 @@ export namespace InteractionUtils {
export interface MultiTouchEventDisposer { (): void; }
/**
- *
+ *
* @param element - element to turn into a touch target
* @param startFunc - event handler, typically Touchable.onTouchStart (classes that inherit touchable can pass in this.onTouchStart)
*/
@@ -87,22 +91,200 @@ export namespace InteractionUtils {
return myTouches;
}
- export function CreatePolyline(points: { X: number, Y: number }[], left: number, top: number, color: string, width: string) {
- const pts = points.reduce((acc: string, pt: { X: number, Y: number }) => acc + `${pt.X - left},${pt.Y - top} `, "");
- return (
+ export function CreatePolyline(points: { X: number, Y: number }[], left: number, top: number,
+ color: string, width: number, strokeWidth: number, bezier: string, fill: string, arrowStart: string, arrowEnd: string,
+ dash: string, scalex: number, scaley: number, shape: string, pevents: string, drawHalo: boolean, nodefs: boolean) {
+
+ let pts: { X: number; Y: number; }[] = [];
+ if (shape) { //if any of the shape are true
+ pts = makePolygon(shape, points);
+ }
+ else if (points.length >= 5 && points[3].X === points[4].X) {
+ for (var i = 0; i < points.length - 3; i += 4) {
+ const array = [[points[i].X, points[i].Y], [points[i + 1].X, points[i + 1].Y], [points[i + 2].X, points[i + 2].Y], [points[i + 3].X, points[i + 3].Y]];
+ for (var t = 0; t < 1; t += 0.01) {
+ const point = beziercurve(t, array);
+ pts.push({ X: point[0], Y: point[1] });
+ }
+ }
+ }
+ else if (points.length > 1 && points[points.length - 1].X === points[0].X && points[points.length - 1].Y === points[0].Y) {
+ //pointer is up (first and last points are the same)
+ const newPoints = points.reduce((p, pts) => { p.push([pts.X, pts.Y]); return p; }, [] as number[][]);
+ newPoints.pop();
+
+ const bezierCurves = fitCurve(newPoints, parseInt(bezier));
+ for (const curve of bezierCurves) {
+ for (var t = 0; t < 1; t += 0.01) {
+ const point = beziercurve(t, curve);
+ pts.push({ X: point[0], Y: point[1] });
+ }
+ }
+ } else {
+ pts = points.slice();
+ // bcz: Ugh... this is ugly, but shapes apprently have an extra point added that is = (p[0].x,p[0].y+1) as some sort of flag. need to remove it here.
+ if (pts.length > 2 && pts[pts.length - 2].X === pts[0].X && pts[pts.length - 2].Y === pts[0].Y) {
+ pts.pop();
+ }
+ }
+ if (isNaN(scalex)) {
+ scalex = 1;
+ }
+ if (isNaN(scaley)) {
+ scaley = 1;
+ }
+ const strpts = pts.reduce((acc: string, pt: { X: number, Y: number }) => acc +
+ `${(pt.X - left - width / 2) * scalex + width / 2},
+ ${(pt.Y - top - width / 2) * scaley + width / 2} `, "");
+ const dashArray = String(Number(width) * Number(dash));
+ const defGuid = Utils.GenerateGuid();
+ const arrowDim = Math.max(0.5, 8 / Math.log(Math.max(2, strokeWidth)));
+ return (<svg fill={color}> {/* setting the svg fill sets the arrowStart fill */}
+ {nodefs ? (null) : <defs>
+ {arrowStart !== "dot" && arrowEnd !== "dot" ? (null) : <marker id={`dot${defGuid}`} orient="auto" overflow="visible">
+ <circle r={1} fill="context-stroke" />
+ </marker>}
+ {arrowStart !== "arrow" && arrowEnd !== "arrow" ? (null) : <marker id={`arrowStart${defGuid}`} orient="auto" overflow="visible" refX="1.6" refY="0" markerWidth="10" markerHeight="7">
+ <polygon points={`${arrowDim} ${-Math.max(1, arrowDim / 2)}, ${arrowDim} ${Math.max(1, arrowDim / 2)}, -1 0`} />
+ </marker>}
+ {arrowStart !== "arrow" && arrowEnd !== "arrow" ? (null) : <marker id={`arrowEnd${defGuid}`} orient="auto" overflow="visible" refX="1.6" refY="0" markerWidth="10" markerHeight="7">
+ <polygon points={`${2 - arrowDim} ${-Math.max(1, arrowDim / 2)}, ${2 - arrowDim} ${Math.max(1, arrowDim / 2)}, 3 0`} />
+ </marker>}
+ </defs>}
<polyline
- points={pts}
+ points={strpts}
style={{
- fill: "none",
+ filter: drawHalo ? "url(#inkSelectionHalo)" : undefined,
+ fill: fill ? fill : "transparent",
+ opacity: strokeWidth !== width ? 0.5 : undefined,
+ pointerEvents: pevents as any,
stroke: color ?? "rgb(0, 0, 0)",
- strokeWidth: parseInt(width),
+ strokeWidth: strokeWidth,
strokeLinejoin: "round",
- strokeLinecap: "round"
+ strokeLinecap: "round",
+ strokeDasharray: dashArray
}}
+ markerStart={`url(#${arrowStart + "Start" + defGuid})`}
+ markerEnd={`url(#${arrowEnd + "End" + defGuid})`}
/>
- );
+
+ </svg>);
}
+ export function makePolygon(shape: string, points: { X: number, Y: number }[]) {
+ if (points.length > 1 && points[points.length - 1].X === points[0].X && points[points.length - 1].Y + 1 === points[0].Y) {
+ //pointer is up (first and last points are the same)
+ if (shape === "arrow" || shape === "line") {
+ //if arrow or line, the two end points should be the starting and the ending point
+ var left = points[0].X;
+ var top = points[0].Y;
+ var right = points[1].X;
+ var bottom = points[1].Y;
+ } else {
+ //otherwise take max and min
+ const xs = points.map(p => p.X);
+ const ys = points.map(p => p.Y);
+ right = Math.max(...xs);
+ left = Math.min(...xs);
+ bottom = Math.max(...ys);
+ top = Math.min(...ys);
+ }
+ } else {
+ //if in the middle of drawing
+ //take first and last points
+ right = points[points.length - 1].X;
+ left = points[0].X;
+ bottom = points[points.length - 1].Y;
+ top = points[0].Y;
+ if (shape !== "arrow" && shape !== "line") {
+ //switch left/right and top/bottom if needed
+ if (left > right) {
+ const temp = right;
+ right = left;
+ left = temp;
+ }
+ if (top > bottom) {
+ const temp = top;
+ top = bottom;
+ bottom = temp;
+ }
+ }
+ }
+ points = [];
+ switch (shape) {
+ case "rectangle":
+ points.push({ X: left, Y: top });
+ points.push({ X: right, Y: top });
+ points.push({ X: right, Y: bottom });
+ points.push({ X: left, Y: bottom });
+ points.push({ X: left, Y: top });
+ return points;
+ case "triangle":
+ // points.push({ X: left, Y: bottom });
+ // points.push({ X: right, Y: bottom });
+ // points.push({ X: (right + left) / 2, Y: top });
+ // points.push({ X: left, Y: bottom });
+
+ points.push({ X: left, Y: bottom });
+ points.push({ X: left, Y: bottom });
+
+ points.push({ X: right, Y: bottom });
+ points.push({ X: right, Y: bottom });
+ points.push({ X: right, Y: bottom });
+ points.push({ X: right, Y: bottom });
+
+ points.push({ X: (right + left) / 2, Y: top });
+ points.push({ X: (right + left) / 2, Y: top });
+ points.push({ X: (right + left) / 2, Y: top });
+ points.push({ X: (right + left) / 2, Y: top });
+
+ points.push({ X: left, Y: bottom });
+ points.push({ X: left, Y: bottom });
+
+
+ return points;
+ case "circle":
+ const centerX = (right + left) / 2;
+ const centerY = (bottom + top) / 2;
+ const radius = bottom - centerY;
+ for (var y = top; y < bottom; y++) {
+ const x = Math.sqrt(Math.pow(radius, 2) - (Math.pow((y - centerY), 2))) + centerX;
+ points.push({ X: x, Y: y });
+ }
+ for (var y = bottom; y > top; y--) {
+ const x = Math.sqrt(Math.pow(radius, 2) - (Math.pow((y - centerY), 2))) + centerX;
+ const newX = centerX - (x - centerX);
+ points.push({ X: newX, Y: y });
+ }
+ points.push({ X: Math.sqrt(Math.pow(radius, 2) - (Math.pow((top - centerY), 2))) + centerX, Y: top });
+ return points;
+ // case "arrow":
+ // const x1 = left;
+ // const y1 = top;
+ // const x2 = right;
+ // const y2 = bottom;
+ // const L1 = Math.sqrt(Math.pow(Math.abs(x1 - x2), 2) + (Math.pow(Math.abs(y1 - y2), 2)));
+ // const L2 = L1 / 5;
+ // const angle = 0.785398;
+ // const x3 = x2 + (L2 / L1) * ((x1 - x2) * Math.cos(angle) + (y1 - y2) * Math.sin(angle));
+ // const y3 = y2 + (L2 / L1) * ((y1 - y2) * Math.cos(angle) - (x1 - x2) * Math.sin(angle));
+ // const x4 = x2 + (L2 / L1) * ((x1 - x2) * Math.cos(angle) - (y1 - y2) * Math.sin(angle));
+ // const y4 = y2 + (L2 / L1) * ((y1 - y2) * Math.cos(angle) + (x1 - x2) * Math.sin(angle));
+ // points.push({ X: x1, Y: y1 });
+ // points.push({ X: x2, Y: y2 });
+ // points.push({ X: x3, Y: y3 });
+ // points.push({ X: x4, Y: y4 });
+ // points.push({ X: x2, Y: y2 });
+ // return points;
+ case "line":
+
+ points.push({ X: left, Y: top });
+ points.push({ X: right, Y: bottom });
+ return points;
+ default:
+ return points;
+ }
+ }
/**
* Returns whether or not the pointer event passed in is of the type passed in
* @param e - pointer event. this event could be from a mouse, a pen, or a finger
@@ -122,8 +304,8 @@ export namespace InteractionUtils {
/**
* Returns euclidean distance between two points
- * @param pt1
- * @param pt2
+ * @param pt1
+ * @param pt2
*/
export function TwoPointEuclidist(pt1: React.Touch, pt2: React.Touch): number {
return Math.sqrt(Math.pow(pt1.clientX - pt2.clientX, 2) + Math.pow(pt1.clientY - pt2.clientY, 2));
@@ -222,7 +404,6 @@ export namespace InteractionUtils {
// let dist12 = TwoPointEuclidist(pt1, pt2);
// let dist23 = TwoPointEuclidist(pt2, pt3);
// let dist13 = TwoPointEuclidist(pt1, pt3);
- // console.log(`distances: ${dist12}, ${dist23}, ${dist13}`);
// let dist12close = dist12 < leniency;
// let dist23close = dist23 < leniency;
// let dist13close = dist13 < leniency;
@@ -254,4 +435,4 @@ export namespace InteractionUtils {
// }
// }
}
-} \ No newline at end of file
+}