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) {
for (let i = 0; i < listenerList.length; i++) {
const callback = listenerList[i];
try {
callback(data, oldValue);
} catch(e) {
console.error(e);
}
}
}
} finally {

View file

@ -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'
});
}
}

View file

@ -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 && <div className={ls.errorMessage}>
performing operation with current parameters leads to an invalid object
(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>}
</Stack>
@ -95,17 +96,22 @@ export default class Wizard extends React.Component {
this.props.onOK(this.params);
this.onClose();
} catch (error) {
let state = {
let stateUpdate = {
hasError: true
};
if (!isTCADError(error)) {
console.error('internal error while performing operation');
console.error(error);
} else {
state.cadCode = error.code;
console.log(error);
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);
}
this.setState(state);
Object.assign(stateUpdate, {code, userMessage});
}
if (printError) {
console.error(error);
}
this.setState(stateUpdate);
}
};

View file

@ -2,6 +2,14 @@
color: lightgoldenrodyellow;
}
.errorCode {
.userErrorMessage, .errorCode {
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 {
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',
};