mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-07 17:04:58 +01:00
error handling
This commit is contained in:
parent
4836b89595
commit
f5b6c20b97
5 changed files with 79 additions and 52 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,14 @@
|
|||
color: lightgoldenrodyellow;
|
||||
}
|
||||
|
||||
.errorCode {
|
||||
.userErrorMessage, .errorCode {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.userErrorMessage {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.errorCode {
|
||||
font-style: italic;
|
||||
}
|
||||
|
|
@ -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',
|
||||
};
|
||||
Loading…
Reference in a new issue