error handling

This commit is contained in:
Val Erastov 2018-02-09 20:04:36 -08:00
parent 4836b89595
commit f5b6c20b97
5 changed files with 79 additions and 52 deletions

View file

@ -93,11 +93,7 @@ export default class Bus {
if (listenerList !== undefined) { if (listenerList !== undefined) {
for (let i = 0; i < listenerList.length; i++) { for (let i = 0; i < listenerList.length; i++) {
const callback = listenerList[i]; const callback = listenerList[i];
try {
callback(data, oldValue); callback(data, oldValue);
} catch(e) {
console.error(e);
}
} }
} }
} finally { } finally {

View file

@ -56,13 +56,12 @@ export function invert( shell ) {
} }
} }
shell.data.inverted = !shell.data.inverted; shell.data.inverted = !shell.data.inverted;
checkShellForErrors(shell, 'UNABLE_BOOLEAN_OPERAND_INVERSION');
}
function checkShellForErrors(shell, code) {
let errors = BREPValidator.validate(shell); let errors = BREPValidator.validate(shell);
if (errors.length !== 0) { 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) { if (edgeA.vertexA === edgeB.vertexA) {
// chooseBetweenEqualEdges(); // chooseBetweenEqualEdges();
// canEdgeBeTransferred(edge, face, opType) // 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) { } else if (edgeA.vertexA === edgeB.vertexB) {
invalid.add(edgeA); invalid.add(edgeA);
@ -500,8 +504,9 @@ function traverseFaces(face, callback) {
let twinFace = halfEdge.twin().loop.face; let twinFace = halfEdge.twin().loop.face;
if (twinFace === null) { if (twinFace === null) {
//this happened because there is no face created for a valid and legit detected loop //this happened because there is no face created for a valid and legit detected loop
throw new CadError('BOOLEAN_INVALID_RESULT', { throw new CadError({
code: 'UNABLE_FACE_EVOLVE', payload: {halfEdge} kind: CadError.KIND.INTERNAL_ERROR,
relatedTopoObjects: [halfEdge]
}); });
// BREP_DEBUG.markEdge("null face", halfEdge.twin()) // BREP_DEBUG.markEdge("null face", halfEdge.twin())
} else { } else {
@ -947,13 +952,17 @@ export function isInsideEnclose(normal, testee, inVec, outVec, strict){
if (strict && veq(outVec, testee)) { if (strict && veq(outVec, testee)) {
//TODO: improve error report //TODO: improve error report
throw new CadError('BOOLEAN_INVALID_RESULT'); throw new CadError({
relatedTopoObjects: [testee]
});
} }
let pivot = inVec.negate(); let pivot = inVec.negate();
if (strict && veq(pivot, testee)) { if (strict && veq(pivot, testee)) {
//TODO: improve error report //TODO: improve error report
throw new CadError('BOOLEAN_INVALID_RESULT'); throw new CadError({
relatedTopoObjects: [testee]
});
} }
let enclosureAngle = leftTurningMeasure(pivot, outVec, normal); let enclosureAngle = leftTurningMeasure(pivot, outVec, normal);
let testeeAngle = leftTurningMeasure(pivot, testee, normal); let testeeAngle = leftTurningMeasure(pivot, testee, normal);
@ -1148,7 +1157,7 @@ class FaceSolveData extends EdgeGraph {
super(); super();
this.face = face; this.face = face;
this.newEdges = []; this.newEdges = [];
this.errors = []; this.collidedEdges = [];
} }
markTransferredFrom(edge) { markTransferredFrom(edge) {
@ -1181,7 +1190,7 @@ class FaceSolveData extends EdgeGraph {
let opp = this.findOppositeEdge(he); let opp = this.findOppositeEdge(he);
if (opp) { if (opp) {
this.errors.push(edgeCollisionError(opp, he)); this.collidedEdges.push(opp, he);
} }
let list = this.vertexToEdge.get(he.vertexA); let list = this.vertexToEdge.get(he.vertexA);
@ -1191,7 +1200,7 @@ class FaceSolveData extends EdgeGraph {
} else { } else {
for (let ex of list) { for (let ex of list) {
if (he.vertexB === ex.vertexB && isSameEdge(he, ex)) { if (he.vertexB === ex.vertexB && isSameEdge(he, ex)) {
this.errors.push(edgeCollisionError(ex, he)); this.collidedEdges.push(ex, he);
// ex.attachManifold(he); // ex.attachManifold(he);
// return; // return;
} }
@ -1253,7 +1262,11 @@ function curveAndEdgeCoincident(curve, edge) {
if (!veq(pt1, pt2)) { if (!veq(pt1, pt2)) {
if (touches > 1) { if (touches > 1) {
//partial tangency should be handled before face-face intersection analysis //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; return false;
} }
@ -1275,21 +1288,19 @@ function edgeCollinearToFace(edge, face) {
return face.rayCast(edge.edge.curve.middlePoint()).inside; return face.rayCast(edge.edge.curve.middlePoint()).inside;
} }
function edgeCollisionError(e1, e2) {
return {
code: 'EDGE_COLLISION', payload: {e1, e2}
}
}
function checkFaceDataForError(facesData) { function checkFaceDataForError(facesData) {
if (facesData.find(f => f.errors.length !== 0)) { if (facesData.find(f => f.collidedEdges.length !== 0)) {
let payload = []; let relatedTopoObjects = [];
for (let faceData of facesData) { for (let faceData of facesData) {
for (let err of faceData.errors) { for (let err of faceData.collidedEdges) {
payload.push(err); 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'
});
} }
} }

View file

@ -11,10 +11,10 @@ import Button from 'ui/components/controls/Button';
import ButtonGroup from 'ui/components/controls/ButtonGroup'; import ButtonGroup from 'ui/components/controls/ButtonGroup';
import FaceSelectionControl from './FaceSelectionControl'; import FaceSelectionControl from './FaceSelectionControl';
import {CURRENT_SELECTION} from "../../../craft/wizard/wizardPlugin"; import {CURRENT_SELECTION} from "../../../craft/wizard/wizardPlugin";
import {isTCADError} from "../../../../utils/errors";
import ls from './Wizard.less'; import ls from './Wizard.less';
import RadioButtons, {RadioButton} from "ui/components/controls/RadioButtons"; import RadioButtons, {RadioButton} from "ui/components/controls/RadioButtons";
import CadError from '../../../../utils/errors';
export default class Wizard extends React.Component { export default class Wizard extends React.Component {
@ -59,7 +59,8 @@ export default class Wizard extends React.Component {
{this.state.hasError && <div className={ls.errorMessage}> {this.state.hasError && <div className={ls.errorMessage}>
performing operation with current parameters leads to an invalid object performing operation with current parameters leads to an invalid object
(manifold / self-intersecting / zero-thickness / complete degeneration or unsupported cases) (manifold / self-intersecting / zero-thickness / complete degeneration or unsupported cases)
{this.state.code && <span className={ls.errorCode}>{this.state.code}</span>} {this.state.code && <div className={ls.errorCode}>{this.state.code}</div>}
{this.state.userMessage && <div className={ls.userErrorMessage}>{this.state.userMessage}</div>}
</div>} </div>}
</Stack> </Stack>
@ -95,17 +96,22 @@ export default class Wizard extends React.Component {
this.props.onOK(this.params); this.props.onOK(this.params);
this.onClose(); this.onClose();
} catch (error) { } catch (error) {
let state = { let stateUpdate = {
hasError: true hasError: true
}; };
if (!isTCADError(error)) { let printError = true;
console.error('internal error while performing operation'); if (error.TYPE === CadError) {
console.error(error); let {code, userMessage, kind} = error;
} else { printError = !code;
state.cadCode = error.code; if (code && kind === CadError.KIND.INTERNAL_ERROR) {
console.log(error); console.warn('Operation Error Code: ' + code);
} }
this.setState(state); Object.assign(stateUpdate, {code, userMessage});
}
if (printError) {
console.error(error);
}
this.setState(stateUpdate);
} }
}; };

View file

@ -2,6 +2,14 @@
color: lightgoldenrodyellow; color: lightgoldenrodyellow;
} }
.errorCode { .userErrorMessage, .errorCode {
font-size: 0.9rem; font-size: 0.9rem;
} }
.userErrorMessage {
color: white;
}
.errorCode {
font-style: italic;
}

View file

@ -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 { export default class CadError extends Error {
constructor(code, payload) { constructor({kind, code, relatedTopoObjects, userMessage} = __EMPTY) {
super(code); super();
this.kind = kind || CadError.KIND.INTERNAL_ERROR;
this.code = code; this.code = code;
this.payload = payload; this.relatedTopoObjects = relatedTopoObjects;
this.type = ERROR_TYPE; 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',
};