From f5b6c20b9738f82c939718245e7999810364cc02 Mon Sep 17 00:00:00 2001 From: Val Erastov Date: Fri, 9 Feb 2018 20:04:36 -0800 Subject: [PATCH] error handling --- modules/bus/index.js | 6 +- web/app/brep/operations/boolean.js | 61 +++++++++++-------- web/app/cad/dom/components/wizard/Wizard.jsx | 26 +++++--- web/app/cad/dom/components/wizard/Wizard.less | 10 ++- web/app/utils/errors.js | 28 +++++---- 5 files changed, 79 insertions(+), 52 deletions(-) diff --git a/modules/bus/index.js b/modules/bus/index.js index a86b5b14..873408d8 100644 --- a/modules/bus/index.js +++ b/modules/bus/index.js @@ -93,11 +93,7 @@ export default class Bus { if (listenerList !== undefined) { for (let i = 0; i < listenerList.length; i++) { const callback = listenerList[i]; - try { - callback(data, oldValue); - } catch(e) { - console.error(e); - } + callback(data, oldValue); } } } finally { diff --git a/web/app/brep/operations/boolean.js b/web/app/brep/operations/boolean.js index fef7caf4..880a8d2f 100644 --- a/web/app/brep/operations/boolean.js +++ b/web/app/brep/operations/boolean.js @@ -56,13 +56,12 @@ export function invert( shell ) { } } shell.data.inverted = !shell.data.inverted; - checkShellForErrors(shell, 'UNABLE_BOOLEAN_OPERAND_INVERSION'); -} - -function checkShellForErrors(shell, code) { let errors = BREPValidator.validate(shell); if (errors.length !== 0) { - throw new CadError(code, errors); + throw new CadError({ + kind: CadError.KIND.INTERNAL_ERROR, + code: 'unable to invert' + }); } } @@ -354,7 +353,12 @@ function mergeFaces(facesA, facesB, opType) { if (edgeA.vertexA === edgeB.vertexA) { // chooseBetweenEqualEdges(); // canEdgeBeTransferred(edge, face, opType) - throw new CadError('BOOLEAN_INVALID_RESULT', edgeCollisionError(edgeA, edgeB)); + throw new CadError({ + kind: CadError.KIND.UNSUPPORTED_CASE, + code: 'edge collision on face merge', + relatedTopoObjects: [edgeA, edgeB], + userMessage: "edges can't be coincident for this operation" + }); } else if (edgeA.vertexA === edgeB.vertexB) { invalid.add(edgeA); @@ -500,8 +504,9 @@ function traverseFaces(face, callback) { let twinFace = halfEdge.twin().loop.face; if (twinFace === null) { //this happened because there is no face created for a valid and legit detected loop - throw new CadError('BOOLEAN_INVALID_RESULT', { - code: 'UNABLE_FACE_EVOLVE', payload: {halfEdge} + throw new CadError({ + kind: CadError.KIND.INTERNAL_ERROR, + relatedTopoObjects: [halfEdge] }); // BREP_DEBUG.markEdge("null face", halfEdge.twin()) } else { @@ -947,13 +952,17 @@ export function isInsideEnclose(normal, testee, inVec, outVec, strict){ if (strict && veq(outVec, testee)) { //TODO: improve error report - throw new CadError('BOOLEAN_INVALID_RESULT'); + throw new CadError({ + relatedTopoObjects: [testee] + }); } let pivot = inVec.negate(); if (strict && veq(pivot, testee)) { //TODO: improve error report - throw new CadError('BOOLEAN_INVALID_RESULT'); + throw new CadError({ + relatedTopoObjects: [testee] + }); } let enclosureAngle = leftTurningMeasure(pivot, outVec, normal); let testeeAngle = leftTurningMeasure(pivot, testee, normal); @@ -1148,7 +1157,7 @@ class FaceSolveData extends EdgeGraph { super(); this.face = face; this.newEdges = []; - this.errors = []; + this.collidedEdges = []; } markTransferredFrom(edge) { @@ -1181,7 +1190,7 @@ class FaceSolveData extends EdgeGraph { let opp = this.findOppositeEdge(he); if (opp) { - this.errors.push(edgeCollisionError(opp, he)); + this.collidedEdges.push(opp, he); } let list = this.vertexToEdge.get(he.vertexA); @@ -1191,7 +1200,7 @@ class FaceSolveData extends EdgeGraph { } else { for (let ex of list) { if (he.vertexB === ex.vertexB && isSameEdge(he, ex)) { - this.errors.push(edgeCollisionError(ex, he)); + this.collidedEdges.push(ex, he); // ex.attachManifold(he); // return; } @@ -1253,7 +1262,11 @@ function curveAndEdgeCoincident(curve, edge) { if (!veq(pt1, pt2)) { if (touches > 1) { //partial tangency should be handled before face-face intersection analysis - throw new CadError('BOOLEAN_INVALID_RESULT', {edge}); + throw new CadError({ + kind: CadError.KIND.INVALID_INPUT, + code: 'edge partial tangency', + relatedTopoObjects: [edge] + }); } return false; } @@ -1275,21 +1288,19 @@ function edgeCollinearToFace(edge, face) { return face.rayCast(edge.edge.curve.middlePoint()).inside; } -function edgeCollisionError(e1, e2) { - return { - code: 'EDGE_COLLISION', payload: {e1, e2} - } -} - function checkFaceDataForError(facesData) { - if (facesData.find(f => f.errors.length !== 0)) { - let payload = []; + if (facesData.find(f => f.collidedEdges.length !== 0)) { + let relatedTopoObjects = []; for (let faceData of facesData) { - for (let err of faceData.errors) { - payload.push(err); + for (let err of faceData.collidedEdges) { + relatedTopoObjects.push(err); } } - throw new CadError('BOOLEAN_INVALID_RESULT', payload); + throw new CadError({ + kind: CadError.KIND.INVALID_INPUT, + relatedTopoObjects, + userMessage: 'unable to process coincident edges for this operation type' + }); } } diff --git a/web/app/cad/dom/components/wizard/Wizard.jsx b/web/app/cad/dom/components/wizard/Wizard.jsx index 700a0427..309f94f8 100644 --- a/web/app/cad/dom/components/wizard/Wizard.jsx +++ b/web/app/cad/dom/components/wizard/Wizard.jsx @@ -11,10 +11,10 @@ import Button from 'ui/components/controls/Button'; import ButtonGroup from 'ui/components/controls/ButtonGroup'; import FaceSelectionControl from './FaceSelectionControl'; import {CURRENT_SELECTION} from "../../../craft/wizard/wizardPlugin"; -import {isTCADError} from "../../../../utils/errors"; import ls from './Wizard.less'; import RadioButtons, {RadioButton} from "ui/components/controls/RadioButtons"; +import CadError from '../../../../utils/errors'; export default class Wizard extends React.Component { @@ -59,7 +59,8 @@ export default class Wizard extends React.Component { {this.state.hasError &&
performing operation with current parameters leads to an invalid object (manifold / self-intersecting / zero-thickness / complete degeneration or unsupported cases) - {this.state.code && {this.state.code}} + {this.state.code &&
{this.state.code}
} + {this.state.userMessage &&
{this.state.userMessage}
}
} @@ -95,17 +96,22 @@ export default class Wizard extends React.Component { this.props.onOK(this.params); this.onClose(); } catch (error) { - let state = { - hasError: true + let stateUpdate = { + hasError: true }; - if (!isTCADError(error)) { - console.error('internal error while performing operation'); + let printError = true; + if (error.TYPE === CadError) { + let {code, userMessage, kind} = error; + printError = !code; + if (code && kind === CadError.KIND.INTERNAL_ERROR) { + console.warn('Operation Error Code: ' + code); + } + Object.assign(stateUpdate, {code, userMessage}); + } + if (printError) { console.error(error); - } else { - state.cadCode = error.code; - console.log(error); } - this.setState(state); + this.setState(stateUpdate); } }; diff --git a/web/app/cad/dom/components/wizard/Wizard.less b/web/app/cad/dom/components/wizard/Wizard.less index fc17f1ba..830281d5 100644 --- a/web/app/cad/dom/components/wizard/Wizard.less +++ b/web/app/cad/dom/components/wizard/Wizard.less @@ -2,6 +2,14 @@ color: lightgoldenrodyellow; } -.errorCode { +.userErrorMessage, .errorCode { font-size: 0.9rem; +} + +.userErrorMessage { + color: white; +} + +.errorCode { + font-style: italic; } \ No newline at end of file diff --git a/web/app/utils/errors.js b/web/app/utils/errors.js index 18e9a88a..7173cbd3 100644 --- a/web/app/utils/errors.js +++ b/web/app/utils/errors.js @@ -1,16 +1,22 @@ -export const ERROR_TYPE = 'CAD_ERROR'; - -export function isTCADError(err) { - return err && err.type === ERROR_TYPE; -} - export default class CadError extends Error { - - constructor(code, payload) { - super(code); + + constructor({kind, code, relatedTopoObjects, userMessage} = __EMPTY) { + super(); + this.kind = kind || CadError.KIND.INTERNAL_ERROR; this.code = code; - this.payload = payload; - this.type = ERROR_TYPE; + this.relatedTopoObjects = relatedTopoObjects; + this.userMessage = userMessage; } + + //https://stackoverflow.com/questions/33870684/why-doesnt-instanceof-work-on-instances-of-error-subclasses-under-babel-node + TYPE = CadError; } + +const __EMPTY = {}; + +CadError.KIND = { + INTERNAL_ERROR: 'INTERNAL_ERROR', + UNSUPPORTED_CASE: 'UNSUPPORTED_CASE', + INVALID_INPUT: 'INVALID_INPUT', +}; \ No newline at end of file