diff --git a/modules/ui/components/Toaster.jsx b/modules/ui/components/Toaster.jsx
new file mode 100644
index 00000000..0fa6e2ed
--- /dev/null
+++ b/modules/ui/components/Toaster.jsx
@@ -0,0 +1,11 @@
+import React from 'react';
+
+
+
+export function Toaster({}) {
+
+ return
+
+
;
+
+}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 46ec9226..875d39b2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1181,6 +1181,21 @@
"integrity": "sha512-A8ia2Wus0OAP6hh28ZgPSCBJEX3Jnql3kg9di/I+Lmg1gbJXgDZBrHr/UGZXl20Vi1lXgMuUq8c8J899KFr5gA==",
"dev": true
},
+ "@babel/runtime": {
+ "version": "7.8.7",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.7.tgz",
+ "integrity": "sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg==",
+ "requires": {
+ "regenerator-runtime": "^0.13.4"
+ },
+ "dependencies": {
+ "regenerator-runtime": {
+ "version": "0.13.4",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.4.tgz",
+ "integrity": "sha512-plpwicqEzfEyTQohIKktWigcLzmNStMGwbOUbykx51/29Z3JOGYldaaNGK7ngNXV+UcoqvIMmloZ48Sr74sd+g=="
+ }
+ }
+ },
"@babel/template": {
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz",
@@ -1287,14 +1302,6 @@
}
}
},
- "@dagrejs/graphlib": {
- "version": "2.1.4",
- "resolved": "https://registry.npmjs.org/@dagrejs/graphlib/-/graphlib-2.1.4.tgz",
- "integrity": "sha512-QCg9sL4uhjn468FDEsb/S9hS2xUZSrv/+dApb1Ze5VKO96pTXKNJZ6MGhIpgWkc1TVhbVGH9/7rq/Mf8/jWicw==",
- "requires": {
- "lodash": "^4.11.1"
- }
- },
"@types/events": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz",
@@ -3107,6 +3114,11 @@
}
}
},
+ "csstype": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.9.tgz",
+ "integrity": "sha512-xz39Sb4+OaTsULgUERcCk+TJj8ylkL4aSVDQiX/ksxbELSqwkgt4d4RD7fovIdgJGSuNYqwZEiVjYY5l0ask+Q=="
+ },
"currently-unhandled": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
@@ -3405,6 +3417,15 @@
"esutils": "^2.0.2"
}
},
+ "dom-helpers": {
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.1.3.tgz",
+ "integrity": "sha512-nZD1OtwfWGRBWlpANxacBEZrEuLa16o1nh7YopFWeoF68Zt8GGEmzHu6Xv4F3XaFIC+YXtTLrzgqKxFgLEe4jw==",
+ "requires": {
+ "@babel/runtime": "^7.6.3",
+ "csstype": "^2.6.7"
+ }
+ },
"domain-browser": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
@@ -6386,7 +6407,8 @@
"lodash": {
"version": "4.17.15",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
- "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
+ "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
+ "dev": true
},
"lodash.camelcase": {
"version": "4.3.0",
@@ -8337,6 +8359,73 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz",
"integrity": "sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q=="
},
+ "react-toastify": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-5.5.0.tgz",
+ "integrity": "sha512-jsVme7jALIFGRyQsri/g4YTsRuaaGI70T6/ikjwZMB4mwTZaCWqj5NqxhGrRStKlJc5npXKKvKeqTiRGQl78LQ==",
+ "requires": {
+ "@babel/runtime": "^7.4.2",
+ "classnames": "^2.2.6",
+ "prop-types": "^15.7.2",
+ "react-transition-group": "^4"
+ },
+ "dependencies": {
+ "classnames": {
+ "version": "2.2.6",
+ "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz",
+ "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q=="
+ },
+ "loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "requires": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ }
+ },
+ "prop-types": {
+ "version": "15.7.2",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
+ "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
+ "requires": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.8.1"
+ }
+ }
+ }
+ },
+ "react-transition-group": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.3.0.tgz",
+ "integrity": "sha512-1qRV1ZuVSdxPlPf4O8t7inxUGpdyO5zG9IoNfJxSO0ImU2A1YWkEQvFPuIPZmMLkg5hYs7vv5mMOyfgSkvAwvw==",
+ "requires": {
+ "@babel/runtime": "^7.5.5",
+ "dom-helpers": "^5.0.1",
+ "loose-envify": "^1.4.0",
+ "prop-types": "^15.6.2"
+ },
+ "dependencies": {
+ "loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "requires": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ }
+ },
+ "prop-types": {
+ "version": "15.7.2",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
+ "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
+ "requires": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.8.1"
+ }
+ }
+ }
+ },
"read-pkg": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
diff --git a/package.json b/package.json
index 3c5b4892..acf0a264 100644
--- a/package.json
+++ b/package.json
@@ -64,6 +64,7 @@
"prop-types": "15.6.0",
"react": "^16.8.6",
"react-dom": "^16.8.6",
+ "react-toastify": "^5.5.0",
"sprintf": "0.1.5",
"three": "0.89.0"
}
diff --git a/web/app/sketcher/actions/constraintActions.js b/web/app/sketcher/actions/constraintActions.js
index 0dca9d31..30f43c75 100644
--- a/web/app/sketcher/actions/constraintActions.js
+++ b/web/app/sketcher/actions/constraintActions.js
@@ -2,8 +2,7 @@ import {AlgNumConstraint, ConstraintDefinitions} from "../constr/ANConstraints";
import {EndPoint} from "../shapes/point";
import {Circle} from "../shapes/circle";
import {Segment} from "../shapes/segment";
-import {isInstanceOf, matchAll, matchTypes, sortSelectionByType} from "./matchUtils";
-import constraints from "../../../test/cases/constraints";
+import {isInstanceOf, matchAll, matchTypes} from "./matchUtils";
import {Arc} from "../shapes/arc";
import {FilletTool} from "../tools/fillet";
import {editConstraint as _editConstraint} from "../components/ConstraintEditor";
@@ -13,13 +12,17 @@ export default [
{
id: 'Coincident',
- shortName: 'Coincident',
+ shortName: 'Coincident Constraint',
description: 'Point Coincident',
- selectionMatcher: (selection, sortedByType) => matchAll(selection, EndPoint, 2),
+ selectionMatcher: {
+ selector: 'matchAll',
+ types: [EndPoint],
+ minQuantity: 2
+ },
- invoke: ctx => {
+ invoke: (ctx, matchedObjects) => {
const {viewer} = ctx;
- const [first, ...others] = viewer.selected;
+ const [first, ...others] = matchedObjects;
let pm = viewer.parametricManager;
for (let obj of others) {
pm._add(
@@ -32,17 +35,26 @@ export default [
{
id: 'Tangent',
- shortName: 'Tangent',
+ shortName: 'Tangent Constraint',
description: 'Tangent Between Line And Circle',
- selectionMatcher: [
- (selection, sortedByType) => matchTypes(sortedByType, Circle, 1, Segment, 1),
- (selection, sortedByType) => matchTypes(sortedByType, Arc, 1, Segment, 1),
- ],
+ selectionMatcher: {
+ selector: 'matchSequence',
+ sequence: [
+ {
+ types: [Circle, Arc],
+ quantity: 1
+ },
+ {
+ types: [Segment],
+ quantity: 1
+ },
+ ]
+ },
- invoke: ctx => {
+ invoke: (ctx, matchedObjects) => {
const {viewer} = ctx;
- const [circle, line] = sortSelectionByType(viewer.selected);
+ const [circle, line] = matchedObjects;
const constraint = new AlgNumConstraint(ConstraintDefinitions.TangentLC, [line, circle]);
constraint.initConstants();
@@ -54,24 +66,21 @@ export default [
{
id: 'EqualRadius',
- shortName: 'Equal Radius',
+ shortName: 'Equal Radius Constraint',
description: 'Equal Radius Between Two Circle',
- selectionMatcher: selection => {
- for (let obj of selection) {
- if (!(isInstanceOf(obj, Circle) || isInstanceOf(obj, Arc))) {
- return false;
- }
- }
- return true;
+ selectionMatcher: {
+ selector: 'matchAll',
+ types: [Circle, Arc],
+ minQuantity: 2
},
- invoke: ctx => {
+ invoke: (ctx, matchedObjects) => {
const {viewer} = ctx;
const pm = viewer.parametricManager;
- for (let i = 1; i < viewer.selected.length; ++i) {
- pm._add(new AlgNumConstraint(ConstraintDefinitions.EqualRadius, [viewer.selected[i-1], viewer.selected[i]]));
+ for (let i = 1; i < matchedObjects.length; ++i) {
+ pm._add(new AlgNumConstraint(ConstraintDefinitions.EqualRadius, [matchedObjects[i-1], matchedObjects[i]]));
}
pm.commit();
}
@@ -80,15 +89,18 @@ export default [
{
id: 'EqualLength',
- shortName: 'Equal Length',
+ shortName: 'Equal Length Constraint',
description: 'Equal Length Between Two Segments',
- selectionMatcher: selection => matchAll(selection, Segment, 2),
-
- invoke: ctx => {
+ selectionMatcher: {
+ selector: 'matchAll',
+ types: [Segment],
+ minQuantity: 2
+ },
+ invoke: (ctx, matchedObjects) => {
const {viewer} = ctx;
const pm = viewer.parametricManager;
- for (let i = 1; i < viewer.selected.length; ++i) {
- pm._add(new AlgNumConstraint(ConstraintDefinitions.EqualLength, [viewer.selected[i-1], viewer.selected[i]]));
+ for (let i = 1; i < matchedObjects.length; ++i) {
+ pm._add(new AlgNumConstraint(ConstraintDefinitions.EqualLength, [matchedObjects[i-1], matchedObjects[i]]));
}
pm.commit();
}
@@ -97,13 +109,25 @@ export default [
{
id: 'PointOnLine',
- shortName: 'Point On Line',
+ shortName: 'Point On Line Constraint',
description: 'Point On Line',
- selectionMatcher: (selection, sortedByType) => matchTypes(sortedByType, EndPoint, 1, Segment, 1),
+ selectionMatcher: {
+ selector: 'matchSequence',
+ sequence: [
+ {
+ types: [EndPoint],
+ quantity: 1
+ },
+ {
+ types: [Segment],
+ quantity: 1
+ },
+ ]
+ },
- invoke: ctx => {
+ invoke: (ctx, matchedObjects) => {
const {viewer} = ctx;
- const [pt, line] = sortSelectionByType(viewer.selected);
+ const [pt, line] = matchedObjects;
let pm = viewer.parametricManager;
pm.add(new AlgNumConstraint(ConstraintDefinitions.PointOnLine, [pt, line]));
}
@@ -111,14 +135,17 @@ export default [
{
id: 'Angle',
- shortName: 'Angle',
+ shortName: 'Angle Constraint',
description: 'Angle',
- selectionMatcher: (selection, sortedByType) => matchAll(sortedByType, Segment, 1),
-
- invoke: ctx => {
+ selectionMatcher: {
+ selector: 'matchAll',
+ types: [Segment],
+ minQuantity: 1
+ },
+ invoke: (ctx, matchedObjects) => {
const {viewer} = ctx;
- const firstSegment = viewer.selected[0];
+ const firstSegment = matchedObjects[0];
const firstConstr = new AlgNumConstraint(ConstraintDefinitions.Angle, [firstSegment]);
firstConstr.initConstants();
@@ -126,8 +153,8 @@ export default [
editConstraint(ctx, firstConstr, () => {
const pm = viewer.parametricManager;
pm._add(firstConstr);
- for (let i = 1; i < viewer.selected.length; ++i) {
- pm._add(new AlgNumConstraint(ConstraintDefinitions.Angle, [viewer.selected[i]], {...firstConstr.constants}));
+ for (let i = 1; i < matchedObjects.length; ++i) {
+ pm._add(new AlgNumConstraint(ConstraintDefinitions.Angle, [matchedObjects[i]], {...firstConstr.constants}));
}
pm.commit();
});
@@ -136,15 +163,20 @@ export default [
{
id: 'Vertical',
- shortName: 'Vertical',
+ shortName: 'Vertical Constraint',
description: 'Vertical',
- selectionMatcher: (selection, sortedByType) => matchAll(sortedByType, Segment, 1),
- invoke: ctx => {
+ selectionMatcher: {
+ selector: 'matchAll',
+ types: [Segment],
+ minQuantity: 1
+ },
+
+ invoke: (ctx, matchedObjects) => {
const {viewer} = ctx;
const pm = viewer.parametricManager;
- viewer.selected.forEach(obj => {
+ matchedObjects.forEach(obj => {
const constr = new AlgNumConstraint(ConstraintDefinitions.Vertical, [obj]);
constr.initConstants();
pm._add(constr);
@@ -155,15 +187,20 @@ export default [
{
id: 'Horizontal',
- shortName: 'Horizontal',
+ shortName: 'Horizontal Constraint',
description: 'Horizontal',
- selectionMatcher: (selection, sortedByType) => matchAll(sortedByType, Segment, 1),
- invoke: ctx => {
+ selectionMatcher: {
+ selector: 'matchAll',
+ types: [Segment],
+ minQuantity: 1
+ },
+
+ invoke: (ctx, matchedObjects) => {
const {viewer} = ctx;
const pm = viewer.parametricManager;
- viewer.selected.forEach(obj => {
+ matchedObjects.forEach(obj => {
const constr = new AlgNumConstraint(ConstraintDefinitions.Horizontal, [obj]);
constr.initConstants();
pm._add(constr);
@@ -174,14 +211,19 @@ export default [
{
id: 'AngleBetween',
- shortName: 'Angle Between',
+ shortName: 'Angle Between Constraint',
description: 'Angle Between Lines',
- selectionMatcher: (selection, sortedByType) => matchAll(sortedByType, Segment, 2),
- invoke: ctx => {
+ selectionMatcher: {
+ selector: 'matchAll',
+ types: [Segment],
+ minQuantity: 2
+ },
+
+ invoke: (ctx, matchedObjects) => {
const {viewer} = ctx;
- const [firstSegment, secondSegment] = viewer.selected;
+ const [firstSegment, secondSegment] = matchedObjects;
const firstConstr = new AlgNumConstraint(ConstraintDefinitions.AngleBetween, [firstSegment, secondSegment]);
firstConstr.initConstants();
@@ -189,9 +231,9 @@ export default [
editConstraint(ctx, firstConstr, () => {
const pm = viewer.parametricManager;
pm._add(firstConstr);
- for (let i = 2; i < viewer.selected.length; ++i) {
+ for (let i = 2; i < matchedObjects.length; ++i) {
pm._add(new AlgNumConstraint(ConstraintDefinitions.Angle,
- [viewer.selected[i-1], viewer.selected[i]], {...firstConstr.constants}));
+ [matchedObjects[i-1], matchedObjects[i]], {...firstConstr.constants}));
}
pm.commit();
});
@@ -200,17 +242,22 @@ export default [
{
id: 'Perpendicular',
- shortName: 'Perpendicular',
+ shortName: 'Perpendicular Constraint',
description: 'Perpendicularity between two or more lines',
- selectionMatcher: (selection, sortedByType) => matchAll(sortedByType, Segment, 2),
- invoke: ctx => {
+ selectionMatcher: {
+ selector: 'matchAll',
+ types: [Segment],
+ minQuantity: 2
+ },
+
+ invoke: (ctx, matchedObjects) => {
const {viewer} = ctx;
const pm = viewer.parametricManager;
- for (let i = 1; i < viewer.selected.length; ++i) {
- const constr = new AlgNumConstraint(ConstraintDefinitions.Perpendicular, [viewer.selected[i-1], viewer.selected[i]]);
+ for (let i = 1; i < matchedObjects.length; ++i) {
+ const constr = new AlgNumConstraint(ConstraintDefinitions.Perpendicular, [matchedObjects[i-1], matchedObjects[i]]);
constr.initConstants();
pm._add(constr);
}
@@ -220,17 +267,22 @@ export default [
{
id: 'Parallel',
- shortName: 'Parallel',
+ shortName: 'Parallel Constraint',
description: 'Parallelism between two or more lines',
- selectionMatcher: (selection, sortedByType) => matchAll(sortedByType, Segment, 2),
- invoke: ctx => {
+ selectionMatcher: {
+ selector: 'matchAll',
+ types: [Segment],
+ minQuantity: 2
+ },
+
+ invoke: (ctx, matchedObjects) => {
const {viewer} = ctx;
const pm = viewer.parametricManager;
- for (let i = 1; i < viewer.selected.length; ++i) {
- const constr = new AlgNumConstraint(ConstraintDefinitions.Parallel, [viewer.selected[i-1], viewer.selected[i]]);
+ for (let i = 1; i < matchedObjects.length; ++i) {
+ const constr = new AlgNumConstraint(ConstraintDefinitions.Parallel, [matchedObjects[i-1], matchedObjects[i]]);
constr.initConstants();
pm._add(constr);
}
@@ -240,14 +292,19 @@ export default [
{
id: 'Length',
- shortName: 'Length',
+ shortName: 'Length Constraint',
description: 'Segment Length',
- selectionMatcher: (selection) => matchAll(selection, Segment, 1),
- invoke: ctx => {
+ selectionMatcher: {
+ selector: 'matchAll',
+ types: [Segment],
+ minQuantity: 1
+ },
+
+ invoke: (ctx, matchedObjects) => {
const {viewer} = ctx;
- const [firstSegment, ...others] = viewer.selected;
+ const [firstSegment, ...others] = matchedObjects;
const firstConstr = new AlgNumConstraint(ConstraintDefinitions.SegmentLength, [firstSegment]);
firstConstr.initConstants();
@@ -265,21 +322,19 @@ export default [
{
id: 'RadiusLength',
- shortName: 'RadiusLength',
+ shortName: 'Radius Length Constraint',
description: 'Radius Length',
- selectionMatcher: (selection) => {
- for (let obj of selection) {
- if (!(isInstanceOf(obj, Circle) || isInstanceOf(obj, Arc))) {
- return false;
- }
- }
- return true;
+
+ selectionMatcher: {
+ selector: 'matchAll',
+ types: [Circle, Arc],
+ minQuantity: 1
},
- invoke: ctx => {
+ invoke: (ctx, matchedObjects) => {
const {viewer} = ctx;
- const [firstCircle, ...others] = viewer.selected;
+ const [firstCircle, ...others] = matchedObjects;
const firstConstr = new AlgNumConstraint(ConstraintDefinitions.RadiusLength, [firstCircle]);
firstConstr.initConstants();
@@ -297,14 +352,28 @@ export default [
{
id: 'DistancePL',
- shortName: 'Point to Line Distance',
+ shortName: 'Point to Line Distance Constraint',
description: 'Distance between Point and Line',
- selectionMatcher: (selection, sortedByType) => matchTypes(sortedByType, EndPoint, 1, Segment, 1),
- invoke: ctx => {
+ selectionMatcher: {
+ selector: 'matchSequence',
+ sequence: [
+ {
+ types: [EndPoint],
+ quantity: 1
+ },
+ {
+ types: [Segment],
+ quantity: 1
+ },
+ ]
+ },
+
+
+ invoke: (ctx, matchedObjects) => {
const {viewer} = ctx;
- const [pt, seg] = sortSelectionByType(viewer.selected);
+ const [pt, seg] = matchedObjects;
const constr = new AlgNumConstraint(ConstraintDefinitions.DistancePL, [pt, seg]);
constr.initConstants();
@@ -318,14 +387,23 @@ export default [
{
id: 'DistancePP',
- shortName: 'Two Point Distance',
+ shortName: 'Two Point Distance Constraint',
description: 'Distance between two Points',
- selectionMatcher: (selection, sortedByType) => matchTypes(sortedByType, EndPoint, 2),
- invoke: ctx => {
+ selectionMatcher: {
+ selector: 'matchSequence',
+ sequence: [
+ {
+ types: [EndPoint],
+ quantity: 2
+ }
+ ]
+ },
+
+ invoke: (ctx, matchedObjects) => {
const {viewer} = ctx;
- const [p1, p2] = sortSelectionByType(viewer.selected);
+ const [p1, p2] = matchedObjects;
const constr = new AlgNumConstraint(ConstraintDefinitions.DistancePP, [p1, p2]);
constr.initConstants();
@@ -339,14 +417,22 @@ export default [
{
id: 'Lock',
- shortName: 'Lock',
+ shortName: 'Lock Point Constraint',
description: 'Lock Point',
- selectionMatcher: (selection) => matchTypes(selection, EndPoint, 1),
+ selectionMatcher: {
+ selector: 'matchSequence',
+ sequence: [
+ {
+ types: [EndPoint],
+ quantity: 1
+ }
+ ]
+ },
- invoke: ctx => {
+ invoke: (ctx, matchedObjects) => {
const {viewer} = ctx;
- const [point] = viewer.selected;
+ const [point] = matchedObjects;
const constr = new AlgNumConstraint(ConstraintDefinitions.LockPoint, [point]);
constr.initConstants();
@@ -356,32 +442,33 @@ export default [
{
id: 'Fillet',
- shortName: 'Fillet',
+ shortName: 'Fillet Tool',
description: 'Add a Fillet',
- selectionMatcher: (selection) => {
- if (matchTypes(selection, EndPoint, 1)) {
- const [point] = selection;
- if (isInstanceOf(point.parent, Segment)) {
- let pair = null;
- point.visitLinked(l => {
- if (l !== point && isInstanceOf(l.parent, Segment)) {
- pair = l;
+ selectionMatcher: {
+ selector: 'function',
+ match: (selection) => {
+ if (matchTypes(selection, EndPoint, 1)) {
+ const [point] = selection;
+ if (isInstanceOf(point.parent, Segment)) {
+ let pair = null;
+ point.visitLinked(l => {
+ if (l !== point && isInstanceOf(l.parent, Segment)) {
+ pair = l;
+ return true;
+ }
+ });
+ if (pair) {
return true;
}
- });
- if (pair) {
- return true;
}
}
- }
- return false;
+ return false;
+ },
+ description: 'a point linking two segment'
},
- invoke: ctx => {
+ invoke: (ctx, matchedObjects) => {
const {viewer} = ctx;
-
-
-
const filletTool = new FilletTool(ctx.viewer);
const cands = filletTool.getCandidateFromSelection(viewer.selected);
if (cands) {
diff --git a/web/app/sketcher/actions/index.js b/web/app/sketcher/actions/index.js
index d583d00d..0bdb33bb 100644
--- a/web/app/sketcher/actions/index.js
+++ b/web/app/sketcher/actions/index.js
@@ -1,42 +1,44 @@
import constraintActions from "./constraintActions";
-import {sortSelectionByType} from "./matchUtils";
+import {getDescription, MatchIndex, matchSelection} from "../selectionMatcher";
+import {toast} from "react-toastify";
const ALL_CONTEXTUAL_ACTIONS = [
...constraintActions,
//keep going here
];
+const index = {};
+ALL_CONTEXTUAL_ACTIONS.forEach(a => index[a.id] = a);
+Object.freeze(index);
+
export function matchAvailableActions(selection) {
- let sortedByType = sortSelectionByType(selection);
let matched = [];
-
+ let matchIndex = new MatchIndex(selection);
if (selection.length) {
for (let action of ALL_CONTEXTUAL_ACTIONS) {
-
- if (Array.isArray(action.selectionMatcher)) {
- action.selectionMatcher.forEach(matcher => {
- if (matcher(selection, sortedByType)) {
- matched.push(action);
- }
- })
- } else {
- if (action.selectionMatcher(selection, sortedByType)) {
- matched.push(action);
- }
+ if (matchSelection(action.selectionMatcher, matchIndex, true)) {
+ matched.push(action);
}
-
}
}
return matched;
-
}
//For backward compatibility
-export function getActionIfAvailable(actionId, selection, cb) {
+export function runActionOrToastWhyNot(actionId, selection, ctx) {
+ const action = index[actionId];
+ if (action) {
+ const matched = matchSelection(action.selectionMatcher, new MatchIndex(selection), false);
+ if (matched) {
+ action.invoke(ctx, matched)
+ } else {
+ toast('The action "' + action.shortName + '" requires selection of ' + getDescription(action.selectionMatcher));
+ }
+ }
matchAvailableActions(selection).forEach(a => {
if (a.id === actionId) {
cb(a);
diff --git a/web/app/sketcher/components/ContextualControls.jsx b/web/app/sketcher/components/ContextualControls.jsx
index e3f5d074..132c0d3f 100644
--- a/web/app/sketcher/components/ContextualControls.jsx
+++ b/web/app/sketcher/components/ContextualControls.jsx
@@ -3,6 +3,7 @@ import ls from './ContextualControls.less';
import {matchAvailableActions} from "../actions";
import {useStream} from "../../../../modules/ui/effects";
import {SketcherAppContext} from "./SketcherApp";
+import {MatchIndex, matchSelection} from "../selectionMatcher";
export function ContextualControls() {
@@ -25,7 +26,7 @@ export function ContextualControls() {
AVAILABLE ACTIONS:
{
- availableActions.map(a =>