mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-16 05:23:19 +01:00
automated tests support
This commit is contained in:
parent
050e2c2348
commit
491e3695d5
28 changed files with 462 additions and 60 deletions
|
|
@ -2,3 +2,8 @@ export default function capitalize(str) {
|
|||
if (!str) return;
|
||||
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||
}
|
||||
|
||||
export function decapitalize(str) {
|
||||
if (!str) return;
|
||||
return str.charAt(0).toLowerCase() + str.slice(1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ export function readBrep(data) {
|
|||
format: 'verbose',
|
||||
data: normalizeTesselationData(faceData.tess, inverted, faceData.surface.normal)
|
||||
};
|
||||
bb._face.data.productionInfo = faceData.productionInfo;
|
||||
if (faceData.ref !== undefined) {
|
||||
bb._face.data.externals = {
|
||||
ref: faceData.ref
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import {enableAnonymousActionHint} from './anonHint';
|
||||
import * as stream from 'lstream';
|
||||
import {DEBUG_FLAGS} from '../debugFlags';
|
||||
|
||||
export function activate(context) {
|
||||
|
||||
|
|
@ -23,6 +24,9 @@ export function activate(context) {
|
|||
return;
|
||||
}
|
||||
if (state.enabled) {
|
||||
if (DEBUG_FLAGS.ACTION_RUN) {
|
||||
console.log("RUNNING ACTION: " + id);
|
||||
}
|
||||
runner(context, data);
|
||||
} else {
|
||||
showAnonymousActionHint(id);
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ export function readSketchContour(contour, face) {
|
|||
ab = ab.map(v => tr.apply(v).data());
|
||||
path.push({TYPE: CURVE_TYPES.SEGMENT, a: ab[0], b: ab[1]});
|
||||
}
|
||||
path[path.length - 1].id = s.id;
|
||||
});
|
||||
return path;
|
||||
}
|
||||
|
|
|
|||
5
web/app/cad/debugFlags.js
Normal file
5
web/app/cad/debugFlags.js
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
export const DEBUG_FLAGS = {
|
||||
|
||||
ACTION_RUN: false
|
||||
|
||||
};
|
||||
|
|
@ -10,6 +10,7 @@ import {toLoops} from '../brep/io/brepLoopsFormat';
|
|||
import {contributeComponent} from './dom/components/ContributedComponents';
|
||||
import BrepDebuggerWindow, {BREP_DEBUG_WINDOW_VISIBLE} from '../brep/debug/debugger/BrepDebuggerWindow';
|
||||
import curveTess from '../brep/geom/impl/curve/curve-tess';
|
||||
import {DEBUG_FLAGS} from './debugFlags';
|
||||
|
||||
|
||||
export function activate({bus, services, streams}) {
|
||||
|
|
@ -17,7 +18,9 @@ export function activate({bus, services, streams}) {
|
|||
addDebugSelectors(services);
|
||||
services.action.registerActions(DebugActions);
|
||||
services.menu.registerMenus([DebugMenuConfig]);
|
||||
|
||||
services.debug = {
|
||||
FLAGS: DEBUG_FLAGS
|
||||
};
|
||||
streams.ui.controlBars.left.update(actions => [...actions, 'menu.debug']);
|
||||
|
||||
bus.enableState(BREP_DEBUG_WINDOW_VISIBLE, false);
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ export default function startApplication(callback) {
|
|||
];
|
||||
|
||||
let allPlugins = [...preUIPlugins, ...plugins];
|
||||
context.services.plugin = createPluginService(allPlugins, context);
|
||||
|
||||
defineStreams(allPlugins, context);
|
||||
|
||||
|
|
@ -98,3 +99,16 @@ export function activatePlugins(plugins, context) {
|
|||
}
|
||||
}
|
||||
|
||||
function createPluginService(plugins, context) {
|
||||
function disposePlugins() {
|
||||
for (let plugin of plugins) {
|
||||
if (plugin.dispose) {
|
||||
plugin.dispose(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
disposePlugins
|
||||
};
|
||||
}
|
||||
|
|
@ -72,6 +72,10 @@ export class MFace extends MObject {
|
|||
}
|
||||
}
|
||||
|
||||
get defaultSketchId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
setSketch(sketch) {
|
||||
|
||||
if (!this.isPlaneBased) {
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@ export function activate(context) {
|
|||
|
||||
const {streams, services} = context;
|
||||
|
||||
const [id, params] = parseHintsFromLocation();
|
||||
const [id, hints] = parseHintsFromLocation();
|
||||
|
||||
processParams(params, context);
|
||||
processParams(hints, context);
|
||||
|
||||
const sketchNamespace = id + '.sketch.';
|
||||
const sketchStorageNamespace = STORAGE_PREFIX + sketchNamespace;
|
||||
|
|
@ -40,20 +40,33 @@ export function activate(context) {
|
|||
let dataStr = services.storage.get(services.project.projectStorageKey());
|
||||
if (dataStr) {
|
||||
let data = JSON.parse(dataStr);
|
||||
if (data.history) {
|
||||
services.craft.reset(data.history);
|
||||
}
|
||||
if (data.expressions) {
|
||||
services.expressions.load(data.expressions);
|
||||
}
|
||||
loadData(data);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function loadData(data) {
|
||||
if (data.history) {
|
||||
services.craft.reset(data.history);
|
||||
}
|
||||
if (data.expressions) {
|
||||
services.expressions.load(data.expressions);
|
||||
}
|
||||
}
|
||||
|
||||
function empty() {
|
||||
loadData({
|
||||
history: [],
|
||||
expressions: ""
|
||||
});
|
||||
}
|
||||
|
||||
services.project = {
|
||||
id, sketchStorageKey, projectStorageKey, sketchStorageNamespace, getSketchURL, save, load
|
||||
id, sketchStorageKey, projectStorageKey, sketchStorageNamespace, getSketchURL, save, load, empty,
|
||||
hints
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,3 +24,7 @@ export function activate({streams, services}) {
|
|||
// services.viewer.setCameraMode(CAMERA_MODE.ORTHOGRAPHIC);
|
||||
|
||||
}
|
||||
|
||||
export function dispose(ctx) {
|
||||
ctx.services.viewer.dispose();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,6 +58,10 @@ export default class Viewer {
|
|||
this.setCameraMode(CAMERA_MODE.PERSPECTIVE);
|
||||
}
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.sceneSetup.renderer.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
export const CAMERA_MODE = {
|
||||
|
|
|
|||
3
web/app/cad/sketch/sketchObjectGlobalId.js
Normal file
3
web/app/cad/sketch/sketchObjectGlobalId.js
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export default function globalSketchId(sketchId, id) {
|
||||
return sketchId + "/" + id;
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import * as math from '../../math/math'
|
|||
import {HashTable} from '../../utils/hashmap'
|
||||
import {Constraints} from '../../sketcher/parametric';
|
||||
import Joints from '../../../../modules/gems/joints';
|
||||
import sketchObjectGlobalId from './sketchObjectGlobalId';
|
||||
|
||||
class SketchGeom {
|
||||
|
||||
|
|
@ -43,9 +44,7 @@ class SketchGeom {
|
|||
}
|
||||
|
||||
export function ReadSketch(sketch, sketchId, readConstructionSegments) {
|
||||
function getID(obj) {
|
||||
return sketchId + "/" + obj.id;
|
||||
}
|
||||
const getID = obj => sketchObjectGlobalId(sketchId, obj.id);
|
||||
const out = new SketchGeom();
|
||||
|
||||
let coiJoints = new Joints();
|
||||
|
|
|
|||
|
|
@ -23,8 +23,10 @@ export function activate(ctx) {
|
|||
if (evt.key.indexOf(prefix) < 0) return;
|
||||
let sketchFaceId = evt.key.substring(prefix.length);
|
||||
let sketchFace = services.cadRegistry.findFace(sketchFaceId);
|
||||
updateSketchForFace(sketchFace);
|
||||
services.viewer.requestRender();
|
||||
if (sketchFace) {
|
||||
updateSketchForFace(sketchFace);
|
||||
services.viewer.requestRender();
|
||||
}
|
||||
};
|
||||
|
||||
services.storage.addListener(onSketchUpdate);
|
||||
|
|
@ -70,7 +72,7 @@ export function activate(ctx) {
|
|||
}
|
||||
|
||||
function updateSketchForFace(mFace) {
|
||||
let sketch = readSketch(mFace.id);
|
||||
let sketch = readSketch(mFace.defaultSketchId);
|
||||
mFace.setSketch(sketch);
|
||||
streams.sketcher.update.next(mFace);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,5 +38,6 @@ export default {
|
|||
},
|
||||
math: {
|
||||
vec
|
||||
}
|
||||
},
|
||||
THREE: THREE
|
||||
}
|
||||
|
|
@ -85,6 +85,10 @@ Viewer.prototype.dispose = function() {
|
|||
this.toolManager.dispose();
|
||||
};
|
||||
|
||||
Viewer.prototype.isDisposed = function() {
|
||||
return this.canvas === null;
|
||||
};
|
||||
|
||||
Viewer.prototype.setTransformation = function(a, b, c, d, e, f, zoom) {
|
||||
this.transformation = [a, b, c, d, e, f];
|
||||
this.scale = zoom;
|
||||
|
|
@ -190,9 +194,11 @@ Viewer.prototype._createServiceLayers = function() {
|
|||
};
|
||||
|
||||
Viewer.prototype.refresh = function() {
|
||||
var viewer = this;
|
||||
window.requestAnimationFrame( function() {
|
||||
viewer.repaint();
|
||||
const viewer = this;
|
||||
window.requestAnimationFrame(function() {
|
||||
if (!viewer.isDisposed()) {
|
||||
viewer.repaint();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
import * as test from '../test';
|
||||
|
||||
export default {
|
||||
|
||||
testPlanes: env => {
|
||||
test.modeller(env.testTPI(tpi => {
|
||||
|
||||
tpi.services.action.run('PLANE');
|
||||
|
||||
console.dir(tpi);
|
||||
|
||||
env.done();
|
||||
}));
|
||||
},
|
||||
|
||||
};
|
||||
40
web/test/cases/craftExtrude.js
Normal file
40
web/test/cases/craftExtrude.js
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import {assertEmpty, assertEquals, assertFaceIsPlane, assertTrue} from '../utils/asserts';
|
||||
import sketchObjectGlobalId from '../../app/cad/sketch/sketchObjectGlobalId';
|
||||
|
||||
export const TEST_MODE = 'modellerUI';
|
||||
|
||||
export function testExtrudeFromSketch2(env, ui) {
|
||||
// globalSketchId(sketchId, seg1.id)
|
||||
}
|
||||
|
||||
export function testExtrudeFromSketch(env, ui) {
|
||||
ui.openWizard('PLANE');
|
||||
ui.wizardOK();
|
||||
ui.selectFaces([0, 0, -10], [0, 0, 10]);
|
||||
let sketchedFace = ui.context.services.selection.face.single;
|
||||
let sketcherUI = ui.openSketcher();
|
||||
let seg1 = sketcherUI.addSegment(-100, -100, 100, -100);
|
||||
let seg2 = sketcherUI.addSegment(100, -100, 100, 100);
|
||||
let seg3 = sketcherUI.addSegment(100, 100, -100, 100);
|
||||
let seg4 = sketcherUI.addSegment(-100, 100, -100, -100);
|
||||
|
||||
ui.commitSketch();
|
||||
|
||||
ui.selectFaces([0, 0, -10], [0, 0, 10]);
|
||||
|
||||
|
||||
ui.openWizard('EXTRUDE');
|
||||
ui.wizardContext.updateParam('value', 200);
|
||||
ui.wizardOK();
|
||||
|
||||
let [leftFace] = ui.rayCastFaces([-200, 0, 100], [200, 0, 100]);
|
||||
|
||||
let sketchId = sketchedFace.defaultSketchId;
|
||||
|
||||
assertTrue(leftFace.brepFace.data.productionInfo.originatedFromPrimitive,
|
||||
sketchObjectGlobalId(sketchId, seg3.id));
|
||||
|
||||
assertTrue(leftFace.brepFace.data.productionInfo.role, "sweep");
|
||||
|
||||
env.done();
|
||||
}
|
||||
81
web/test/cases/craftPlane.js
Normal file
81
web/test/cases/craftPlane.js
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
import {assertEmpty, assertEquals, assertFaceIsPlane, assertTrue} from '../utils/asserts';
|
||||
|
||||
export const TEST_MODE = 'modellerUI';
|
||||
|
||||
export function testCreatePlaneAtOriginDefaultXY(env, ui) {
|
||||
ui.openWizard('PLANE');
|
||||
ui.wizardOK();
|
||||
assertFaceIsPlane(ui.rayCastFaces([0, 0, -10], [0, 0, 10])[0]);
|
||||
env.done();
|
||||
}
|
||||
|
||||
export function testCreatePlaneAtOriginXZ(env, ui) {
|
||||
ui.openWizard('PLANE');
|
||||
ui.wizardContext.updateParam('orientation', 'XZ');
|
||||
ui.wizardOK();
|
||||
assertFaceIsPlane(ui.rayCastFaces([0, -10, 0], [0, 10, 0])[0]);
|
||||
env.done();
|
||||
}
|
||||
|
||||
export function testCreatePlaneAtZY(env, ui) {
|
||||
ui.openWizard('PLANE');
|
||||
ui.wizardContext.updateParam('orientation', 'ZY');
|
||||
ui.wizardOK();
|
||||
assertFaceIsPlane(ui.rayCastFaces([-10, 0, 0], [10, 0, 0])[0]);
|
||||
env.done();
|
||||
}
|
||||
|
||||
export function testCreatePlaneAtOriginXYOffset(env, ui) {
|
||||
ui.openWizard('PLANE');
|
||||
ui.wizardContext.updateParam('depth', 100);
|
||||
ui.wizardOK();
|
||||
assertEmpty(ui.rayCastFaces([0, 0, -10], [0, 0, 10]));
|
||||
assertFaceIsPlane(ui.rayCastFaces([0, 0, 90], [0, 0, 110])[0]);
|
||||
env.done();
|
||||
}
|
||||
|
||||
export function testCreatePlaneAtOriginXZOffset(env, ui) {
|
||||
ui.openWizard('PLANE');
|
||||
ui.wizardContext.updateParam('orientation', 'XZ');
|
||||
ui.wizardContext.updateParam('depth', 100);
|
||||
ui.wizardOK();
|
||||
assertEmpty(ui.rayCastFaces([0, -10, 0], [0, 10, 0]));
|
||||
assertFaceIsPlane(ui.rayCastFaces([0, 90, 0], [0, 110, 0])[0]);
|
||||
env.done();
|
||||
}
|
||||
|
||||
export function testCreatePlaneAtOriginZYOffset(env, ui) {
|
||||
ui.openWizard('PLANE');
|
||||
ui.wizardContext.updateParam('orientation', 'ZY');
|
||||
ui.wizardContext.updateParam('depth', 100);
|
||||
ui.wizardOK();
|
||||
assertEmpty(ui.rayCastFaces([-10, 0, 0], [10, 0, 0]));
|
||||
assertFaceIsPlane(ui.rayCastFaces([90, 0, 0], [110, 0, 0])[0]);
|
||||
env.done();
|
||||
}
|
||||
|
||||
export function testCreatePlaneParallelToOther(env, ui) {
|
||||
ui.openWizard('PLANE');
|
||||
ui.wizardContext.updateParam('orientation', 'ZY');
|
||||
ui.wizardContext.updateParam('depth', 100);
|
||||
ui.wizardOK();
|
||||
|
||||
assertEmpty(ui.rayCastFaces([-10, 0, 0], [10, 0, 0]));
|
||||
let captured = ui.rayCastFaces([90, 0, 0], [210, 0, 0]);
|
||||
assertTrue(captured.length === 1);
|
||||
|
||||
let baseFace = captured[0];
|
||||
|
||||
ui.openWizard('PLANE');
|
||||
ui.wizardContext.updateParam('parallelTo', baseFace.id);
|
||||
ui.wizardContext.updateParam('depth', 100);
|
||||
ui.wizardOK();
|
||||
|
||||
captured = ui.rayCastFaces([90, 0, 0], [210, 0, 0]);
|
||||
assertTrue(captured.length === 2);
|
||||
assertTrue(captured[0].id === baseFace.id);
|
||||
assertTrue(captured[1].id !== baseFace.id);
|
||||
|
||||
env.done();
|
||||
}
|
||||
|
||||
|
|
@ -11,7 +11,7 @@ export class Menu {
|
|||
.on('click', (e) => this.popup.hide())
|
||||
.on('contextmenu', (e) => {
|
||||
const target = $(e.target).closest('.right-click-menu');
|
||||
if (target.length == 0) return true;
|
||||
if (target.length === 0) return true;
|
||||
return this.onShowMenu(e, target);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
10
web/test/modes.js
Normal file
10
web/test/modes.js
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import * as test from './test';
|
||||
import modellerUISubject from './utils/subjects/modeller/modellerUISubject';
|
||||
|
||||
export const modellerUI = func => env => {
|
||||
test.emptyModeller(env.test(win => {
|
||||
let subject = modellerUISubject(win.__CAD_APP);
|
||||
func(env, subject);
|
||||
}));
|
||||
};
|
||||
|
||||
|
|
@ -4,17 +4,19 @@ body {
|
|||
}
|
||||
|
||||
#sandbox {
|
||||
position: fixed;
|
||||
border: 3px plum solid;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 60%;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: 40%;
|
||||
max-height: 500px;
|
||||
width: 40%;
|
||||
max-width: 500px;
|
||||
background-color: #233930;
|
||||
}
|
||||
|
||||
#test-list {
|
||||
width: 100%;
|
||||
height: ~"calc(40% - 22px)";
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
#sandbox iframe {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
import * as test from './test';
|
||||
import * as modes from './modes';
|
||||
|
||||
export default {
|
||||
SketcherObjects: [
|
||||
TestCase('segment'),
|
||||
|
|
@ -20,7 +23,8 @@ export default {
|
|||
],
|
||||
|
||||
Craft: [
|
||||
TestCase('craft'),
|
||||
TestCase('craftPlane'),
|
||||
TestCase('craftExtrude'),
|
||||
],
|
||||
|
||||
BREP: [
|
||||
|
|
@ -36,11 +40,22 @@ export default {
|
|||
};
|
||||
|
||||
function TestCase(name) {
|
||||
let tests = require('./cases/' + name).default;
|
||||
tests = Object.keys(tests).filter(key => key.startsWith('test')).map(key => ({
|
||||
name: key,
|
||||
func: tests[key]
|
||||
}));
|
||||
let testModule = require('./cases/' + name);
|
||||
let tests;
|
||||
function registerTests(testsHolder, helperWrapper) {
|
||||
tests = testsHolder;
|
||||
tests = Object.keys(tests).filter(key => key.startsWith('test')).map(key => ({
|
||||
name: key,
|
||||
func: helperWrapper(tests[key])
|
||||
}));
|
||||
|
||||
}
|
||||
let mode = modes[testModule.TEST_MODE];
|
||||
if (mode) {
|
||||
registerTests(testModule, mode);
|
||||
} else {
|
||||
registerTests(testModule.default, func => env => func(env));
|
||||
}
|
||||
return {
|
||||
name, tests
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,18 +52,18 @@ export class TestEnv {
|
|||
}
|
||||
}
|
||||
|
||||
testTPI(testBlock) {
|
||||
return this.test(function(win, app) {
|
||||
testBlock(app.TPI);
|
||||
});
|
||||
}
|
||||
|
||||
assertTrue(stmt, msg) {
|
||||
if (!stmt) {
|
||||
this.fail('assertTrue fails.', msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
assertEmpty(array, msg) {
|
||||
if (array.length !== 0) {
|
||||
this.fail('assertEmpty fails. Array length = ' + array.length, msg);
|
||||
}
|
||||
}
|
||||
|
||||
assertFalse(stmt, msg) {
|
||||
if (stmt) {
|
||||
this.fail('assertFalse fails.', msg);
|
||||
|
|
@ -95,7 +95,7 @@ export class TestEnv {
|
|||
assertData(expected, actual) {
|
||||
const expectedJSON = JSON.stringify(expected).replace(/\s/g, '');
|
||||
const actualJSON = JSON.stringify(actual).replace(/\s/g, '');
|
||||
if (actualJSON != expectedJSON) {
|
||||
if (actualJSON !== expectedJSON) {
|
||||
console.log('EXPECTED:');
|
||||
console.log(this.prettyJSON(expected));
|
||||
console.log('ACTUAL:');
|
||||
|
|
@ -141,7 +141,7 @@ function checkSimilarity(data1, data2) {
|
|||
const info1 = info(data1);
|
||||
const info2 = info(data2);
|
||||
console.log(info1 + " : " + info2);
|
||||
return info1 == info2;
|
||||
return info1 === info2;
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -174,8 +174,22 @@ export function sketch(callback) {
|
|||
load('/sketcher.html#' + TEST_PROJECT, callback, SKETCHER_API);
|
||||
}
|
||||
|
||||
let ModellerWin = null;
|
||||
export function modeller(callback) {
|
||||
load('/index.html#' + TEST_PROJECT, callback, MODELLER_API);
|
||||
if (ModellerWin != null) {
|
||||
ModellerWin.__CAD_APP.services.project.empty();
|
||||
callback(ModellerWin, MODELLER_API(ModellerWin));
|
||||
return;
|
||||
}
|
||||
load('/index.html#' + TEST_PROJECT, (win, api) => {
|
||||
ModellerWin = win;
|
||||
let ctx = win.__CAD_APP;
|
||||
ctx.streams.lifecycle.projectLoaded.attach(loaded => {
|
||||
if (loaded) {
|
||||
callback(win, api);
|
||||
}
|
||||
})
|
||||
}, MODELLER_API);
|
||||
}
|
||||
|
||||
export function emptyModeller(callback) {
|
||||
|
|
|
|||
49
web/test/utils/asserts.js
Normal file
49
web/test/utils/asserts.js
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import {FailError} from '../test';
|
||||
|
||||
export function fail(msg, optionalMsg) {
|
||||
throw new FailError(msg + (optionalMsg === undefined ? '' : ' ' + optionalMsg));
|
||||
}
|
||||
|
||||
export function assertTrue(stmt, msg) {
|
||||
if (!stmt) {
|
||||
fail('assertTrue fails.', msg);
|
||||
}
|
||||
}
|
||||
|
||||
export function assertEmpty(array, msg) {
|
||||
if (array.length !== 0) {
|
||||
fail('assertEmpty fails. Array length = ' + array.length, msg);
|
||||
}
|
||||
}
|
||||
|
||||
export function assertFalse(stmt, msg) {
|
||||
if (stmt) {
|
||||
fail('assertFalse fails.', msg);
|
||||
}
|
||||
}
|
||||
|
||||
export function assertEquals(expected, actual, msg) {
|
||||
if (expected !== actual) {
|
||||
fail('assertEquals: Expected: ' + expected + ' but was ' + actual, msg);
|
||||
}
|
||||
}
|
||||
|
||||
export function assertFloatEquals(expected, actual, msg) {
|
||||
if (Math.abs(expected - actual) >= 1E-6) {
|
||||
fail('assertFloatEquals: Expected: ' + expected + ' but was ' + actual, msg);
|
||||
}
|
||||
}
|
||||
|
||||
export function assertPointXY2DEquals(expectedX, expectedY, actual, msg) {
|
||||
if (actual.x !== expectedX || actual.y !== expectedY) {
|
||||
fail('assertPoint2DEquals: Expected: (' + expectedX + ', ' + expectedY + ') but was (' + actual.x + ', ' + actual.y + ')', msg);
|
||||
}
|
||||
}
|
||||
|
||||
export function assertPoint2DEquals(expected, actial, msg) {
|
||||
this.assertPointXY2DEquals(expected.x, expected.y, actial, msg);
|
||||
}
|
||||
|
||||
export function assertFaceIsPlane(face) {
|
||||
assertTrue(face.shell.surfacePrototype !== undefined);
|
||||
}
|
||||
|
|
@ -46,6 +46,14 @@ export function addSegment(app, aX, aY, bX, bY) {
|
|||
return segment;
|
||||
}
|
||||
|
||||
export function addSegmentInModel(app, aX, aY, bX, bY) {
|
||||
|
||||
[aX, aY] = modelToScreen(app.viewer, aX, aY);
|
||||
[bX, bY] = modelToScreen(app.viewer, bX, bY);
|
||||
|
||||
return addSegment(app, aX, aY, bX, bY);
|
||||
}
|
||||
|
||||
export function polyLine(app) {
|
||||
app.actions['addMultiSegment'].action();
|
||||
const tool = app.viewer.toolManager.tool;
|
||||
|
|
@ -79,4 +87,13 @@ export class TestSegment {
|
|||
add(app) {
|
||||
return addSegment(app, this.a.x, this.a.y, this.b.x, this.b.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function modelToScreen(viewer, x, y) {
|
||||
|
||||
let modelToScreenMx = viewer.screenToModelMatrix.invert();
|
||||
[x, y] = modelToScreenMx.apply3([x, y, 0]);
|
||||
x /= viewer.retinaPxielRatio;
|
||||
y = (viewer.canvas.height - y) / viewer.retinaPxielRatio;
|
||||
return [x, y];
|
||||
}
|
||||
|
|
|
|||
95
web/test/utils/subjects/modeller/modellerUISubject.js
Normal file
95
web/test/utils/subjects/modeller/modellerUISubject.js
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
import {TestMouseEvent} from '../../mouse-event';
|
||||
import {getAttribute} from '../../../../../modules/scene/objectData';
|
||||
import {FACE} from '../../../../app/cad/scene/entites';
|
||||
import {createSubjectFromInPlaceSketcher} from './sketcherUISubject';
|
||||
|
||||
export default ctx => {
|
||||
|
||||
function openWizard(operationId) {
|
||||
ctx.services.action.run(operationId);
|
||||
}
|
||||
|
||||
function wizardOK() {
|
||||
ctx.services.wizard.applyWorkingRequest();
|
||||
}
|
||||
|
||||
function sceneMouseEvent(type, x, y) {
|
||||
let domEl = ctx.services.viewer.sceneSetup.domElement();
|
||||
let xMid = Math.round(domEl.offsetWidth / 2 + x);
|
||||
let yMid = Math.round(domEl.offsetHeight / 2 + y);
|
||||
domEl.dispatchEvent(mouseEvent(type, xMid, yMid));
|
||||
}
|
||||
|
||||
function clickOnScene(x, y) {
|
||||
sceneMouseEvent('mousedown', x, y);
|
||||
sceneMouseEvent('mouseup', x, y);
|
||||
}
|
||||
|
||||
function rayCast(from3, to3) {
|
||||
const THREE = ctx.services.tpi.THREE;
|
||||
let raycaster = new THREE.Raycaster();
|
||||
let from = new THREE.Vector3().fromArray(from3);
|
||||
let to = new THREE.Vector3().fromArray(to3);
|
||||
let dir = to.sub(from);
|
||||
let dist = dir.length();
|
||||
raycaster.set(from, dir.normalize());
|
||||
return raycaster.intersectObjects( ctx.services.cadScene.workGroup.children, true ).filter(h => h.distance <= dist);
|
||||
}
|
||||
|
||||
function rayCastFaces(from, to) {
|
||||
let models = rayCast(from, to).map(h => {
|
||||
if (h.face) {
|
||||
let faceV = getAttribute(h.face, FACE);
|
||||
if (faceV && faceV.model) {
|
||||
return faceV.model;
|
||||
}
|
||||
}
|
||||
});
|
||||
let out = [];
|
||||
models.forEach(m => {
|
||||
if (!!m && !out.includes(m)) {
|
||||
out.push(m);
|
||||
}
|
||||
});
|
||||
return out;
|
||||
}
|
||||
|
||||
function selectFaces(from, to) {
|
||||
rayCastFaces(from, to).forEach(face => ctx.services.pickControl.pick(face));
|
||||
}
|
||||
|
||||
function getWizardContext() {
|
||||
return ctx.streams.wizard.wizardContext.value
|
||||
}
|
||||
|
||||
function openSketcher() {
|
||||
ctx.services.action.run('EditFace');
|
||||
return createSubjectFromInPlaceSketcher(ctx);
|
||||
}
|
||||
|
||||
function commitSketch() {
|
||||
ctx.services.action.run('sketchSaveAndExit');
|
||||
}
|
||||
|
||||
return {
|
||||
context: ctx,
|
||||
openWizard, wizardOK, sceneMouseEvent, clickOnScene,
|
||||
rayCastFaces, selectFaces, openSketcher, commitSketch,
|
||||
get wizardContext() { return getWizardContext()}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function mouseEvent(type, x, y) {
|
||||
return new MouseEvent(type, {
|
||||
bubbles: true,
|
||||
screenX: x,
|
||||
screenY: y,
|
||||
clientX: x,
|
||||
clientY: y,
|
||||
pageX: x,
|
||||
pageY: y,
|
||||
offsetX: x,
|
||||
offsetY: y,
|
||||
});
|
||||
}
|
||||
26
web/test/utils/subjects/modeller/sketcherUISubject.js
Normal file
26
web/test/utils/subjects/modeller/sketcherUISubject.js
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import * as sketcher_utils from '../../../utils/sketcher-utils'
|
||||
import {decapitalize} from '../../../../../modules/gems/capitalize';
|
||||
|
||||
export function createSubjectFromInPlaceSketcher(ctx) {
|
||||
|
||||
|
||||
let actions = {};
|
||||
for (const actionId of Object.keys(ctx.streams.action.state)) {
|
||||
if (actionId.startsWith('sketch')) {
|
||||
let oldId = decapitalize(actionId.substring(6));
|
||||
actions[oldId] = {
|
||||
action: () => ctx.services.action.run(actionId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const oldStyleSketcherApp = {
|
||||
viewer: ctx.services.sketcher.inPlaceEditor.viewer,
|
||||
actions
|
||||
};
|
||||
|
||||
return {
|
||||
addSegment: sketcher_utils.addSegmentInModel.bind(this, oldStyleSketcherApp)
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in a new issue