From 5ba5b95023161669f68f5b6ce867dced5d2923c4 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Sun, 21 May 2017 03:24:50 +0100 Subject: [PATCH 001/371] download data prototype --- .../interface/dicomViewer/DicomViewer.js | 10 +++++----- .../webapp/js/components/widgets/AWidget.js | 19 +++++++++++++++---- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js b/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js index 6acabc150..ed434570e 100644 --- a/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js +++ b/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js @@ -28,6 +28,7 @@ define(function (require) { this.changeMode = this.changeMode.bind(this); this.changeOrientation = this.changeOrientation.bind(this); + this.download = this.download.bind(this); } loadSingleView() { @@ -75,16 +76,11 @@ define(function (require) { $("#" + this.props.id).on("dialogresizestop", function (event, ui) { camera.canvas = { - // width: ui.size.width - 260 - 30, - // height: ui.size.height - 30, width: container.offsetWidth, height: container.offsetHeight, }; camera.fitBox(2); - - // renderer.setSize(ui.size.width - 260 - 30, ui.size.height - 30); renderer.setSize(container.offsetWidth, container.offsetHeight); - }); /** @@ -920,6 +916,10 @@ define(function (require) { this.stackHelper.index = Math.floor(this.stackHelper.orientationMaxIndex/2); } + download(){ + window.location = this.props.files; + } + render() { var dicomViewerContent; if (this.state.mode == "single_view") { diff --git a/src/main/webapp/js/components/widgets/AWidget.js b/src/main/webapp/js/components/widgets/AWidget.js index ce835618e..3437194df 100644 --- a/src/main/webapp/js/components/widgets/AWidget.js +++ b/src/main/webapp/js/components/widgets/AWidget.js @@ -434,6 +434,13 @@ define(function (require) { })); } + addDownloadButton(downloadFunction){ + var that = this; + this.addButtonToTitleBar($("
").click(function () { + that.download(); + })); + } + /** * Makes the widget draggable or not * @@ -577,6 +584,9 @@ define(function (require) { this.container = this.$el.children().get(0); var dialogParent = this.$el.parent(); + this.modal = $(""); + this.modal.appendTo(this.$el); + this.spinner = GEPPETTO.ComponentFactory.renderComponent(React.createFactory(spinner)(), this.modal.get(0)); //add history this.showHistoryIcon(true); @@ -592,6 +602,11 @@ define(function (require) { //add help button this.addHelpButton(); + //add download button + if (super.download) { + this.addDownloadButton(super.download); + } + // initialize content this.size = this.state.defaultSize; this.position = this.state.defaultPosition; @@ -642,10 +657,6 @@ define(function (require) { }); } - // setController(controller) { - // this.controller = controller; - // } - showHistoryIcon(show) { var that = this; if (show && this.$el.parent().find(".history-icon").length == 0) { From b046bbfe59c4074151db00f59cc2b0c661a8fd49 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Sun, 21 May 2017 03:31:44 +0100 Subject: [PATCH 002/371] revert spinner tests --- src/main/webapp/js/components/widgets/AWidget.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/webapp/js/components/widgets/AWidget.js b/src/main/webapp/js/components/widgets/AWidget.js index 3437194df..10f932f4c 100644 --- a/src/main/webapp/js/components/widgets/AWidget.js +++ b/src/main/webapp/js/components/widgets/AWidget.js @@ -584,10 +584,6 @@ define(function (require) { this.container = this.$el.children().get(0); var dialogParent = this.$el.parent(); - this.modal = $(""); - this.modal.appendTo(this.$el); - this.spinner = GEPPETTO.ComponentFactory.renderComponent(React.createFactory(spinner)(), this.modal.get(0)); - //add history this.showHistoryIcon(true); From 66ba153aeedb7ae818dd0c35c05084bae8ec3317 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Sun, 21 May 2017 05:09:57 +0100 Subject: [PATCH 003/371] refactor,adding api, etc --- .../interface/dicomViewer/DicomViewer.js | 888 +----------------- .../dicomViewer/DicomViewerEngine.js | 859 +++++++++++++++++ 2 files changed, 882 insertions(+), 865 deletions(-) create mode 100644 src/main/webapp/js/components/interface/dicomViewer/DicomViewerEngine.js diff --git a/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js b/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js index ed434570e..a55ed16d2 100644 --- a/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js +++ b/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js @@ -2,18 +2,9 @@ define(function (require) { var React = require('react'); window.THREE = require('three'); - var AMI = require('./ami.min.js'); - - var LoadersVolume = AMI.default.Loaders.Volume; - var CamerasOrthographic = AMI.default.Cameras.Orthographic; - var ControlsOrthographic = AMI.default.Controls.TrackballOrtho; - var HelpersStack = AMI.default.Helpers.Stack; - var ControlsTrackball = AMI.default.Controls.Trackball; - var HelpersBoundingBox = AMI.default.Helpers.BoundingBox; - var ModelsStack = AMI.default.Models.Stack; - var HelpersLocalizer = AMI.default.Helpers.Localizer; require('./DicomViewer.less'); + var DicomViewerEngine = require('./DicomViewerEngine'); var AbstractComponent = require('../../AComponent'); @@ -22,7 +13,12 @@ define(function (require) { constructor(props) { super(props); + if (this.props.mode === undefined){ + this.props.mode = "quad_view"; + } + this.state = { + files: this.props.files, mode: this.props.mode }; @@ -31,864 +27,26 @@ define(function (require) { this.download = this.download.bind(this); } - loadSingleView() { - // Setup renderer - var container = document.getElementById(this.props.id).getElementsByClassName('dicomViewer')[0]; - var renderer = new THREE.WebGLRenderer({ - antialias: true, - }); - renderer.setSize(container.offsetWidth, container.offsetHeight); - renderer.setClearColor(0x353535, 1); - renderer.setPixelRatio(window.devicePixelRatio); - container.appendChild(renderer.domElement); - - // Setup scene - var scene = new THREE.Scene(); - - // Setup camera - var camera = new CamerasOrthographic( - container.clientWidth / -2, container.clientWidth / 2, - container.clientHeight / 2, container.clientHeight / -2, - 0.1, 10000); - - this.camera = camera; - - // Setup controls - var controls = new ControlsOrthographic(camera, container); - controls.staticMoving = true; - controls.noRotate = true; - camera.controls = controls; - - /** - * Handle window resize - */ - var _this = this; - function onWindowResize() { - camera.canvas = { - width: container.offsetWidth, - height: container.offsetHeight, - }; - camera.fitBox(2); - - renderer.setSize(container.offsetWidth, container.offsetHeight); - } - window.addEventListener('resize', onWindowResize, false); - - $("#" + this.props.id).on("dialogresizestop", function (event, ui) { - camera.canvas = { - width: container.offsetWidth, - height: container.offsetHeight, - }; - camera.fitBox(2); - renderer.setSize(container.offsetWidth, container.offsetHeight); - }); - - /** - * Start animation loop - */ - function animate() { - controls.update(); - renderer.render(scene, camera); - - // request new frame - requestAnimationFrame(function () { - if (_this.state.mode == "single_view") { - animate(); - } - }); - } - animate(); - - // Setup loader - var _this = this; - var loader = new LoadersVolume(container); - loader.load(this.props.files) - .then(function () { - // merge files into clean series/stack/frame structure - var series = loader.data[0].mergeSeries(loader.data); - var stack = series[0].stack[0]; - loader.free(); - loader = null; - // be carefull that series and target stack exist! - var stackHelper = new HelpersStack(stack); - _this.stackHelper = stackHelper; - // stackHelper.orientation = 2; - stackHelper.index = Math.floor(stack._dimensionsIJK.z/2); - - // tune bounding box - stackHelper.bbox.visible = false; - - // tune slice border - stackHelper.border.color = 0xFF9800; - // stackHelper.border.visible = false; - - scene.add(stackHelper); - - // hook up callbacks - controls.addEventListener('OnScroll', function (e) { - if (e.delta > 0) { - if (stackHelper.index >= stackHelper.orientationMaxIndex - 1) { - return false; - } - stackHelper.index += 1; - } else { - if (stackHelper.index <= 0) { - return false; - } - stackHelper.index -= 1; - } - - }); - - // center camera and interactor to center of bouding box - // for nicer experience - // set camera - var worldbb = stack.worldBoundingBox(); - var lpsDims = new THREE.Vector3( - worldbb[1] - worldbb[0], - worldbb[3] - worldbb[2], - worldbb[5] - worldbb[4] - ); - - // box: {halfDimensions, center} - var box = { - center: stack.worldCenter().clone(), - halfDimensions: - new THREE.Vector3(lpsDims.x + 10, lpsDims.y + 10, lpsDims.z + 10), - }; - - // init and zoom - var canvas = { - width: container.clientWidth, - height: container.clientHeight, - }; - - camera.directions = [stack.xCosine, stack.yCosine, stack.zCosine]; - camera.box = box; - camera.canvas = canvas; - camera.update(); - - // Not working properly. See issue: https://github.com/FNNDSC/ami/issues/120 - //camera.fitBox(2, 2); - camera.fitBox(2); - }) - .catch(function (error) { - window.console.log('oops... something went wrong...'); - window.console.log(error); - }); + setData(files) { + this.setState({ files: files }); } - loadQuadView() { - let ready = false; - // 3d renderer - let r0 = { - domId: 'r0', - domElement: null, - renderer: null, - color: 0x212121, - targetID: 0, - camera: null, - controls: null, - scene: null, - light: null, - }; - - // 2d axial renderer - let r1 = { - domId: 'r1', - domElement: null, - renderer: null, - color: 0x121212, - sliceOrientation: 'axial', - sliceColor: 0xFF1744, - targetID: 1, - camera: null, - controls: null, - scene: null, - light: null, - stackHelper: null, - localizerHelper: null, - localizerScene: null, - }; - - // 2d sagittal renderer - let r2 = { - domId: 'r2', - domElement: null, - renderer: null, - color: 0x121212, - sliceOrientation: 'sagittal', - sliceColor: 0xFFEA00, - targetID: 2, - camera: null, - controls: null, - scene: null, - light: null, - stackHelper: null, - localizerHelper: null, - localizerScene: null, - }; - - - // 2d coronal renderer - let r3 = { - domId: 'r3', - domElement: null, - renderer: null, - color: 0x121212, - sliceOrientation: 'coronal', - sliceColor: 0x76FF03, - targetID: 3, - camera: null, - controls: null, - scene: null, - light: null, - stackHelper: null, - localizerHelper: null, - localizerScene: null, - }; - - // data to be loaded - // let dataInfo = [ - // ['adi1', { - // location: - // 'https://cdn.rawgit.com/FNNDSC/data/master/dicom/adi_brain/mesh.stl', - // label: 'Left', - // loaded: false, - // material: null, - // materialFront: null, - // materialBack: null, - // mesh: null, - // meshFront: null, - // meshBack: null, - // color: 0xe91e63, - // opacity: 0.7, - // }], - // ['adi2', { - // location: - // 'https://cdn.rawgit.com/FNNDSC/data/master/dicom/adi_brain/mesh2.stl', - // label: 'Right', - // loaded: false, - // material: null, - // materialFront: null, - // materialBack: null, - // mesh: null, - // meshFront: null, - // meshBack: null, - // color: 0x03a9f4, - // opacity: 1, - // }], - // ]; - // let data = new Map(dataInfo); - - // extra variables to show mesh plane intersections in 2D renderers - let sceneClip = new THREE.Scene(); - let clipPlane1 = new THREE.Plane(new THREE.Vector3(0, 0, 0), 0); - let clipPlane2 = new THREE.Plane(new THREE.Vector3(0, 0, 0), 0); - let clipPlane3 = new THREE.Plane(new THREE.Vector3(0, 0, 0), 0); - - function initRenderer3D(renderObj) { - // renderer - renderObj.domElement = document.getElementById(renderObj.domId); - renderObj.renderer = new THREE.WebGLRenderer({ - antialias: true, - }); - renderObj.renderer.setSize( - renderObj.domElement.clientWidth, renderObj.domElement.clientHeight); - renderObj.renderer.setClearColor(renderObj.color, 1); - renderObj.renderer.domElement.id = renderObj.targetID; - renderObj.domElement.appendChild(renderObj.renderer.domElement); - - // camera - renderObj.camera = new THREE.PerspectiveCamera( - 45, renderObj.domElement.clientWidth / renderObj.domElement.clientHeight, - 0.1, 100000); - renderObj.camera.position.x = 250; - renderObj.camera.position.y = 250; - renderObj.camera.position.z = 250; - - // controls - renderObj.controls = new ControlsTrackball( - renderObj.camera, renderObj.domElement); - renderObj.controls.rotateSpeed = 5.5; - renderObj.controls.zoomSpeed = 1.2; - renderObj.controls.panSpeed = 0.8; - renderObj.controls.staticMoving = true; - renderObj.controls.dynamicDampingFactor = 0.3; - - // scene - renderObj.scene = new THREE.Scene(); - - // light - renderObj.light = new THREE.DirectionalLight(0xffffff, 1); - renderObj.light.position.copy(renderObj.camera.position); - renderObj.scene.add(renderObj.light); - - } - - function initRenderer2D(rendererObj) { - // renderer - rendererObj.domElement = document.getElementById(rendererObj.domId); - rendererObj.renderer = new THREE.WebGLRenderer({ - antialias: true, - }); - rendererObj.renderer.autoClear = false; - rendererObj.renderer.localClippingEnabled = true; - rendererObj.renderer.setSize( - rendererObj.domElement.clientWidth, rendererObj.domElement.clientHeight); - rendererObj.renderer.setClearColor(0x121212, 1); - rendererObj.renderer.domElement.id = rendererObj.targetID; - rendererObj.domElement.appendChild(rendererObj.renderer.domElement); - - // camera - rendererObj.camera = new CamerasOrthographic( - rendererObj.domElement.clientWidth / -2, - rendererObj.domElement.clientWidth / 2, - rendererObj.domElement.clientHeight / 2, - rendererObj.domElement.clientHeight / -2, - 1, 1000); - - // controls - rendererObj.controls = new ControlsOrthographic( - rendererObj.camera, rendererObj.domElement); - rendererObj.controls.staticMoving = true; - rendererObj.controls.noRotate = true; - rendererObj.camera.controls = rendererObj.controls; - - // scene - rendererObj.scene = new THREE.Scene(); - } - - function initHelpersStack(rendererObj, stack) { - rendererObj.stackHelper = new HelpersStack(stack); - rendererObj.stackHelper.bbox.visible = false; - rendererObj.stackHelper.borderColor = rendererObj.sliceColor; - rendererObj.stackHelper.slice.canvasWidth = - rendererObj.domElement.clientWidth; - rendererObj.stackHelper.slice.canvasHeight = - rendererObj.domElement.clientHeight; - - // set camera - let worldbb = stack.worldBoundingBox(); - let lpsDims = new THREE.Vector3( - (worldbb[1] - worldbb[0]) / 2, - (worldbb[3] - worldbb[2]) / 2, - (worldbb[5] - worldbb[4]) / 2 - ); - - // box: {halfDimensions, center} - let box = { - center: stack.worldCenter().clone(), - halfDimensions: - new THREE.Vector3(lpsDims.x + 10, lpsDims.y + 10, lpsDims.z + 10), - }; - - // init and zoom - let canvas = { - width: rendererObj.domElement.clientWidth, - height: rendererObj.domElement.clientHeight, - }; - - rendererObj.camera.directions = - [stack.xCosine, stack.yCosine, stack.zCosine]; - rendererObj.camera.box = box; - rendererObj.camera.canvas = canvas; - rendererObj.camera.orientation = rendererObj.sliceOrientation; - rendererObj.camera.update(); - rendererObj.camera.fitBox(2, 1); - - rendererObj.stackHelper.orientation = rendererObj.camera.stackOrientation; - rendererObj.stackHelper.index = - Math.floor(rendererObj.stackHelper.orientationMaxIndex / 2); - rendererObj.scene.add(rendererObj.stackHelper); - } - - function initHelpersLocalizer(rendererObj, stack, referencePlane, localizers) { - rendererObj.localizerHelper = new HelpersLocalizer( - stack, rendererObj.stackHelper.slice.geometry, referencePlane); - - for (let i = 0; i < localizers.length; i++) { - rendererObj.localizerHelper['plane' + (i + 1)] = localizers[i].plane; - rendererObj.localizerHelper['color' + (i + 1)] = localizers[i].color; - } - - rendererObj.localizerHelper.canvasWidth = - rendererObj.domElement.clientWidth; - rendererObj.localizerHelper.canvasHeight = - rendererObj.domElement.clientHeight; - - rendererObj.localizerScene = new THREE.Scene(); - rendererObj.localizerScene.add(rendererObj.localizerHelper); + loadView() { + if (this.state.mode == "single_view") { + DicomViewerEngine.loadSingleView(this); } - - var _this = this; - /** - * Init the quadview - */ - function init() { - /** - * Called on each animation frame - */ - function animate() { - // we are ready when both meshes have been loaded - if (ready) { - // render - r0.controls.update(); - r1.controls.update(); - r2.controls.update(); - r3.controls.update(); - - r0.light.position.copy(r0.camera.position); - r0.renderer.render(r0.scene, r0.camera); - - // r1 - r1.renderer.clear(); - r1.renderer.render(r1.scene, r1.camera); - // mesh - //r1.renderer.clearDepth(); - // data.forEach(function (object, key) { - // object.materialFront.clippingPlanes = [clipPlane1]; - // object.materialBack.clippingPlanes = [clipPlane1]; - // }); - //r1.renderer.render(sceneClip, r1.camera); - // localizer - r1.renderer.clearDepth(); - r1.renderer.render(r1.localizerScene, r1.camera); - - // r2 - r2.renderer.clear(); - r2.renderer.render(r2.scene, r2.camera); - // mesh - //r2.renderer.clearDepth(); - // data.forEach(function (object, key) { - // object.materialFront.clippingPlanes = [clipPlane2]; - // object.materialBack.clippingPlanes = [clipPlane2]; - // }); - //r2.renderer.render(sceneClip, r2.camera); - // localizer - r2.renderer.clearDepth(); - r2.renderer.render(r2.localizerScene, r2.camera); - - // r3 - r3.renderer.clear(); - r3.renderer.render(r3.scene, r3.camera); - // mesh - //r3.renderer.clearDepth(); - // data.forEach(function (object, key) { - // object.materialFront.clippingPlanes = [clipPlane3]; - // object.materialBack.clippingPlanes = [clipPlane3]; - // }); - //r3.renderer.render(sceneClip, r3.camera); - // localizer - r3.renderer.clearDepth(); - r3.renderer.render(r3.localizerScene, r3.camera); - } - - // request new frame - requestAnimationFrame(function () { - if (_this.state.mode == "quad_view") { - animate(); - } - }); - } - - // renderers - initRenderer3D(r0); - initRenderer2D(r1); - initRenderer2D(r2); - initRenderer2D(r3); - - // start rendering loop - animate(); + else if (this.state.mode == "quad_view") { + DicomViewerEngine.loadQuadView(this); } - - // init threeJS - init(); - - - // load sequence for each file - // instantiate the loader - // it loads and parses the dicom image - var _this = this; - let loader = new LoadersVolume(); - loader.load(this.props.files) - .then(function () { - let series = loader.data[0].mergeSeries(loader.data)[0]; - loader.free(); - loader = null; - // get first stack from series - let stack = series.stack[0]; - stack.prepare(); - - // center 3d camera/control on the stack - let centerLPS = stack.worldCenter(); - r0.camera.lookAt(centerLPS.x, centerLPS.y, centerLPS.z); - r0.camera.updateProjectionMatrix(); - r0.controls.target.set(centerLPS.x, centerLPS.y, centerLPS.z); - - // bouding box - let boxHelper = new HelpersBoundingBox(stack); - r0.scene.add(boxHelper); - - // red slice - initHelpersStack(r1, stack); - r0.scene.add(r1.scene); - - // yellow slice - initHelpersStack(r2, stack); - r0.scene.add(r2.scene); - - // green slice - initHelpersStack(r3, stack); - r0.scene.add(r3.scene); - - // create new mesh with Localizer shaders - let plane1 = r1.stackHelper.slice.cartesianEquation(); - let plane2 = r2.stackHelper.slice.cartesianEquation(); - let plane3 = r3.stackHelper.slice.cartesianEquation(); - - // localizer red slice - initHelpersLocalizer(r1, stack, plane1, [ - { - plane: plane2, - color: new THREE.Color(r2.stackHelper.borderColor), - }, - { - plane: plane3, - color: new THREE.Color(r3.stackHelper.borderColor), - }, - ]); - - // localizer yellow slice - initHelpersLocalizer(r2, stack, plane2, [ - { - plane: plane1, - color: new THREE.Color(r1.stackHelper.borderColor), - }, - { - plane: plane3, - color: new THREE.Color(r3.stackHelper.borderColor), - }, - ]); - - // localizer green slice - initHelpersLocalizer(r3, stack, plane3, [ - { - plane: plane1, - color: new THREE.Color(r1.stackHelper.borderColor), - }, - { - plane: plane2, - color: new THREE.Color(r2.stackHelper.borderColor), - }, - ]); - - - - /** - * Update Layer Mix - */ - function updateLocalizer(refObj, targetLocalizersHelpers) { - let refHelper = refObj.stackHelper; - let localizerHelper = refObj.localizerHelper; - let plane = refHelper.slice.cartesianEquation(); - localizerHelper.referencePlane = plane; - - // bit of a hack... works fine for this application - for (let i = 0; i < targetLocalizersHelpers.length; i++) { - for (let j = 0; j < 4; j++) { - let targetPlane = targetLocalizersHelpers[i]['plane' + (j + 1)]; - if (targetPlane && - plane.x === targetPlane.x && - plane.y === targetPlane.y && - plane.z === targetPlane.z) { - targetLocalizersHelpers[i]['plane' + (j + 1)] = plane; - } - } - } - - // update the geometry will create a new mesh - localizerHelper.geometry = refHelper.slice.geometry; - } - - function updateClipPlane(refObj, clipPlane) { - const stackHelper = refObj.stackHelper; - const camera = refObj.camera; - let vertices = stackHelper.slice.geometry.vertices; - let p1 = new THREE.Vector3(vertices[0].x, vertices[0].y, vertices[0].z) - .applyMatrix4(stackHelper._stack.ijk2LPS); - let p2 = new THREE.Vector3(vertices[1].x, vertices[1].y, vertices[1].z) - .applyMatrix4(stackHelper._stack.ijk2LPS); - let p3 = new THREE.Vector3(vertices[2].x, vertices[2].y, vertices[2].z) - .applyMatrix4(stackHelper._stack.ijk2LPS); - - clipPlane.setFromCoplanarPoints(p1, p2, p3); - - let cameraDirection = new THREE.Vector3(1, 1, 1); - cameraDirection.applyQuaternion(camera.quaternion); - - if (cameraDirection.dot(clipPlane.normal) > 0) { - clipPlane.negate(); - } - } - - function onYellowChanged() { - updateLocalizer(r2, [r1.localizerHelper, r3.localizerHelper]); - updateClipPlane(r2, clipPlane2); - } - - function onRedChanged() { - updateLocalizer(r1, [r2.localizerHelper, r3.localizerHelper]); - updateClipPlane(r1, clipPlane1); - } - - function onGreenChanged() { - updateLocalizer(r3, [r1.localizerHelper, r2.localizerHelper]); - updateClipPlane(r3, clipPlane3); - } - - function onDoubleClick(event) { - const canvas = event.srcElement.parentElement; - const id = event.target.id; - const mouse = { - x: ((event.clientX - canvas.offsetLeft) / canvas.clientWidth) * 2 - 1, - y: - ((event.clientY - canvas.offsetTop) / canvas.clientHeight) * 2 + 1, - }; - // - let camera = null; - let stackHelper = null; - let scene = null; - switch (id) { - case '0': - camera = r0.camera; - stackHelper = r1.stackHelper; - scene = r0.scene; - break; - case '1': - camera = r1.camera; - stackHelper = r1.stackHelper; - scene = r1.scene; - break; - case '2': - camera = r2.camera; - stackHelper = r2.stackHelper; - scene = r2.scene; - break; - case '3': - camera = r3.camera; - stackHelper = r3.stackHelper; - scene = r3.scene; - break; - } - - const raycaster = new THREE.Raycaster(); - raycaster.setFromCamera(mouse, camera); - - const intersects = raycaster.intersectObjects(scene.children, true); - if (intersects.length > 0) { - let ijk = - ModelsStack.worldToData(stackHelper.stack, intersects[0].point); - r1.stackHelper.index = - ijk.getComponent((r1.stackHelper.orientation + 2) % 3); - r2.stackHelper.index = - ijk.getComponent((r2.stackHelper.orientation + 2) % 3); - r3.stackHelper.index = - ijk.getComponent((r3.stackHelper.orientation + 2) % 3); - - onGreenChanged(); - onRedChanged(); - onYellowChanged(); - } - } - - // event listeners - r0.domElement.addEventListener('dblclick', onDoubleClick); - r1.domElement.addEventListener('dblclick', onDoubleClick); - r2.domElement.addEventListener('dblclick', onDoubleClick); - r3.domElement.addEventListener('dblclick', onDoubleClick); - - function onScroll(event) { - const id = event.target.domElement.id; - let stackHelper = null; - switch (id) { - case 'r1': - stackHelper = r1.stackHelper; - break; - case 'r2': - stackHelper = r2.stackHelper; - break; - case 'r3': - stackHelper = r3.stackHelper; - break; - } - - if (event.delta > 0) { - if (stackHelper.index >= stackHelper.orientationMaxIndex - 1) { - return false; - } - stackHelper.index += 1; - } else { - if (stackHelper.index <= 0) { - return false; - } - stackHelper.index -= 1; - } - - onGreenChanged(); - onRedChanged(); - onYellowChanged(); - } - - // event listeners - r1.controls.addEventListener('OnScroll', onScroll); - r2.controls.addEventListener('OnScroll', onScroll); - r3.controls.addEventListener('OnScroll', onScroll); - - function windowResize2D(rendererObj) { - rendererObj.camera.canvas = { - width: rendererObj.domElement.clientWidth, - height: rendererObj.domElement.clientHeight, - }; - rendererObj.camera.fitBox(2, 1); - rendererObj.renderer.setSize( - rendererObj.domElement.clientWidth, - rendererObj.domElement.clientHeight); - - // update info to draw borders properly - rendererObj.stackHelper.slice.canvasWidth = - rendererObj.domElement.clientWidth; - rendererObj.stackHelper.slice.canvasHeight = - rendererObj.domElement.clientHeight; - rendererObj.localizerHelper.canvasWidth = - rendererObj.domElement.clientWidth; - rendererObj.localizerHelper.canvasHeight = - rendererObj.domElement.clientHeight; - } - - function onWindowResize() { - // update 3D - r0.camera.aspect = r0.domElement.clientWidth / r0.domElement.clientHeight; - r0.camera.updateProjectionMatrix(); - r0.renderer.setSize( - r0.domElement.clientWidth, r0.domElement.clientHeight); - - // update 2d - windowResize2D(r1); - windowResize2D(r2); - windowResize2D(r3); - } - - window.addEventListener('resize', onWindowResize, false); - - $("#" + _this.props.id).on("dialogresizestop", function (event, ui) { - // update 3D - r0.camera.aspect = r0.domElement.clientWidth / r0.domElement.clientHeight; - r0.camera.updateProjectionMatrix(); - r0.renderer.setSize( - r0.domElement.clientWidth, r0.domElement.clientHeight); - - // update 2d - windowResize2D(r1); - windowResize2D(r2); - windowResize2D(r3); - - }); - - - ready = true; - - // load meshes on the stack is all set - // let meshesLoaded = 0; - // function loadSTLObject(object) { - // const stlLoader = new THREE.STLLoader(); - // stlLoader.load(object.location, function (geometry) { - // // 3D mesh - // object.material = new THREE.MeshLambertMaterial({ - // opacity: object.opacity, - // color: object.color, - // clippingPlanes: [], - // side: THREE.DoubleSide, - // transparent: true, - // }); - // object.mesh = new THREE.Mesh(geometry, object.material); - // const RASToLPS = new THREE.Matrix4(); - // RASToLPS.set(-1, 0, 0, 0, - // 0, -1, 0, 0, - // 0, 0, 1, 0, - // 0, 0, 0, 1); - // object.mesh.applyMatrix(RASToLPS); - // r0.scene.add(object.mesh); - - // // front - // object.materialFront = new THREE.MeshBasicMaterial({ - // color: object.color, - // side: THREE.FrontSide, - // depthWrite: true, - // opacity: 0, - // transparent: true, - // clippingPlanes: [], - // }); - - // object.meshFront = new THREE.Mesh(geometry, object.materialFront); - // object.meshFront.applyMatrix(RASToLPS); - // sceneClip.add(object.meshFront); - - // // back - // object.materialBack = new THREE.MeshBasicMaterial({ - // color: object.color, - // side: THREE.BackSide, - // depthWrite: true, - // opacity: object.opacity, - // transparent: true, - // clippingPlanes: [], - // }); - - // object.meshBack = new THREE.Mesh(geometry, object.materialBack); - // object.meshBack.applyMatrix(RASToLPS); - // sceneClip.add(object.meshBack); - - // meshesLoaded++; - - // onGreenChanged(); - // onRedChanged(); - // onYellowChanged(); - - // // good to go - // if (meshesLoaded === data.size) { - // ready = true; - // } - // }); - // } - - // data.forEach(function (object, key) { - // loadSTLObject(object); - // }); - }) - .catch(function (error) { - window.console.log('oops... something went wrong...'); - window.console.log(error); - }); } componentDidMount() { - - if (this.state.mode == "single_view") { - this.loadSingleView(); - } - else if (this.state.mode == "quad_view") { - this.loadQuadView(); - } + this.loadView(); } componentDidUpdate() { - - if (this.state.mode == "single_view") { - this.loadSingleView(); - } - else if (this.state.mode == "quad_view") { - this.loadQuadView(); - } + this.loadView(); } changeMode() { @@ -917,7 +75,7 @@ define(function (require) { } download(){ - window.location = this.props.files; + window.location = this.state.files; } render() { @@ -931,10 +89,10 @@ define(function (require) { else if (this.state.mode == "quad_view") { dicomViewerContent = (
-
-
-
-
+
+
+
+
) } @@ -942,7 +100,7 @@ define(function (require) {
) From d3d9df3a44142aa943d84a72875fd7d8ed16c287 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Mon, 22 May 2017 21:11:17 +0100 Subject: [PATCH 010/371] add set data for instances --- .../interface/dicomViewer/DicomViewer.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js b/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js index 615ff449b..c4bbd6174 100644 --- a/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js +++ b/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js @@ -27,11 +27,20 @@ define(function (require) { this.download = this.download.bind(this); } - setData(files) { - this.setState({ files: files }); + setData(data) { + if (data.getMetaType == undefined){ + this.setState({ files: files }); + } + else if (data.getMetaType() == "Instance"){ + if (data.getVariable().getInitialValues()[0].value.format == "NIFTI"){ + this.setState({ files: [data.getVariable().getInitialValues()[0].value.data] }); + } + else if (data.getVariable().getInitialValues()[0].value.format == "DCM"){ + // WHAT do we do here? + } + } } - loadView() { if (this.state.mode == "single_view") { DicomViewerEngine.loadSingleView(this); From 9a90c85e22293900dc3085b0e58b739378cde9f5 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Tue, 23 May 2017 22:33:22 +0100 Subject: [PATCH 011/371] removing buttons images for highresviewer --- .../images/fullpage_grouphover.png | Bin 4907 -> 0 bytes .../bigImageViewer/images/fullpage_hover.png | Bin 5214 -> 0 bytes .../bigImageViewer/images/fullpage_pressed.png | Bin 5213 -> 0 bytes .../bigImageViewer/images/fullpage_rest.png | Bin 5155 -> 0 bytes .../bigImageViewer/images/home_grouphover.png | Bin 4808 -> 0 bytes .../bigImageViewer/images/home_hover.png | Bin 5107 -> 0 bytes .../bigImageViewer/images/home_pressed.png | Bin 5138 -> 0 bytes .../bigImageViewer/images/home_rest.png | Bin 5061 -> 0 bytes .../bigImageViewer/images/next_grouphover.png | Bin 3004 -> 0 bytes .../bigImageViewer/images/next_hover.png | Bin 3433 -> 0 bytes .../bigImageViewer/images/next_pressed.png | Bin 3503 -> 0 bytes .../bigImageViewer/images/next_rest.png | Bin 3061 -> 0 bytes .../images/previous_grouphover.png | Bin 2987 -> 0 bytes .../bigImageViewer/images/previous_hover.png | Bin 3461 -> 0 bytes .../bigImageViewer/images/previous_pressed.png | Bin 3499 -> 0 bytes .../bigImageViewer/images/previous_rest.png | Bin 3064 -> 0 bytes .../images/rotateleft_grouphover.png | Bin 1902 -> 0 bytes .../bigImageViewer/images/rotateleft_hover.png | Bin 2289 -> 0 bytes .../bigImageViewer/images/rotateleft_pressed.png | Bin 2242 -> 0 bytes .../bigImageViewer/images/rotateleft_rest.png | Bin 1943 -> 0 bytes .../images/rotateright_grouphover.png | Bin 1970 -> 0 bytes .../bigImageViewer/images/rotateright_hover.png | Bin 2338 -> 0 bytes .../images/rotateright_pressed.png | Bin 2228 -> 0 bytes .../bigImageViewer/images/rotateright_rest.png | Bin 1961 -> 0 bytes .../bigImageViewer/images/zoomin_grouphover.png | Bin 4794 -> 0 bytes .../bigImageViewer/images/zoomin_hover.png | Bin 5126 -> 0 bytes .../bigImageViewer/images/zoomin_pressed.png | Bin 5172 -> 0 bytes .../bigImageViewer/images/zoomin_rest.png | Bin 5041 -> 0 bytes .../bigImageViewer/images/zoomout_grouphover.png | Bin 4596 -> 0 bytes .../bigImageViewer/images/zoomout_hover.png | Bin 4931 -> 0 bytes .../bigImageViewer/images/zoomout_pressed.png | Bin 5007 -> 0 bytes .../bigImageViewer/images/zoomout_rest.png | Bin 4811 -> 0 bytes 32 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/fullpage_grouphover.png delete mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/fullpage_hover.png delete mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/fullpage_pressed.png delete mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/fullpage_rest.png delete mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/home_grouphover.png delete mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/home_hover.png delete mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/home_pressed.png delete mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/home_rest.png delete mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/next_grouphover.png delete mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/next_hover.png delete mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/next_pressed.png delete mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/next_rest.png delete mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/previous_grouphover.png delete mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/previous_hover.png delete mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/previous_pressed.png delete mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/previous_rest.png delete mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/rotateleft_grouphover.png delete mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/rotateleft_hover.png delete mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/rotateleft_pressed.png delete mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/rotateleft_rest.png delete mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/rotateright_grouphover.png delete mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/rotateright_hover.png delete mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/rotateright_pressed.png delete mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/rotateright_rest.png delete mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/zoomin_grouphover.png delete mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/zoomin_hover.png delete mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/zoomin_pressed.png delete mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/zoomin_rest.png delete mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/zoomout_grouphover.png delete mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/zoomout_hover.png delete mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/zoomout_pressed.png delete mode 100644 src/main/webapp/js/components/interface/bigImageViewer/images/zoomout_rest.png diff --git a/src/main/webapp/js/components/interface/bigImageViewer/images/fullpage_grouphover.png b/src/main/webapp/js/components/interface/bigImageViewer/images/fullpage_grouphover.png deleted file mode 100644 index 3ca4e1e3c366d44e9dbbde415f6addaef941b129..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4907 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 R`k?>-002ovPDHLkV1ijn#q9t9 diff --git a/src/main/webapp/js/components/interface/bigImageViewer/images/home_grouphover.png b/src/main/webapp/js/components/interface/bigImageViewer/images/home_grouphover.png deleted file mode 100644 index 204e1cc94b5057e04a42f0d4483ba0c96c05e7cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4808 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 LNkvXXu0mjf%T$KU diff --git a/src/main/webapp/js/components/interface/bigImageViewer/images/next_pressed.png b/src/main/webapp/js/components/interface/bigImageViewer/images/next_pressed.png deleted file mode 100644 index 95f169d65150b1e5162585bd0695e4617363afbe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3503 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 diff --git a/src/main/webapp/js/components/interface/bigImageViewer/images/next_rest.png b/src/main/webapp/js/components/interface/bigImageViewer/images/next_rest.png deleted file mode 100644 index 5ead544b5d7ef6de16e83f9d28aee515666fcb89..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3061 zcmV4Tx0C)j~RL^S@K@|QrZmG~B2wH0nvUrdpNm;9CMbtL^5n^i$+aIn^?(HA4aZWV5ov6ELTdbo0FI&wK{O>*+w4vx20?>!`FrQsdJlnHR>OPy zcd~b_n$otK2Za4V;76L-DzNVtaSB-y0*E}{p()372;bw_^6ZZ}PI-92wGS&j#91PI zKs7DSe@(bk%_Y-7gGe}(^>I=@oY#w#*Bu9GZf3^F5WP>3rn}7Ut74&?PWBFvy`A)a zPP5)V!Xd&78LdA?xQ(9mjMYElVd13a#D+Z_7&Y|xU=_C-srWU*6kiZcC!$nw*)9$7 zn6CX+@=AhmkT}X@VSsa5NKe;HZuq)~1$`#h6R+ZTR#D-3j}vF!)ZOnz+5)dI4jl{{ z44Mr{P!L4~VVJN`K!!XTF*LGrKO?IK8z<8w`3e3jI8lUGNUta*C8 zn(P`s>{pjD=7Kek#B;Fw@hxAK%$F&Q6vg9J^Xf~4by_hu-=A!MJ3Znq&n~srbFGPs zH&&aMXZ>nO`|hf|ljc?VPhR!${AbO?W8x_>CU%PFA&Hm8F7cAsOREdwU~R_;ot1_u z(ruCYB-LPGn!NQdT|ZlRy+(fw^-+`=%+gee_kY4FWHg<*4sZI8+sFJD270UUORdLHO0nA4V) z%{fwsET5CQ>B?eK%uw4yQc~9?*JVo2}ze(;aRcp*ceL#HUJSllrgm5wQKR zQu+C;QrUh^8rFfA`ftFz{YAidi-`aL010qNS#tmYE+YT{E+YYWr9XB600^x~L_t(o z37wZ&PgGkPhWFkz7Zem61O!_~K~Gx5v0D=pO*FC7C$SUt#+Vqr^WGoe5AZh_y;2je zyipS`oVv9&4iN`D+9JvzGeS|+T>ZSOYgaad#?y;@wf366^{)3D*1|3pi`M^JY)WaZ zTbGrgxBUIH`u#sUQS(Zh+vm=mvtGY`ZSUK+&jn%4&(GW2w{N$eK7DG%VzDA~1;zy= zZ0`4Wx3qs^P8r0pW5?`|A3r*5=m;C4S_jg%L>m@VH#IdCg27{)0`)^M zE)y`|tXZ?h85$b$J$(4kzjyE6@cQ-ZLp3!ul`SnTHO%v6G8y~x=g*0Ofr0t)@$u~Q z=g-s4&CS`y#>U*}=xBCiWF#vvF0j==eHV-u$elZP#DEV8oj7r#?&QgnJzZU0otUGJ zd7(l?GBGhR^Y-oA;cM5fy}Ek!>KNS_Nkczf^mW113paR zYj1B4ojP@@{=k6)TRS>BwuZyuX2)@Yh{If?Q#G5|FjP#XQu#z8G4c5EdV05P+0uxUM+6AH z0YrdIzFNWzomPE)z4`*b{o}W8a>bt=$8w-LTPPI$oYrhs0;J8fX+X95JRsB#FyBw@ zV|k{cq9VO<CaP2`X^9|b?jvx&?j5M>jwHp0RvfFk^jnrLKg5niB7UTS#Fc}} z5jG3JQkY74qlRP_hYbOvQqKF|%?$%4v;mgCfB(LZ++uT=eI+0UF~>JTd6CU!@>=xA z*)qzv@{t_aFe6mnVlx4g9oUyHU5cDOeL784et?lLVgbe~z-=N)K=?L51Y!jk2JJ**(JDDz5kN|G zNtl`G>wB3b3ECP%u?;&043T0jE-vbBvIu1h_N=(Ig|p2@HlR{1Ev=uRzJh7;7n88eqS6nW3t$2B zI$R;OL}Tjk;lodN?b`J(z|8`p0-z3}yUE;T-t}cLZYN+0!^6YVTxj{4nrai4y7(vy zZEbCD$Q8plsiH_u<1RC`fCC^$Ak?6vjf?f!=FOXb=U^!@M>P3)PB=Jtk_AfT1;zx* zT$W3~kOzvKD$fIIh(}Q}9`B8^z=tA%$;heMEP>DX=r`1Q(Flx~!HkikM~}Y5&%W_& zaPT9CHDWqCI5?Q&i80NA(lF$GcrEoA02Bd~d-3AM*9{vsj3Zr~K8i2QD^JL&xrIfs ze5HK#djT=eCVCQLjfWPMlhZeT6~+AVFJHdsf}E5IVMw|pD=tbYFAzN%SOnOA0F_f1 zqC6m)6)Bv)D3KGdCODeeN91C%;r;y2cwfZWLi}o6&0JFe;UGbr6E_Q0y?ghr3co8NgG}fL%s61C_?|_?i~yCHSptRu z_thmZVl9l~2zIZQsd(#bTeogq2alvMkMsG2~#VMwavg;qnmk^hyt0MZD6DB=tF ze0uaK(h6*tB7_q->IiekxVh&@3Eu!L5HgRK5fe$@1;b=7ATcCBk_Z68_)mqN2(+sZ zT@5ir?-HF(5hChSWZMMb5&}ot3qlbjUs(c#wt2y;UQtT^<1!>rdX*V}AmoEE62yFZ zc!%&EjobK3fpv7{bL5#U{W67DTo3{xLX3I%Hf4p+xC5#^fCNe#O5{rdCbsnLC$+mb zF-?;mw3#+`#hYoBK2`vs@B7SX&@CQN@*`JVaBkb3uFYI=Q@z_33Vp9ye#p%76&_F; za!sguc@g~*cjHx=Sia~Ffmy!TZI?irrgnAv|7iVR0a~ZeFJ&@U00000NkvXXu0mjf DA#uj9 diff --git a/src/main/webapp/js/components/interface/bigImageViewer/images/previous_grouphover.png b/src/main/webapp/js/components/interface/bigImageViewer/images/previous_grouphover.png deleted file mode 100644 index 016e63957a87778ba2562ec7a91dee22248590d8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2987 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 diff --git a/src/main/webapp/js/components/interface/bigImageViewer/images/previous_hover.png b/src/main/webapp/js/components/interface/bigImageViewer/images/previous_hover.png deleted file mode 100644 index d4a5c1552c7a8a5e7ddae0d2a94f235e00e44c62..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3461 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 Z{{Y0&G1asaZ{h#|002ovPDHLkV1h%en=b$W diff --git a/src/main/webapp/js/components/interface/bigImageViewer/images/previous_rest.png b/src/main/webapp/js/components/interface/bigImageViewer/images/previous_rest.png deleted file mode 100644 index 9716dac67b7ccd4c68b427eeca7de3548eed0726..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3064 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 LNkvXXu0mjfWXDWt diff --git a/src/main/webapp/js/components/interface/bigImageViewer/images/rotateleft_pressed.png b/src/main/webapp/js/components/interface/bigImageViewer/images/rotateleft_pressed.png deleted file mode 100644 index b968ebf8ca3bff215d92ac0b41af76e317cff80b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2242 zcmV;z2tD_SP)!e1yPbVK{QQzl@Ps9(LNLjQYopb zMpfxUC~7PE&^|*cB8)LqRPNW84-nhJbC>-u2phnfpxt**)uy zm$6OTkw2gH%%1;z{?C8;&Ty{lVzG;(%l~H;;2SHKTZPI!xC<=EEk?#i#2b0v=tngf zhb``nHXEJA2#NPVg!2&)*qjOk20tM`36o&4(PnTxvq6N!dmvIF5n_-C;e!VcezLv2 zeUok50@gng}8|ohu z1R<8sWX8?h3709wO1VdwO0@Ha0$e{n(qM6Tf|>MGlKl>gyrJq7b7J zLNN)pro$}i(58#Xr8Ce?3-w>ymFoEX7n*L|xN&^@_U+#zTUkPGK0>o(d|Bfs66+x% zb+w7dhkks(Foy;b$z@BRG&g_`(awZih>@wqwT*kI)qJQZ*q|li_?Z66-p2=sO*q_kVZjCofE6crdYabqC~zFv2x1LQw~T z>>{kVD2N>RA_sLuKyg%nt$V>qt7sbTT)hf@>~HOw>MXAhSZiIVgDPQWIW0wG>)}hY)tb z3BFX7Wi)qm!20Lq_|@(=Ce}an&{wu@-MWe%#l1LW*;y$K}y?(TA^U={OZo#^jPs?k%fX-sbzdsnXl9nskXNE(VNGP79!y=75wW) z2vl3OR>J0D#5g|g3T}D(< zVEn1S*(0%Q*RGFw-e~ZQ>i;np-6EMgF$5Sw_R*mkwIOJc9&;7 zfI#X$tb9~PeUlKlR+Xx&s-uLjzr-Ui%5@+rB|+8xt@8&A2WB6DG@RZgV(ZI z@bs6hQtg=1P`FKX{TlGuhf}n;H80-(hm2bk%#lW&QlH2;bYIs?@a&OY&a>}pFS7w0 z)SEcdG-0_;DKE5i8akCKZPJ06v(UOp$MU)n2ig!3Fh}^7@u!soR=K`867uYC?IdnH<&x#ZJB+%}1G#VKWd!87_z_lp8_;?yS zHcvvecpUk25^wY+OLK3H(|_4gM4~MOs}MplB|^`aH$k^dhrSb}i2^<2dIk+i^0E{H zRarSQ(G~nuAHr%ruNZrN9TMGv~^cD`(wMs7Z)Rkn1RA8Dt_PkY$U!vo2DAFzM1~imM!L z1@-0TI&;?8{YzAck^@<>5%J~{q-GJkB-alP4qo=@9yf?gMWNY;4<9}`Gc!}nu3Iz8 z*LZcpX5O(8q88wJ%%#%eYHleg1<@&aFt3))eQNU*B+~)!P`jYEA=S`x#lpl#*Gx=L zPiuYs{TEqJvVDYHp0#hcTA_`PkDt!1SgjyxQU7sDki0SG5FrPm*CeU@d!I}9A`agB zdktby8#}kA;F70!zDy<@qwS^pGc6B9se=zNO=I}Q)@*O8l8YW=TVz8zuNgvLU*GSU z$Wp6i#C&A*3l}e5rPz7(I=`Z61j(Gv(-x88^5O)^Ey3F!Z>!pCHN5-70knlRIJvSV zuW?d^!8NO~FoL#*fq{V@^33%rk!8;5+EoE;u`d;SdwX9V9UVRXc6xpI@=QanKvIsQ z#l1yl;WbBZ8fBS7OL*JkSs*Qng5_W!+fX<*zCJ`G&Yd`M!t=^B6IcbNkIjYjj8IW{ zgc?HK#*G_)8jVIDd$94=#Md7gZd76}qH;Oy=X5qQMMW4zDslzVH^2Eyd-`H(c|$Ij zJ4@HknbOPn^i090i5FFRvxGW3J9o$9@h^+KZEfkCP`|WkvME79S<=8PJ8g%4_9pY5My_dHT+<*CghFjnrji1bX?`ZzN_cM#UG+O9#tK)Dn`~MbT0PZ!H(dGzG Q-2eap07*qoM6N<$f~^ohNdN!< diff --git a/src/main/webapp/js/components/interface/bigImageViewer/images/rotateleft_rest.png b/src/main/webapp/js/components/interface/bigImageViewer/images/rotateleft_rest.png deleted file mode 100644 index ebbf081b8aa0a038f4ecdd594087150cba1e8ea8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1943 zcmV;I2Wa?-P)E zK~#9!%$Q3|6j>C8tE#IXG&Io7t3ePD1d_qmBm#zDT+G5za3*o%XpGU!Bsv>i>fpkS zChAf*CPv)h!oFMdQrlzKTHhF|ulzpn9 zz5vpN(E%X<k_Hf`U&{X3)4n3tZO?o3Th%@PDbRK`W4(a7@h@|@S}bp?aL=_gN~ zbeuhVb_}<|DvKrp`T|A=geEig_VykGz_(dhS(R3+)eZ=WUA$_nY=GkZe*ePU+}v9z z^;bhf!ymXEgoPMTL=R{cOd^oJzP_Jxa&mS8q?{lynM9$1@oWsQIK`h)gTcU);W=S` zety{Ha`n~J)ZD=%fwh3*FbP6q0?01~1qHh^GBOHiOgJ2-F$R9+5?~^Q3ctlLc?f_s z8Bt0IC*9{g9?!(bj~_d0YikL}&wxVOkO_LZ%YHX@cXuDm%gfsZkU|28L?UKPY!yY( zhKcOV9B#Ke2RUMg8fF?x3nbM-p%4K|2=4}5wNIZu{T}x%v`F%=DG{9E!i5W3Rpey7 zapOkW=FOWMolfT#S~;AjV6rpS$V;%@5!ZV4{lR z1cPy$*=)Asd)Q{PO>}m4E^4;A{IHln6>;m5M5=6(at2ts2bfM?Iz9HXP7#?jG# zRw?Rq=1axJB@w7YP9p%1#FG;1L^5+JDJcT{Rqx-w|I_8mmnT$PJ{~Gj2ZWR{q@|^8 zLI@0ih>%0<3NyB0X%=MImA+XSa(iZP@oKR%cj61#B^lSDbK`FuY9=FOX_ zw{N!;L4kBE#7OF3Az`8+g{dgwo&^irz!VYG^Xl#7F<+K$q#`1`0hp+W)B?r z!H3_tMAp;OW9skkbK+Sml@0)qI>ZzP@ErV3l7H0^6OW9T2xekp!i7BeG%_+0&CJZS z)8%kDJk`}(r1J9einnivM2IKl=H_tY=>GhzXY$Tio}(vk<&*jrXs zCZG%$0V!&tkr0Msg-t#Hu_}IQYRWS+GqVI3FH{O1K72$te*9QvV`G#5I7#aXmN;=gFIRu!%k9sjxSXh{P z?AUQjU0vM*O8pEa>#0+}<|7PMZEb(jPT_`zhBAtZip(5RaMn2;5dZ9he<>{XM}QLwr#n}%1RIJ2cZIue1LjiEo#fuk*w7rdXYpY9h497Kr5$EKuJSyZ;u(3RyuU(r*SBN+?`$oc$f}R0Q1Ac!&KG++RCe#py{@ho;C9N z_3I7_!{Fecfp!7q45>q(q!6u;R=mz3x`<{v+Q{#>wk?_TOrBq&AUl22sTY6?{1(d(q; z=jS_65Uo_$Xa^xL+_alYN=m5r;U7PKEa17BD_5?xVcasa0BgErC@oC<)Mh{_r%#_g z0=ZN7?b{ban<>3{^G2fGrnCv93X)U6Jrj1Cflg_t7NqK1QBe^iwH`fsBp^)w>({S8 zVEv3@K~*cN+g!yPD@CCbCr%t08ymCM*VjvEkYWe{hg~I~#-tEyvg8vQIfGN&$)SId zUc7k0cXf5~DF6PJmX-%h3BO7ab%Vtv?ev(X96frp@xzA?d6Ecs+U=3$+ATy{v?C5g4{F zhXm#T5x|b>N838iR0yoQ2VN(TSy5}xo;}+zc7x8Vj;o`i<8QX=uu6sh_I8l$y-CLm z+LKpR9-^A}rKImox@Qx6<=|D0SJfcqRMcw&RL}iCZt1^~`&HS5vIP<8+S002ovPDHLkV1j};jw1j7 diff --git a/src/main/webapp/js/components/interface/bigImageViewer/images/rotateright_grouphover.png b/src/main/webapp/js/components/interface/bigImageViewer/images/rotateright_grouphover.png deleted file mode 100644 index 86e8689c14b1ef1af96adc7aa3ba9531113a31c4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1970 zcmV;j2Tk~iP)I8Zm`N5E0RZkMfEy<$fpYu+`O4%0LV%NCDOavsIkacbp6~FuC@n3Gr>Cboob%W?+qRA6}=2e{rgYi@ggjwK!yDPZ2%JoNa}+J4|*~)GfN5z3T)FfZR}cL z!M9|m-!ui@;g)3)C=;@A02-^PsQ3`C%)^%A0g8dqqDBXj*4Ni}FE1~z2u7}Uc7km{qpkiFAEC`$@61C$VX`B1c+Ffo0~J6nwt2dN00cz!UBLgJS!`U2LeeF zFTGC;rlh1809S-(gLm%S8C8r#-Efy7uXJ|7SPF3lSm`M(EiHtzM&OLZ4AXrK3$Wzm zWF8C#d1K=bd}3mJ1I4AKWw26FSy?5WJrD>`q*+5lLsQk&)gR-PX(d@m$z}665@DU> zLeTE4uC6-RkqldMTo~mPxdAZ4!r?UrDe2h;8y+4qZr^Taa9+6q_?n>kz)(K47yiHh!uUNYcSr9%f{=MHnB#Vh)8b?TGxt)gEk6l(1{pTFw6ceiko- z=j3D?wY9Z^fKUX|Twq0DUQt|JT!B|EDHY6kU?d=tWqy9%AV97x%E=fag%cz9@AtCJ z>RMgh2Z$u$P&TIdXpW_&B?k^qOf#*#xL|y16T@IOJv~i~L`FtNBJdvz>EDEJm?!~8 zYwNF^WYDkT;v!>eYFf&@h*NdY^k2+~->BiNoKlAI?+gL-xX&X@!ee>px4Vh%B zCf_7>$nR)=pPtB<89^@-m6W_>wnKiQoI=qvGBO++TvJmEAfMP^(V3Zrs$j}Yt^AE4AP5Xg)X%tGnKLyt)q#z?16rxZa6|$l-ysQ*KVk(5nUxBm zc>Ve{g}i{2qM%^Ac=__B0E1M&WLOgrlItBm*>-K#{+Vj*glXT}TJNY1!8WB)F<- z!rP&Nk;#F9fdF=IlH?>E?G{y7969olpq=*i-gE0KBUu58%pfM|H%UZ5=t6BW2L}gD z{5hw3nsvR|l3+YCISTRm+y+BBP_DU()UrLEI6c9R#s+`BxnZ& zB875?-O%t=)HE%J{6+U|LptVpak)(WMeHQZD;GGV#! zpFbc?eqCK%6b@=*hcZ$jqoEj){zMDA&I%LseE06%)bX(!H*Q#vd+x%83!h=mShf1V3gSW!`t z%pdMWz!1K&kphT$1i(*Vr$@1{pe%6@yahn%iX{S;e(>PI;~4v%+k$FWUuS3MFN*$E z#Xv}b2>C^k@97}HxhbeXrKx*R>qqp{r0qGtt!ihcwav~=zJ*d1HbV>b4Pmb)PxiI| z+L=0{0b18Wcteug(up$8S=-dwQp?*I`JA(Dt?7O%_4}Q*|I1eh?g7DlK8H9S6I=NT z!T#m<8Metc8XZi$Z#4h!`%Hq5Mw?y2>xYTv|3`oU0EVS7rgVc{q5uE@07*qoM6N<$ Eg7A5stN;K2 diff --git a/src/main/webapp/js/components/interface/bigImageViewer/images/rotateright_hover.png b/src/main/webapp/js/components/interface/bigImageViewer/images/rotateright_hover.png deleted file mode 100644 index d22a728fe9b973f28df270e165fe57ee10353d1a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2338 zcmV+-3ElRIP)Qpe82uIC>DzuLI~}^p64D* zFI;q&`W;Kvf8)9F;gR>h{M1wX)Z^TB-MacHkQ1r_C0YTs9Hu6a09aEpUf#X?jx`%M z{z#@$%YYC{9H)k)5GoeaFcM?z>$(ZPx^lVUzr6XzkH7cJ=Z~v@Rh4Q=wvro0ON1jP zXN742qAQsn9C-cLWZ|Ow2|~gum$O>ARQAe6&TG}_4utwiV-_XxIOB;_qMl5)sSuFT zg`vZDZ`-~@-BMDjgiuZ^pzB~}0g0VDbm$$?*0w5ME>D=bOwOK~;&wjk`=W+1lfe)o z;UVd}APCct-kzprdLDDqoo#ko`#e80b@BGkJ+MvPS5PX?1~dZ`$(ksL^^Zeuzax{W zeY*U_)@VrI_l|N3oI=e*O&>K z(dp{(Rt5lqX;Y->4z=zzJKR>CdY$60{lnSONT&=Ei^Uo*Z=A}uh z)rrt?oNH~JoLOx?GgDIMrOQ6?JRgy`KwXPhrX1(ovHt$6VMM$JNTBA_#$gH@Yc_5C zp?X(gj*ex<-ha=r7B5L?nCRH^7y%z+>JuUyJiomrtr(X10z>&CM*X0xK6KidcJy?d zi`C1RR_QyHHD#(?F*=f8*t+%G&+dKgbR^{)>5gbqnQzn+^}exNQu*9e<=k0cLnx*` zG_d0+5KjdLh^9MCk2}o6oW^tnnMN@}<4K>(y<0!%-l!!At{ru1l!nNLeTn;<_3x7Af*Q57a%oj|e#`F|7#(l?oP4U3xB`SzaPAE!QUGaSsFMh)BUS)iV~J zMg?b@!I`nZW-t6<5y|JT2U*tR!1yGCdL6^MPgdo6HYLi(OerofjH~J;ghW*M%v1gP z9>*;fS=4f&^tp?L>mniqQ|JgIsp-0)Mwi3tDTiK}f-oxqzI0CxzHwhxCN%*s{56eU z|62#_d|*s^CryyU6ypk}nkSJ;7gZa9s;H}KoTSMPAncH)!;={ zbmsCi4Ob>|W1~lE+OjpmvNV=R`nX;pgkuU-j8}EkXCCNk;~*v|_!k+Hbp)|C0e7oK zoR1qIuc#`(38q4=p^AiBaV)F^ny5RtIyiVH;(KJY$Td4O|M8zc_bazrmFGRdtd%P@ z6<&t9it+@*i2_3Rt}jmCT&@zp)Kl^I)I{?^s^OE9s#8}0(LJELhk}}|?)00jg~Y!CK4F$|dkgZ?Y}G!RKDkQ4`+xF(WPfa;k5H1^<~ zaKx>nOsNb<4j+CgG+B1DjhGF_dFHKy?=C16&%E)`7+mVGz`SzS)d?FffSU!aT8olB;mmDIHdELf!B^5Khb2e_>4CWMk(TD z8iqSqXp%}DPk-%^Ll-72C@;ht)l8kPOU4c$I1#&W#ixVKP|T zn=BOaxfkC&e)&Wgg<`0=J@8Ec#lt8CVDYctzx+{8l6CUh5Y24hi1bhY=Ix>1hvB!w z2vkCdN~1ztvv*e>sJ~O%S-nafoP= zyqQx7^k1IOpc|agL}6w-qxt{NXXZFGy3r+@=iyxa|670o01E4_WuV+H9{>OV07*qo IM6N<$g4cO(DgXcg diff --git a/src/main/webapp/js/components/interface/bigImageViewer/images/rotateright_pressed.png b/src/main/webapp/js/components/interface/bigImageViewer/images/rotateright_pressed.png deleted file mode 100644 index fc2ead64e506a0639e5af55332d2afdb74e4fe4e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2228 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 U<@`%&ga7~l07*qoM6N<$f_Nk7;{X5v diff --git a/src/main/webapp/js/components/interface/bigImageViewer/images/zoomin_hover.png b/src/main/webapp/js/components/interface/bigImageViewer/images/zoomin_hover.png deleted file mode 100644 index 3cab721f1bd5b0360dd1eac094c36a409b517738..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5126 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 Hu0mjf@M4@K diff --git a/src/main/webapp/js/components/interface/bigImageViewer/images/zoomout_grouphover.png b/src/main/webapp/js/components/interface/bigImageViewer/images/zoomout_grouphover.png deleted file mode 100644 index 46d21b3e507e7363b4e8afd0e77343814ad63748..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4596 zcmVKLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z000LcNklH(?ZPyy5hPAL)Q z67iYw_ZVLbDCYYbke`vLR= z=mOBrzi;sU6aeJ$}QVSFxPdN<2W&d5E*J| zX^GvrbLWpIPMr7&hw1>3=lkgbXliN-larJGwS;hd0ivp^FpJ9ni}ZsB4~}o!w(V0z zQ5u|24jQpm6a|D35JKR(E>>1nFg`y1n^UJw{T0AE00)4_%@zV+%vGyuk}TesdH9v=Q%Hk$>dGGzP%pTJpGqGXN}(CeQOAnMVhZ?(OaUIF(Al^E^aR6sw;x*0A0PL}*G;6!i7= zVf*&&#{t}zqDu~c7Msa+U9nUE=%rF=#5B#HQA*+aev?UB`Hma};{7lT;dvem!@$76 zz`oO`Pag;H4v#G1$nn7dfZpC-IiaNrg~CWWordeWEkRlVv`Y737=ls?$8pf#-;b`Y zt`E82WB{b31WJ@-o&%s=xNzZnsZ{Dc;fw-egTxs7jl33S53WkS=ap=$?tgf!Y_kE<(X%IpX1VJGILN$**j`?~g0u33trAeBmCa&i)ji;G|IG9WT=OPB}% zEtN{;o}QjZLqkJDilWHb`K_;PHj8Stibszg%`Pu5KL+5-O;-{|ZWq1z`T0NHyLWGB z@7}$r)oP%WzBP!ZY1p)B6Glfzv9hxA58g(&yims4X=Nq~ASjhee_@RMcyMs=r@34X zl}hF7_V9nW6h%QkpU1Oj&oD7D@voOJUzPyWIFM*bSwjXscI=p}86SWkfGUS^`8ly>Vi?ij@IUwf>g95IVt#)9x0f$p zhOX-<7K>0-6_nC%6kdlwDaF~dXHltCK6f1Fp8(c4Or2{cR(IT7?ccv26B841bCu@R z)$Hl%`Oq+opC3JX^as0k?LwteLA6?i>$(l5Q&km)VZby^6bc30y?YneuU|*KUjIW7 z1Y-b}0aOyrl`s>FVTOl?5r!ciK71(mzXpITfOj-a>u7Ip|D|b~A00k?7&~_CKt7+3 zTMSiIk;!DRw6uij>1o`&c@vdNr9>(HGh=LtL#zR?B?qQbuK8LsgM)*Jq6kl(JRync zAkE8t8;AN%CX@My5b`6&SbtYn7qZzbY}>}%+#JF%e8L#}GK!)x#@Hf1+~Rj(9xnmX z2(x+f=D7cO@!~~tLJ%WMhHFZQp(x5`048Is&KP^a5AnD-lGG)qZ31b4>FDTa7&q6} z){^6_&ijwFBq{z}mB!nUJEku|dHi!S>o#<3F-$(6ZyL&7*KIZ43NT$7cayU=O^mmp zlxbNjkOmmTFq)d|#DpnBC5L|{QBtDB@c&wfCKy#!-z3An?kAC^>M#E!@;Zp73j2S! e$hY0#KLY>*6CH&u;pYAT0000KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z000PXNkl9)x703VQ-uE$Y#^bREu$^KsUkM+DHl$~%M^VJB0L{@D0{{;PlY$@s0)R}$=4VR5FaQHUr$@(e7yvIUEN~MbZT}Uf z4TP1L4ZuEp_;BaJg9rPgC=yz0I(zo)^c!!yF`ob^0C8d}F;gfM7ywU9OmHg<-7jWR zAZZA9XlSVK*s){J_Vn~TnX$}zH15!n%iUGH(9n~kqoW_b@WKm!d;Rs-rvZd1TY>_> zrBZ1LXvdBn?O{?NGJ$YToH)^Y{P^+b)~?R|h$h|>H2$f;Y=uqiFl)giGzljr)19Mu z?W5c%KBYb>jlX^R^y#-=d+oIe01W^EfG7dd0QmCd%WRCn;Najrm}Z1aDO*9@ix)3G zv+Jwde=A1+YSFb1Wl+w?0TrV-)1i&+7zk;g6^S^KR#=ywDLZblV%0WpH*>{3@!MzL zeR*hT=v@Hy1S(7e4G$0V-o1P8#Y`N>&Am%+jr+j|9~|B?(080Kp2~!uHcW-r`*EQd z!SggM%a9ZdUi_BQMu)148h*F3XYM+k(er?}xtoKjl8Xwg$%l$!6D8|Ndm;nK1$x2(Sjm1h^ zV3y2AYRl%Jvpb$af1s|$SamUx|LM`AN52iA1Avzt0to;I4jiCmrzs3Wq_)Qf z;BMNq>4(;pm$QveV$6Au#BehpZ6xAYvN9l{DE%YZfB^<;4c3O)^)To6sIHMQYdyW0 z-MMq;egGq>Ku#ZjUh+&31W0@3%$YL>yBjy2#+6ZLE-ol7EM~1F$C1rJXmc1i#`kR# zG%c_>3>^0T#s5cJA1cL|=V)4*X5(A4qVHgICVHgck8XU$P30qXAa(Yw# zQpbi3-})|q_fmlzt?*1LrCTRYo_x%$-Fi%n-F18|L2)E04sD8khm`LM4l=^{t}uQ^ zn7~~$Ke50S#?O%QU5b4NQD9RP$%d99H(7IfM5TXlaBxc!H%{UZYR$k)r)cx$%}=5LsM}Dk0j95jQ{3%dh z@us(7!-k!gE?xR81tS1ZYdH`Aq?B@_xHaMgw`N%vJ!xg6ZQCrPJe(;vblkipg}b;% zEp-{p20GLnL+QMhaYrZvo(r|A)lE2G5xGYij&1jU1%R7?H0#`o+C%_E9LHVu+^lWN zbCMP!V`j%7ML`bF`vE+W!( znX6j1QhgmQAap+%s#dEk!a#Uo-IAopk`nGhC_}2z=bwMRqI~3XIn?X*755uE6=iHC z(eR;sg$#$X9y+>KtD#NBZ(p_N=jW>d5qIi_%{ms61Y6Uwc>vzyJAWBfD)Pm+o8b zIb!JfmPGUYQB)6hAU%PH8QAHtA4xhSt~Xj;F+13v1z9>8p!xk*Az)bEN3Iy z1_wL(GKv&Hg6>bFnGF-nod)wvBs*Kv)E?FQVZ&uQoOH#lMq2r$>kOWMcw2$#txgDITgt1lR&t5F8sQb=!@L*P>hHa(O(} zN;LZ$h*qy8ONC=&W9MF>uiwr-s)@27Qvh)n6S^PXxQodYK$Hb}TQog?d)4IZ?Ch0P zZxg1y({7s-fcX6R^XF;*{@r`F_pbV;nJLy4!MK2s47ABGt&@NOqX`@kvR#aGd9Qrt z@A1{y+1ZZ()Doa(OS-&;ICSVx+A|S=V0?W1?ca@Ty*0kYt9xAx;}LX$z(oSD26zpC zl<^uY-Y*h#7r}UhY`4LU0ns?}ZvIxKQuzqLLIM+{>e6!0r1FGG9`Z}2(mw$F>DT_F zzdH5w^$oj%MrR`w&^dq{fRZe+q)`mO2H=n&8$hU*iJ978eBU1ia5wQxJ()jF9q)TR@b2Bap|!@fYuD2KZw5dXK;E*f zHN|4_$Eyos-;sUQ)z54R3tI~+Ya#-hMK1}9@$rnTohymTAK#I8C(HU{rPO=OJPn|n zc%_;OVAU38+qP{m#^Cz(>uHH%Ck4$*+Qqz-vd8nheL{$@F>~+MwaV?vvMg6w|7#qf z)_RheN43_M0Zb>0SCX;0Jz)#Ta+raEfh9q8`}XZtg0Fr3%ICF)?FsrmS75n z!U|Cy1VKA_+XS;yO0!jKs}^~y(@L@($TFBrCbJAAZ7pvcL>uuhT1tv(5&sW_SOFuY zToT|9t~U35C9KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z000QONkl(2@iMr6E9K0)}8GHcnz^9e?b4KfF6T zGxzktv&(qB38-|WqdR+L-}{?$&%Nh=E-^E{%R@xe+{ZT z?HgfQKp2530P5hugR#Sh4|mmSHQ{+4z4g{x`BSG(l>z_{fE$<>m`)~>41i~6XSo@M zZWJ>ikT8Vx$Rm%WpMLu3?`JZZA>Uc;o4s_QorA%;GF$V$P}jcr;)}_nM~|L8dGcfq zfD^I?6ab!>m}meE4-dD734zD}!hGzp$I?$d_0+u`QS;EJ@BM4{!qj9>;re3CulY=x zK%^lpgJPW><<7xv*}*%1@X41mv+q6s{PQ24IB{YDz$$<$fLZ|L1K_h~&$8AUd-m-4 z8m1oMQp#o!>y0!eAy@0W5uKm0uuuyKtf5Ok}+X# z?$F8GZ@;|d?)%?=<<-~zdF05Ea{$T#loJMe=bd->z<~o_$BgT`^|cExjdkM0iT&I9 zH~;*^pPqYQ{?m!J%KVCNd)nQkl;39AIwA!mLldoikn4HES*lWX`Kl~l&uy7py54zk z|NWMD_Sxt&&ph)%X!oHW?B#Nq*Tqb+SVUV}Tiq)L0Q=anW5YlC;r?H}fBc1?Ui{Jzt1$iemml)ZA(Wj1Ey^d znTCKViIpT)f=CIWl%S|>=#(;ad}-A&$0p3$Cnpb$j*jjEkOU9~U2>PT}O0tv& zNg*vEU}g(`Y~o^P##lY@=%bJB29N+?2OllM69WJ;nN0XVGyD7d_s)Mlw%fUu^V5QK zS{ZDcibxp($`GUsf~knCkgfd|!ZHO}2EmYkAR#nZURw0K^Rv5l?AUP|fEa)X05f!e zQuCol0E{P|c%rW~J9X#kTs9f0x^CLE5wnz#ri6?bL`p$O0g@6RQ4bifFTvUedmj9% z2H0cvBx_fIi;hlS%f>eibnO@!8M$SAe7qFwEF*}UbtiCR7Yz>&-&UNT8RX>>r$C6C zl2ptft1~LhjtCiX8)O=wItT$^2D1iyAAYR{zo6JzBCcj#X4hfgVbENWd$*JZ2L}f; zbqYjR>rc1X<|_aequKci6(}ut{3M81FLQ0C#L=C-G0U=2O#v)gU=JjD}}U9Lxi=0(VuvsZ-+gyX%6m`k`^BvzyExb9>MRiTAdEGjo2=5>k;8!Y2(oA( z(blom0QJ+iE=hS^ZhGjUhr*tz0jSQ*%$z&=*Tpv%`gY~w(Jm;h zAWVW01&lagBmk8FRNGo>B!d1ZAWeeO3dwjEii0D$pB*{(W}#5H3}7XIsjI$*XBsfU z-mk|^4M1gjdivtT#hH^&zA*p#?55G{iR1vRN)+Tu5D|nVkb#B?xfa;uN|==>lF5E# zH|@NB->=WUer0ZH(s7)x0OSLhO5hp2VQU949FS75o~u(+Qy-PfW$*BB7O(u_$yTvsg>yv#B$)3Vkm&UmM}v@L(p1da&j_UqNpIDMF7MBB!m#@ zNF;KH5Mncc#C>}@(%tRK$}U%(H~+J+jPFf9FrB$gCDuKU->FMcNS8nF>`Q~y~1^UAZ znHluUPKLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z000N}NklZpvOEENIVZ<#4OM(NW>^YgQ7%< z@&(wCU8IN)5SFa7NTNj9WtjzpKoXG=k~2bK$UqE%*x(WEw)sAlV?ds zRb5?G_gCkhd(QcnU}pT5O$Z_S&+!u;%FJtwhB?` z{Hao@RELI!CYV`TF+j`9%PTi-+_?4j+i%Z(`st@D020f3PyqPm&6_=-nVFfvFcyeo zApF-~e|`M)>C=DPv17*}Ddh%cW+4RS@oM5k5|L0!$!@p1dgaQMxpU{vUA=JO!b1RE z05O2n0BHa`H#f&bgy)`n?lDaMc8=rpg9M*^^2zL>Lx=w9I8Mp@ml9EyzhNGvxhI7X zE&xA@BBxfXEr0aUN9WI+IrBAuj)6*Spi7r7@sT4(9>+|UW%=CMQxjacaN)1}_wWCQ zlyaPiQf5w!R|Ei02qCSd%mB!MW45-@XM?)RD5(^@MbM0?w|Z5s|8IIs`E7=WU=$F)+!W0*LOVSDAn4?q0#rcImn z1JFt-#muf5s0Uzv`xaP`pJ!%|h+HCavMfu&FpQpk_SxUR_S$RD02ndXdp1KaW-676 zozVR0>FGZf3WZ^%R96V$FtaCw@QimHA%r7@kk-TOWS74!6OqJe zG)*HSa)Th)&= zeh7k~;5d%+Bp4#1BuV0SyWL)1UT$~0U9Gi-5W-tnSP&~KD{U(c^qR0iF!aqg-`w7@ zV@EbSJ6qIRH_|kvG)>b^r=x1MTHA3P*;ixMb(mUfQc972w6T;@20>7~di83yw6s(+ zeU7$^Z9fbFP+xuZ)l#)ueXwuezD=Iz)uSki%ztF6?JwR&M@I|G%gf_)b93!xvstrR ziM+pA17l~O6SxpsBT{Ukc(lj@-C$N$E*=I}jdi^$l zwgJjp(zPwbi!Z)tdnN@C-@SYH>-XP(KlVItYpGNkAtITkDSoaIkt~%;Bh0+@?Afz% zv)Q}{pkZKQt1i)++GN ycj~=+2!x-_6g@Upja0oOT>%d!O}=~gE@`dXM6^OgKNx_P z@kGm-o}>?CEzH)fTYG}){{8#?3c)i#q48911tCNUK!KUH8BAF^scTs@NUB)s;xWwR z%l9@qm0hbQQx@$yR4`ys^Y@H~N<9Lv~&A~iNY4&Sv z)hBPYtt1D5tbr*M3Tr@Y>-*v$2Z(>!S5l}?{QoG#IvB@sdIJ2%z6JcqCy{m4zfW!c l^oJnp?SJbge(MANKLGgW6Oj(y!CC+S002ovPDHLkV1hX!DhdDq From 30ce9e7828d3abe9a0d6460fe5e05a0e7434cd25 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Tue, 23 May 2017 22:34:02 +0100 Subject: [PATCH 012/371] integrate with model --- .../bigImageViewer/BigImageViewer.js | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/main/webapp/js/components/interface/bigImageViewer/BigImageViewer.js b/src/main/webapp/js/components/interface/bigImageViewer/BigImageViewer.js index 7016f3de8..9210bbd5d 100644 --- a/src/main/webapp/js/components/interface/bigImageViewer/BigImageViewer.js +++ b/src/main/webapp/js/components/interface/bigImageViewer/BigImageViewer.js @@ -21,7 +21,7 @@ define(function (require) { this.state = { settings: $.extend(settings, this.props.settings), - file: this.props.file + file: this.extractFilePath(this.props.file) }; this.download = this.download.bind(this); @@ -29,13 +29,30 @@ define(function (require) { loadViewer() { this.state.settings.tileSources = this.state.file; - this.state.settings.showNavigator = this.state.showNavigator; - this.viewer = OpenSeadragon(this.state.settings); } download() { //What do we do here? + console.log("Downloading data..."); + } + + extractFilePath(data) { + if (data === undefined){ + return undefined; + } + else if (data.getMetaType == undefined) { + return data; + } + else if (data.getMetaType() == "Instance") { + if (data.getVariable().getInitialValues()[0].value.format == "DZI") { + return data.getVariable().getInitialValues()[0].value.data; + } + } + } + + setData(data) { + this.setState({ file: this.extractFilePath(data) }); } componentDidUpdate() { From cd8ef84af36a789bc00d0ba663c41d0987589721 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Tue, 23 May 2017 22:34:23 +0100 Subject: [PATCH 013/371] convert to less --- .../googleViewer/{GoogleViewer.css => GoogleViewer.less} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/main/webapp/js/components/interface/googleViewer/{GoogleViewer.css => GoogleViewer.less} (100%) diff --git a/src/main/webapp/js/components/interface/googleViewer/GoogleViewer.css b/src/main/webapp/js/components/interface/googleViewer/GoogleViewer.less similarity index 100% rename from src/main/webapp/js/components/interface/googleViewer/GoogleViewer.css rename to src/main/webapp/js/components/interface/googleViewer/GoogleViewer.less From bf2d888cec4015fb1e58ad4db7d868f75ccb7e0d Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Tue, 23 May 2017 22:34:44 +0100 Subject: [PATCH 014/371] add download and require less --- .../interface/googleViewer/GoogleViewer.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/webapp/js/components/interface/googleViewer/GoogleViewer.js b/src/main/webapp/js/components/interface/googleViewer/GoogleViewer.js index bdeb17b8b..7ec98c099 100644 --- a/src/main/webapp/js/components/interface/googleViewer/GoogleViewer.js +++ b/src/main/webapp/js/components/interface/googleViewer/GoogleViewer.js @@ -1,11 +1,6 @@ define(function (require) { - var link = document.createElement("link"); - link.type = "text/css"; - link.rel = "stylesheet"; - link.href = "geppetto/js/components/interface/googleViewer/GoogleViewer.css"; - document.getElementsByTagName("head")[0].appendChild(link); - + require('./GoogleViewer.less'); var React = require('react'); var GoogleMapsLoader = require('google-maps'); var AbstractComponent = require('../../AComponent'); @@ -98,6 +93,11 @@ define(function (require) { }); } + download() { + //What do we do here? + console.log("Downloading data..."); + } + render () { return (

From 0057b692e4ac736bebf70e6b5f9434c2940745b6 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Tue, 23 May 2017 22:35:16 +0100 Subject: [PATCH 015/371] integrate with model and bug fixing --- .../interface/dicomViewer/DicomViewer.js | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js b/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js index c4bbd6174..a898281c1 100644 --- a/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js +++ b/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js @@ -13,12 +13,12 @@ define(function (require) { constructor(props) { super(props); - if (this.props.mode === undefined){ + if (this.props.mode === undefined) { this.props.mode = "quad_view"; } this.state = { - files: this.props.files, + files: this.extractFilesPath(this.props.files), mode: this.props.mode }; @@ -28,14 +28,21 @@ define(function (require) { } setData(data) { - if (data.getMetaType == undefined){ - this.setState({ files: files }); + this.setState({ files: this.extractFilesPath(data) }); + } + + extractFilesPath(data) { + if (data === undefined){ + return undefined; } - else if (data.getMetaType() == "Instance"){ - if (data.getVariable().getInitialValues()[0].value.format == "NIFTI"){ - this.setState({ files: [data.getVariable().getInitialValues()[0].value.data] }); + else if (data.getMetaType == undefined) { + return data; + } + else if (data.getMetaType() == "Instance") { + if (data.getVariable().getInitialValues()[0].value.format == "NIFTI") { + return data.getVariable().getInitialValues()[0].value.data; } - else if (data.getVariable().getInitialValues()[0].value.format == "DCM"){ + else if (data.getVariable().getInitialValues()[0].value.format == "DCM") { // WHAT do we do here? } } @@ -68,25 +75,25 @@ define(function (require) { } changeOrientation() { - if (this.camera.orientation == "coronal"){ + if (this.camera.orientation == "coronal") { this.camera.orientation = "sagittal"; } - else if (this.camera.orientation == "sagittal"){ + else if (this.camera.orientation == "sagittal") { this.camera.orientation = "axial"; } - else if (this.camera.orientation == "axial"){ + else if (this.camera.orientation == "axial") { this.camera.orientation = "coronal"; } this.camera.update() this.camera.fitBox(2) this.stackHelper.orientation = this.camera.stackOrientation; - this.stackHelper.index = Math.floor(this.stackHelper.orientationMaxIndex/2); + this.stackHelper.index = Math.floor(this.stackHelper.orientationMaxIndex / 2); } download() { GEPPETTO.Utility.createZipFromRemoteFiles(this.state.files, "data.zip"); } - + render() { var dicomViewerContent; if (this.state.mode == "single_view") { From dc973b3503d57e0cf94ad60dcebb77552359ee2d Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Wed, 24 May 2017 20:58:28 +0100 Subject: [PATCH 016/371] rename data property --- .../interface/dicomViewer/DicomViewer.js | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js b/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js index a898281c1..467f6fdb6 100644 --- a/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js +++ b/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js @@ -18,7 +18,7 @@ define(function (require) { } this.state = { - files: this.extractFilesPath(this.props.files), + files: this.extractFilesPath(this.props.data), mode: this.props.mode }; @@ -32,20 +32,21 @@ define(function (require) { } extractFilesPath(data) { - if (data === undefined){ - return undefined; - } - else if (data.getMetaType == undefined) { - return data; - } - else if (data.getMetaType() == "Instance") { - if (data.getVariable().getInitialValues()[0].value.format == "NIFTI") { - return data.getVariable().getInitialValues()[0].value.data; + var files; + if (data != undefined){ + if (data.getMetaType == undefined) { + files = data; } - else if (data.getVariable().getInitialValues()[0].value.format == "DCM") { - // WHAT do we do here? + else if (data.getMetaType() == "Instance") { + if (data.getVariable().getInitialValues()[0].value.format == "NIFTI") { + files = data.getVariable().getInitialValues()[0].value.data; + } + else if (data.getVariable().getInitialValues()[0].value.format == "DCM") { + // WHAT do we do here? + } } } + return files; } loadView() { From 67e928dfde55c83a3eda49188291a461f7b60f80 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Wed, 24 May 2017 21:50:37 +0100 Subject: [PATCH 017/371] refactors and polish api --- .../bigImageViewer/BigImageViewer.js | 19 ++++++------ .../interface/googleViewer/GoogleViewer.js | 29 +++++++++++++++---- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/src/main/webapp/js/components/interface/bigImageViewer/BigImageViewer.js b/src/main/webapp/js/components/interface/bigImageViewer/BigImageViewer.js index 9210bbd5d..c6fbbd57b 100644 --- a/src/main/webapp/js/components/interface/bigImageViewer/BigImageViewer.js +++ b/src/main/webapp/js/components/interface/bigImageViewer/BigImageViewer.js @@ -21,7 +21,7 @@ define(function (require) { this.state = { settings: $.extend(settings, this.props.settings), - file: this.extractFilePath(this.props.file) + file: this.extractFilePath(this.props.data) }; this.download = this.download.bind(this); @@ -38,17 +38,16 @@ define(function (require) { } extractFilePath(data) { - if (data === undefined){ - return undefined; - } - else if (data.getMetaType == undefined) { - return data; - } - else if (data.getMetaType() == "Instance") { - if (data.getVariable().getInitialValues()[0].value.format == "DZI") { - return data.getVariable().getInitialValues()[0].value.data; + var file; + if (data != undefined){ + if (data.getMetaType == undefined) { + file = data; + } + else if (data.getMetaType() == "Instance" && data.getVariable().getInitialValues()[0].value.format == "DZI") { + file = data.getVariable().getInitialValues()[0].value.data; } } + return file; } setData(data) { diff --git a/src/main/webapp/js/components/interface/googleViewer/GoogleViewer.js b/src/main/webapp/js/components/interface/googleViewer/GoogleViewer.js index 7ec98c099..d6ec0129a 100644 --- a/src/main/webapp/js/components/interface/googleViewer/GoogleViewer.js +++ b/src/main/webapp/js/components/interface/googleViewer/GoogleViewer.js @@ -18,6 +18,7 @@ define(function (require) { mapTypeControl: false } + var path = this.extractFilesPath(this.props.data); var imageMapTypeSettings = { getTileUrl: function (coord, zoom) { var normalizedCoord = _this.getNormalizedCoord(coord, zoom); @@ -32,20 +33,38 @@ define(function (require) { isPng: false, maxZoom: 11, minZoom: 0, - radius: 1738000 + radius: 1738000, + path: path } + + this.state = { mapSettings: $.extend(mapSettings, this.props.mapSettings), imageMapTypeSettings: $.extend(imageMapTypeSettings, this.props.imageMapTypeSettings), tileWidth: (this.props.tileWidth != undefined)?this.props.tileWidth:256 , tileHeight: (this.props.tileHeight != undefined)?this.props.tileHeight:256, - path: this.props.path + path: path }; } - shouldComponentUpdate() { - return false; + extractFilesPath(data) { + var path; + if (data != undefined){ + if (data.getMetaType == undefined) { + path = data; + } + else if (data.getMetaType() == "Instance") { + if (data.getVariable().getInitialValues()[0].value.format == "GOOGLE_MAP") { + path = data.getVariable().getInitialValues()[0].value.data; + } + } + } + return path; + } + + setData(data) { + this.setState({ path: this.extractFilesPath(data) }); } // Normalizes the coords that tiles repeat across the x axis (horizontally) @@ -73,7 +92,7 @@ define(function (require) { componentDidMount () { var _this = this; - GoogleMapsLoader.KEY = this.props.googleKey; + GoogleMapsLoader.KEY = 'AIzaSyAtAf8S4uU54ZogtLqbzc8pvQI6phGDL1Q'; GoogleMapsLoader.load(function (google) { var container = document.getElementById(_this.props.id + "_component"); From c88957f155fef15f677da372a8b44099b6c53d1d Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Wed, 24 May 2017 22:13:15 +0100 Subject: [PATCH 018/371] improve casper readme --- .../webapp/js/pages/tests/casperjs/README.md | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/main/webapp/js/pages/tests/casperjs/README.md b/src/main/webapp/js/pages/tests/casperjs/README.md index 364a48d63..c382bf56b 100644 --- a/src/main/webapp/js/pages/tests/casperjs/README.md +++ b/src/main/webapp/js/pages/tests/casperjs/README.md @@ -1,23 +1,27 @@ -##Run with +## Prereqs -`casperjs test LiveTests.js --engine=slimerjs` +* node.js +* npm -to test that Casper is properly installed. +## Install with: -Run `casperjs test UITests.js --engine=slimerjs` to run Geppetto UI Tests. +`npm install -g phantomjs casperjs slimerjs` -Run `casperjs test --includes=CoreTestsUtility.js CoreTests.js --engine=slimerjs` to run Core projects Tests +## Run with -##Prereqs +To test that Casper is properly installed: -* node.js -* npm +`casperjs test LiveTests.js --engine=slimerjs` + +To run Geppetto UI Tests: + +`casperjs test UITests.js --engine=slimerjs` -##Install with: +To run Core projects Tests: -`npm install -g phantomjs npm install -g casperjs npm install -g slimerjs` +`casperjs test --includes=CoreTestsUtility.js CoreTests.js --engine=slimerjs` -##documentation +## documentation * [CasperJS Test API documentation](http://docs.casperjs.org/en/latest/modules/tester.html) - assert API * [CasperJS Core API documentation](http://docs.casperjs.org/en/latest/modules/casper.html) - actions like clicks. From 7a8256a17937309133f14d8d40996de0d8fa0720 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Thu, 25 May 2017 22:31:06 +0100 Subject: [PATCH 019/371] validating if there are files --- .../interface/dicomViewer/DicomViewer.js | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js b/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js index 467f6fdb6..3087db245 100644 --- a/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js +++ b/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js @@ -95,6 +95,29 @@ define(function (require) { GEPPETTO.Utility.createZipFromRemoteFiles(this.state.files, "data.zip"); } + /** + * + * @returns {{widgetType, isWidget}|{size: {height: *, width: *}, position: {left: *, top: *}}} + */ + getView() { + // add data-type and data field + any other custom fields in the component-specific attribute + var baseView = super.getView(); + baseView.data = this.props.data; + return baseView; + } + + /** + * + * @param view + */ + setView(view) { + // set base properties + super.setView(view) + if (view.data != undefined){ + this.setData(view.data); + } + } + render() { var dicomViewerContent; if (this.state.mode == "single_view") { From d962af2a1e32ffaad10b49ba0f41ad3583596731 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Thu, 25 May 2017 22:37:34 +0100 Subject: [PATCH 020/371] initial work adding new patienthm sample project and testing new widgets --- .../js/pages/tests/casperjs/CoreTests.js | 23 ++++++++++++++++++- .../pages/tests/casperjs/CoreTestsUtility.js | 12 +++++++++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/main/webapp/js/pages/tests/casperjs/CoreTests.js b/src/main/webapp/js/pages/tests/casperjs/CoreTests.js index 2da477ad6..71c23e6b6 100644 --- a/src/main/webapp/js/pages/tests/casperjs/CoreTests.js +++ b/src/main/webapp/js/pages/tests/casperjs/CoreTests.js @@ -30,6 +30,7 @@ casper.test.begin('Geppetto basic tests', 52, function suite(test) { test.assertExists('div[project-id="16"]', "Project width id 16 from core bundle are present"); test.assertExists('div[project-id="18"]', "Project width id 18 from core bundle are present"); test.assertExists('div[project-id="58"]', "Project width id 58 from core bundle are present"); + test.assertExists('div[project-id="82"]', "Project width id 10 from core bundle are present"); }, null, 3000); }); @@ -88,6 +89,12 @@ casper.test.begin('Geppetto basic tests', 52, function suite(test) { casper.thenOpen(urlBase+baseFollowUp+cElegansPVDR,function() { casper.then(function(){launchTest(test,"cElegansPVDR",180000);}); }); + + /**Tests patientHM project**/ + casper.thenOpen(urlBase+baseFollowUp+patientHM,function() { + casper.then(function(){launchTest(test,"patientHM",180000);}); + casper.then(function(){patientHMTest(test);}); + }); casper.run(function() { test.done(); @@ -461,10 +468,24 @@ function c302Connectome(test){ }, null, 30000); } - function c302PVDR(test){ casper.echo("------------STARTING C302 Muscle Model TEST--------------"); casper.waitForSelector('div[id="Popup1"]', function() { this.echo("I've waited for Popup1 to load."); }, null, 30000); +} + +function patientHMTest(test){ + casper.echo("------------STARTING Patient HM TEST--------------"); + + // casper.waitForSelector('div[id="DicomViewer1"]', function() { + // this.echo("I've waited for DicomViewer1 to load."); + // testDicomWidget(test, "DicomViewer1"); + // }, null, 5000); + + // casper.waitForSelector('div[id="DicomViewer1"]', function() { + // this.echo("I've waited for DicomViewer1 to load."); + // testDicomWidget(test, "DicomViewer1"); + // }, null, 5000); + } \ No newline at end of file diff --git a/src/main/webapp/js/pages/tests/casperjs/CoreTestsUtility.js b/src/main/webapp/js/pages/tests/casperjs/CoreTestsUtility.js index b1d9e5faf..3b7b0c5b6 100644 --- a/src/main/webapp/js/pages/tests/casperjs/CoreTestsUtility.js +++ b/src/main/webapp/js/pages/tests/casperjs/CoreTestsUtility.js @@ -11,6 +11,7 @@ var cElegansPVDR = "load_project_from_id=8"; var eyeWire = "load_project_from_id=9"; var nwbSample = "load_project_from_id=18"; var Pharyngeal = "load_project_from_id=58"; +var patientHM = "load_project_from_id=82"; var defaultColor = [0.00392156862745098,0.6,0.9098039215686274]; var zoomClicks = 50, panClicks=10, rotateClicks=20; @@ -379,4 +380,13 @@ function testingConnectionLines(test, expectedLines){ test.assertEquals(expectedLines, connectionLines, "The control panel opened with right amount of rows"); }); -} \ No newline at end of file +} + +function testBaseWidget(test, id){ + test.assertExists('div[id="'+id+'"]', id + " exists"); +} + +function testDicomWidget(test, id){ + testBaseWidget(test, id); + +} \ No newline at end of file From 2da334e837b9e137019fe3ea319ab103ca8e2b21 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Thu, 25 May 2017 23:36:48 +0100 Subject: [PATCH 021/371] fix scroll on quad view --- .../js/components/interface/dicomViewer/DicomViewer.js | 8 ++++---- .../components/interface/dicomViewer/DicomViewerEngine.js | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js b/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js index 3087db245..32f59498f 100644 --- a/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js +++ b/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js @@ -129,10 +129,10 @@ define(function (require) { else if (this.state.mode == "quad_view") { dicomViewerContent = (
-
-
-
-
+
+
+
+
) } diff --git a/src/main/webapp/js/components/interface/dicomViewer/DicomViewerEngine.js b/src/main/webapp/js/components/interface/dicomViewer/DicomViewerEngine.js index 599b4f4fd..614de77b7 100644 --- a/src/main/webapp/js/components/interface/dicomViewer/DicomViewerEngine.js +++ b/src/main/webapp/js/components/interface/dicomViewer/DicomViewerEngine.js @@ -692,7 +692,7 @@ module.exports = { r3.domElement.addEventListener('dblclick', onDoubleClick); function onScroll(event) { - const id = event.target.domElement.id; + const id = $(event.target.domElement).data("id"); let stackHelper = null; switch (id) { case 'r1': From 5d292520aba37afda143d8ba3e784bd4370c29bc Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Fri, 26 May 2017 00:48:36 +0100 Subject: [PATCH 022/371] fixing double click event --- .../components/interface/dicomViewer/DicomViewerEngine.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/webapp/js/components/interface/dicomViewer/DicomViewerEngine.js b/src/main/webapp/js/components/interface/dicomViewer/DicomViewerEngine.js index 614de77b7..f2fa10b2d 100644 --- a/src/main/webapp/js/components/interface/dicomViewer/DicomViewerEngine.js +++ b/src/main/webapp/js/components/interface/dicomViewer/DicomViewerEngine.js @@ -635,10 +635,11 @@ module.exports = { const canvas = event.srcElement.parentElement; const id = event.target.id; const mouse = { - x: ((event.clientX - canvas.offsetLeft) / canvas.clientWidth) * 2 - 1, - y: - ((event.clientY - canvas.offsetTop) / canvas.clientHeight) * 2 + 1, + x: ((event.clientX - $(canvas).offset().left) / canvas.clientWidth) * 2 - 1, + y: - ((event.clientY - $(canvas).offset().top) / canvas.clientHeight) * 2 + 1, }; - // + + let camera = null; let stackHelper = null; let scene = null; From 383382883b564ec88a27152c49d19d0979259073 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Mon, 29 May 2017 20:34:24 +0100 Subject: [PATCH 023/371] complete refactor: implemented onclick, ctrl + click, scroll, load improvements, clean up code --- .../interface/dicomViewer/DicomViewer.js | 502 ++++++++-- .../dicomViewer/DicomViewerEngine.js | 857 ------------------ .../interface/dicomViewer/DicomViewerUtils.js | 193 ++++ 3 files changed, 628 insertions(+), 924 deletions(-) delete mode 100644 src/main/webapp/js/components/interface/dicomViewer/DicomViewerEngine.js create mode 100644 src/main/webapp/js/components/interface/dicomViewer/DicomViewerUtils.js diff --git a/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js b/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js index 32f59498f..cd9545c47 100644 --- a/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js +++ b/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js @@ -4,22 +4,93 @@ define(function (require) { window.THREE = require('three'); require('./DicomViewer.less'); - var DicomViewerEngine = require('./DicomViewerEngine'); + var DicomViewerUtils = require('./DicomViewerUtils'); var AbstractComponent = require('../../AComponent'); + var AMI = require('./ami.min.js'); + var LoadersVolume = AMI.default.Loaders.Volume; + var HelpersStack = AMI.default.Helpers.Stack; + var HelpersBoundingBox = AMI.default.Helpers.BoundingBox; + var ModelsStack = AMI.default.Models.Stack; + var HelpersLocalizer = AMI.default.Helpers.Localizer; + return class DicomViewer extends AbstractComponent { constructor(props) { super(props); - if (this.props.mode === undefined) { - this.props.mode = "quad_view"; - } - this.state = { files: this.extractFilesPath(this.props.data), - mode: this.props.mode + mode: (this.props.mode === undefined) ? "quad_view" : this.props.mode, + orientation: (this.props.orientation === undefined) ? "coronal" : this.props.orientation + }; + + // 3d renderer + this.r0 = { + domClass: 'r0', + domElement: null, + renderer: null, + color: 0x212121, + targetID: 0, + camera: null, + controls: null, + scene: null, + light: null, + }; + + // 2d axial renderer + this.r1 = { + domClass: 'r1', + domElement: null, + renderer: null, + color: 0x121212, + sliceOrientation: 'axial', + sliceColor: 0xFF1744, + targetID: 1, + camera: null, + controls: null, + scene: null, + light: null, + stackHelper: null, + localizerHelper: null, + localizerScene: null, + }; + + // 2d sagittal renderer + this.r2 = { + domClass: 'r2', + domElement: null, + renderer: null, + color: 0x121212, + sliceOrientation: 'sagittal', + sliceColor: 0xFFEA00, + targetID: 2, + camera: null, + controls: null, + scene: null, + light: null, + stackHelper: null, + localizerHelper: null, + localizerScene: null, + }; + + // 2d coronal renderer + this.r3 = { + domClass: 'r3', + domElement: null, + renderer: null, + color: 0x121212, + sliceOrientation: 'coronal', + sliceColor: 0x76FF03, + targetID: 3, + camera: null, + controls: null, + scene: null, + light: null, + stackHelper: null, + localizerHelper: null, + localizerScene: null, }; this.changeMode = this.changeMode.bind(this); @@ -33,7 +104,7 @@ define(function (require) { extractFilesPath(data) { var files; - if (data != undefined){ + if (data != undefined) { if (data.getMetaType == undefined) { files = data; } @@ -49,21 +120,352 @@ define(function (require) { return files; } - loadView() { - if (this.state.mode == "single_view") { - DicomViewerEngine.loadSingleView(this); + componentDidMount() { + this.ready = false; + var _this = this; + + /** + * Init the quadview + */ + function init() { + /** + * Called on each animation frame + */ + function animate() { + // we are ready when both meshes have been loaded + if (_this.ready) { + // render + _this.r0.controls.update(); + _this.r1.controls.update(); + _this.r2.controls.update(); + _this.r3.controls.update(); + + _this.r0.light.position.copy(_this.r0.camera.position); + _this.r0.renderer.render(_this.r0.scene, _this.r0.camera); + + // r1 + _this.r1.renderer.clear(); + _this.r1.renderer.render(_this.r1.scene, _this.r1.camera); + // localizer + _this.r1.renderer.clearDepth(); + _this.r1.renderer.render(_this.r1.localizerScene, _this.r1.camera); + + // r2 + _this.r2.renderer.clear(); + _this.r2.renderer.render(_this.r2.scene, _this.r2.camera); + // localizer + _this.r2.renderer.clearDepth(); + _this.r2.renderer.render(_this.r2.localizerScene, _this.r2.camera); + + // r3 + _this.r3.renderer.clear(); + _this.r3.renderer.render(_this.r3.scene, _this.r3.camera); + // localizer + _this.r3.renderer.clearDepth(); + _this.r3.renderer.render(_this.r3.localizerScene, _this.r3.camera); + } + + // request new frame + requestAnimationFrame(function () { + animate(); + }); + } + + // renderers + DicomViewerUtils.initRenderer3D(_this.r0, _this.getContainer()); + DicomViewerUtils.initRenderer2D(_this.r1, _this.getContainer()); + DicomViewerUtils.initRenderer2D(_this.r2, _this.getContainer()); + DicomViewerUtils.initRenderer2D(_this.r3, _this.getContainer()); + + // start rendering loop + animate(); } - else if (this.state.mode == "quad_view") { - DicomViewerEngine.loadQuadView(this); + + // init threeJS + init(); + + // load sequence for each file + // instantiate the loader + // it loads and parses the dicom image + let loader = new LoadersVolume(); + loader.load(this.state.files) + .then(function () { + let series = loader.data[0].mergeSeries(loader.data)[0]; + loader.free(); + loader = null; + // get first stack from series + let stack = series.stack[0]; + stack.prepare(); + + // center 3d camera/control on the stack + let centerLPS = stack.worldCenter(); + _this.r0.camera.lookAt(centerLPS.x, centerLPS.y, centerLPS.z); + _this.r0.camera.updateProjectionMatrix(); + _this.r0.controls.target.set(centerLPS.x, centerLPS.y, centerLPS.z); + + // bouding box + let boxHelper = new HelpersBoundingBox(stack); + _this.r0.scene.add(boxHelper); + + // red slice + DicomViewerUtils.initHelpersStack(_this.r1, stack); + _this.r0.scene.add(_this.r1.scene); + + // yellow slice + DicomViewerUtils.initHelpersStack(_this.r2, stack); + _this.r0.scene.add(_this.r2.scene); + + // green slice + DicomViewerUtils.initHelpersStack(_this.r3, stack); + _this.r0.scene.add(_this.r3.scene); + + // create new mesh with Localizer shaders + let plane1 = _this.r1.stackHelper.slice.cartesianEquation(); + let plane2 = _this.r2.stackHelper.slice.cartesianEquation(); + let plane3 = _this.r3.stackHelper.slice.cartesianEquation(); + + // localizer red slice + DicomViewerUtils.initHelpersLocalizer(_this.r1, stack, plane1, [ + { + plane: plane2, + color: new THREE.Color(_this.r2.stackHelper.borderColor), + }, + { + plane: plane3, + color: new THREE.Color(_this.r3.stackHelper.borderColor), + }, + ]); + + // localizer yellow slice + DicomViewerUtils.initHelpersLocalizer(_this.r2, stack, plane2, [ + { + plane: plane1, + color: new THREE.Color(_this.r1.stackHelper.borderColor), + }, + { + plane: plane3, + color: new THREE.Color(_this.r3.stackHelper.borderColor), + }, + ]); + + // localizer green slice + DicomViewerUtils.initHelpersLocalizer(_this.r3, stack, plane3, [ + { + plane: plane1, + color: new THREE.Color(_this.r1.stackHelper.borderColor), + }, + { + plane: plane2, + color: new THREE.Color(_this.r2.stackHelper.borderColor), + }, + ]); + + _this.configureEvents(); + _this.ready = true; + + }) + .catch(function (error) { + window.console.log('oops... something went wrong...'); + window.console.log(error); + }); + + + } + + configureEvents() { + + var _this = this; + function goToPoint(event) { + const canvas = event.srcElement.parentElement; + const id = event.target.id; + const mouse = { + x: ((event.clientX - $(canvas).offset().left) / canvas.clientWidth) * 2 - 1, + y: - ((event.clientY - $(canvas).offset().top) / canvas.clientHeight) * 2 + 1, + }; + + + let camera = null; + let stackHelper = null; + let scene = null; + switch (id) { + case '0': + camera = _this.r0.camera; + stackHelper = _this.r1.stackHelper; + scene = _this.r0.scene; + break; + case '1': + camera = _this.r1.camera; + stackHelper = _this.r1.stackHelper; + scene = _this.r1.scene; + break; + case '2': + camera = _this.r2.camera; + stackHelper = _this.r2.stackHelper; + scene = _this.r2.scene; + break; + case '3': + camera = _this.r3.camera; + stackHelper = _this.r3.stackHelper; + scene = _this.r3.scene; + break; + } + + const raycaster = new THREE.Raycaster(); + raycaster.setFromCamera(mouse, camera); + + const intersects = raycaster.intersectObjects(scene.children, true); + if (intersects.length > 0) { + let ijk = + ModelsStack.worldToData(stackHelper.stack, intersects[0].point); + _this.r1.stackHelper.index = + ijk.getComponent((_this.r1.stackHelper.orientation + 2) % 3); + _this.r2.stackHelper.index = + ijk.getComponent((_this.r2.stackHelper.orientation + 2) % 3); + _this.r3.stackHelper.index = + ijk.getComponent((_this.r3.stackHelper.orientation + 2) % 3); + + DicomViewerUtils.updateLocalizer(_this.r2, [_this.r1.localizerHelper, _this.r3.localizerHelper]); + DicomViewerUtils.updateLocalizer(_this.r1, [_this.r2.localizerHelper, _this.r3.localizerHelper]); + DicomViewerUtils.updateLocalizer(_this.r3, [_this.r1.localizerHelper, _this.r2.localizerHelper]); + } } + + function goToSingleView(event){ + const id = event.target.id; + let orientation = null; + switch (id) { + case '1': + orientation = "sagittal"; + break; + case '2': + orientation = "axial"; + break; + case '3': + orientation = "coronal"; + break; + } + _this.setState({ mode: "single_view", orientation: orientation }); + } + + function onClick(event) { + + if (event.ctrlKey){ + goToSingleView(event); + } + else{ + goToPoint(event); + } + } + + function onScroll(event) { + const id = $(event.target.domElement).data("id"); + let stackHelper = null; + switch (id) { + case 'r1': + stackHelper = _this.r1.stackHelper; + break; + case 'r2': + stackHelper = _this.r2.stackHelper; + break; + case 'r3': + stackHelper = _this.r3.stackHelper; + break; + } + + if (event.delta > 0) { + if (stackHelper.index >= stackHelper.orientationMaxIndex - 1) { + return false; + } + stackHelper.index += 1; + } else { + if (stackHelper.index <= 0) { + return false; + } + stackHelper.index -= 1; + } + + DicomViewerUtils.updateLocalizer(_this.r2, [_this.r1.localizerHelper, _this.r3.localizerHelper]); + DicomViewerUtils.updateLocalizer(_this.r1, [_this.r2.localizerHelper, _this.r3.localizerHelper]); + DicomViewerUtils.updateLocalizer(_this.r3, [_this.r1.localizerHelper, _this.r2.localizerHelper]); + } + + // event listeners + // _this.r0.domElement.addEventListener('dblclick', onDoubleClick); + // _this.r1.domElement.addEventListener('dblclick', onDoubleClick); + // _this.r2.domElement.addEventListener('dblclick', onDoubleClick); + // _this.r3.domElement.addEventListener('dblclick', onDoubleClick); + + // event listeners + this.r0.domElement.addEventListener('click', onClick); + this.r1.domElement.addEventListener('click', onClick); + this.r2.domElement.addEventListener('click', onClick); + this.r3.domElement.addEventListener('click', onClick); + + // event listeners + this.r1.controls.addEventListener('OnScroll', onScroll); + this.r2.controls.addEventListener('OnScroll', onScroll); + this.r3.controls.addEventListener('OnScroll', onScroll); + + + window.addEventListener('resize', this.setLayout, false); + + $(this.getContainer()).parent().on("dialogresizestop", function (event, ui) { + this.setLayout() + }); } - componentDidMount() { - this.loadView(); + setQuadLayout() { + // update 3D + this.r0.camera.aspect = this.r0.domElement.clientWidth / this.r0.domElement.clientHeight; + this.r0.camera.updateProjectionMatrix(); + this.r0.renderer.setSize( + this.r0.domElement.clientWidth, this.r0.domElement.clientHeight); + + // update 2d + DicomViewerUtils.windowResize2D(this.r1); + DicomViewerUtils.windowResize2D(this.r2); + DicomViewerUtils.windowResize2D(this.r3); } - componentDidUpdate() { - this.loadView(); + setSingleLayout() { + var rendererObj; + switch (this.state.orientation) { + case 'sagittal': + rendererObj = this.r1; + break; + case 'axial': + rendererObj = this.r2; + break; + case 'coronal': + rendererObj = this.r3; + break; + } + + + var container = this.getContainer(); + rendererObj.camera.canvas = { + width: container.offsetWidth, + height: container.offsetHeight, + }; + rendererObj.stackHelper.slice.canvasWidth = + container.offsetWidth; + rendererObj.stackHelper.slice.canvasHeight = + container.offsetHeight + 20; + rendererObj.camera.fitBox(2); + rendererObj.renderer.setSize(container.offsetWidth, container.offsetHeight); + } + + setLayout() { + if (this.state.mode == 'single_view') { + this.setSingleLayout(); + } + else { + this.setQuadLayout(); + } + } + + componentDidUpdate(prevProps, prevState) { + this.setLayout(); } changeMode() { @@ -76,66 +478,26 @@ define(function (require) { } changeOrientation() { - if (this.camera.orientation == "coronal") { - this.camera.orientation = "sagittal"; + var newOrientation; + if (this.state.orientation == "coronal") { + newOrientation = "sagittal"; } - else if (this.camera.orientation == "sagittal") { - this.camera.orientation = "axial"; + else if (this.state.orientation == "sagittal") { + newOrientation = "axial"; } - else if (this.camera.orientation == "axial") { - this.camera.orientation = "coronal"; + else if (this.state.orientation == "axial") { + newOrientation = "coronal"; } - this.camera.update() - this.camera.fitBox(2) - this.stackHelper.orientation = this.camera.stackOrientation; - this.stackHelper.index = Math.floor(this.stackHelper.orientationMaxIndex / 2); + + this.setState({ orientation: newOrientation }); } download() { GEPPETTO.Utility.createZipFromRemoteFiles(this.state.files, "data.zip"); } - /** - * - * @returns {{widgetType, isWidget}|{size: {height: *, width: *}, position: {left: *, top: *}}} - */ - getView() { - // add data-type and data field + any other custom fields in the component-specific attribute - var baseView = super.getView(); - baseView.data = this.props.data; - return baseView; - } - - /** - * - * @param view - */ - setView(view) { - // set base properties - super.setView(view) - if (view.data != undefined){ - this.setData(view.data); - } - } - render() { - var dicomViewerContent; - if (this.state.mode == "single_view") { - dicomViewerContent = ( -
-
- ) - } - else if (this.state.mode == "quad_view") { - dicomViewerContent = ( -
-
-
-
-
-
- ) - } + return (
) } diff --git a/src/main/webapp/js/components/interface/dicomViewer/DicomViewerEngine.js b/src/main/webapp/js/components/interface/dicomViewer/DicomViewerEngine.js deleted file mode 100644 index f2fa10b2d..000000000 --- a/src/main/webapp/js/components/interface/dicomViewer/DicomViewerEngine.js +++ /dev/null @@ -1,857 +0,0 @@ -var AMI = require('./ami.min.js'); -var LoadersVolume = AMI.default.Loaders.Volume; -var CamerasOrthographic = AMI.default.Cameras.Orthographic; -var ControlsOrthographic = AMI.default.Controls.TrackballOrtho; -var HelpersStack = AMI.default.Helpers.Stack; -var ControlsTrackball = AMI.default.Controls.Trackball; -var HelpersBoundingBox = AMI.default.Helpers.BoundingBox; -var ModelsStack = AMI.default.Models.Stack; -var HelpersLocalizer = AMI.default.Helpers.Localizer; - -module.exports = { - loadSingleView: function (component) { - - // Setup renderer - var container = component.getContainer().getElementsByClassName('dicomViewer')[0]; - container.innerHTML = ''; - var renderer = new THREE.WebGLRenderer({ - antialias: true, - }); - renderer.setSize(container.offsetWidth, container.offsetHeight); - renderer.setClearColor(0x353535, 1); - renderer.setPixelRatio(window.devicePixelRatio); - container.appendChild(renderer.domElement); - - // Setup scene - var scene = new THREE.Scene(); - - // Setup camera - var camera = new CamerasOrthographic( - container.clientWidth / -2, container.clientWidth / 2, - container.clientHeight / 2, container.clientHeight / -2, - 0.1, 10000); - - component.camera = camera; - - // Setup controls - var controls = new ControlsOrthographic(camera, container); - controls.staticMoving = true; - controls.noRotate = true; - camera.controls = controls; - - /** - * Handle window resize - */ - function onWindowResize() { - camera.canvas = { - width: container.offsetWidth, - height: container.offsetHeight, - }; - camera.fitBox(2); - - renderer.setSize(container.offsetWidth, container.offsetHeight); - } - window.addEventListener('resize', onWindowResize, false); - - $("#" + component.props.id).on("dialogresizestop", function (event, ui) { - camera.canvas = { - width: container.offsetWidth, - height: container.offsetHeight, - }; - camera.fitBox(2); - renderer.setSize(container.offsetWidth, container.offsetHeight); - }); - - /** - * Start animation loop - */ - function animate() { - controls.update(); - renderer.render(scene, camera); - - // request new frame - requestAnimationFrame(function () { - if (component.state.mode == "single_view") { - animate(); - } - }); - } - animate(); - - // Setup loader - var loader = new LoadersVolume(container); - loader.load(component.state.files) - .then(function () { - // merge files into clean series/stack/frame structure - var series = loader.data[0].mergeSeries(loader.data); - var stack = series[0].stack[0]; - loader.free(); - loader = null; - // be carefull that series and target stack exist! - var stackHelper = new HelpersStack(stack); - component.stackHelper = stackHelper; - // stackHelper.orientation = 2; - stackHelper.index = Math.floor(stack._dimensionsIJK.z / 2); - - // tune bounding box - stackHelper.bbox.visible = false; - - // tune slice border - stackHelper.border.color = 0xFF9800; - // stackHelper.border.visible = false; - - scene.add(stackHelper); - - // hook up callbacks - controls.addEventListener('OnScroll', function (e) { - if (e.delta > 0) { - if (stackHelper.index >= stackHelper.orientationMaxIndex - 1) { - return false; - } - stackHelper.index += 1; - } else { - if (stackHelper.index <= 0) { - return false; - } - stackHelper.index -= 1; - } - - }); - - // center camera and interactor to center of bouding box - // for nicer experience - // set camera - var worldbb = stack.worldBoundingBox(); - var lpsDims = new THREE.Vector3( - worldbb[1] - worldbb[0], - worldbb[3] - worldbb[2], - worldbb[5] - worldbb[4] - ); - - // box: {halfDimensions, center} - var box = { - center: stack.worldCenter().clone(), - halfDimensions: - new THREE.Vector3(lpsDims.x + 10, lpsDims.y + 10, lpsDims.z + 10), - }; - - // init and zoom - var canvas = { - width: container.clientWidth, - height: container.clientHeight, - }; - - camera.directions = [stack.xCosine, stack.yCosine, stack.zCosine]; - camera.box = box; - camera.canvas = canvas; - camera.update(); - - // Not working properly. See issue: https://github.com/FNNDSC/ami/issues/120 - //camera.fitBox(2, 2); - camera.fitBox(2); - }) - .catch(function (error) { - window.console.log('oops... something went wrong...'); - window.console.log(error); - }); - - - - }, - - loadQuadView: function (component) { - - let ready = false; - - // 3d renderer - let r0 = { - domClass: 'r0', - domElement: null, - renderer: null, - color: 0x212121, - targetID: 0, - camera: null, - controls: null, - scene: null, - light: null, - }; - - // 2d axial renderer - let r1 = { - domClass: 'r1', - domElement: null, - renderer: null, - color: 0x121212, - sliceOrientation: 'axial', - sliceColor: 0xFF1744, - targetID: 1, - camera: null, - controls: null, - scene: null, - light: null, - stackHelper: null, - localizerHelper: null, - localizerScene: null, - }; - - // 2d sagittal renderer - let r2 = { - domClass: 'r2', - domElement: null, - renderer: null, - color: 0x121212, - sliceOrientation: 'sagittal', - sliceColor: 0xFFEA00, - targetID: 2, - camera: null, - controls: null, - scene: null, - light: null, - stackHelper: null, - localizerHelper: null, - localizerScene: null, - }; - - - // 2d coronal renderer - let r3 = { - domClass: 'r3', - domElement: null, - renderer: null, - color: 0x121212, - sliceOrientation: 'coronal', - sliceColor: 0x76FF03, - targetID: 3, - camera: null, - controls: null, - scene: null, - light: null, - stackHelper: null, - localizerHelper: null, - localizerScene: null, - }; - - // data to be loaded - // let dataInfo = [ - // ['adi1', { - // location: - // 'https://cdn.rawgit.com/FNNDSC/data/master/dicom/adi_brain/mesh.stl', - // label: 'Left', - // loaded: false, - // material: null, - // materialFront: null, - // materialBack: null, - // mesh: null, - // meshFront: null, - // meshBack: null, - // color: 0xe91e63, - // opacity: 0.7, - // }], - // ['adi2', { - // location: - // 'https://cdn.rawgit.com/FNNDSC/data/master/dicom/adi_brain/mesh2.stl', - // label: 'Right', - // loaded: false, - // material: null, - // materialFront: null, - // materialBack: null, - // mesh: null, - // meshFront: null, - // meshBack: null, - // color: 0x03a9f4, - // opacity: 1, - // }], - // ]; - // let data = new Map(dataInfo); - - // extra variables to show mesh plane intersections in 2D renderers - let sceneClip = new THREE.Scene(); - let clipPlane1 = new THREE.Plane(new THREE.Vector3(0, 0, 0), 0); - let clipPlane2 = new THREE.Plane(new THREE.Vector3(0, 0, 0), 0); - let clipPlane3 = new THREE.Plane(new THREE.Vector3(0, 0, 0), 0); - - function initRenderer3D(renderObj) { - // renderer - renderObj.domElement = component.getContainer().getElementsByClassName(renderObj.domClass)[0]; - renderObj.domElement.innerHTML = ''; - - renderObj.renderer = new THREE.WebGLRenderer({ - antialias: true, - }); - renderObj.renderer.setSize( - renderObj.domElement.clientWidth, renderObj.domElement.clientHeight); - renderObj.renderer.setClearColor(renderObj.color, 1); - renderObj.renderer.domElement.id = renderObj.targetID; - renderObj.domElement.appendChild(renderObj.renderer.domElement); - - // camera - renderObj.camera = new THREE.PerspectiveCamera( - 45, renderObj.domElement.clientWidth / renderObj.domElement.clientHeight, - 0.1, 100000); - renderObj.camera.position.x = 250; - renderObj.camera.position.y = 250; - renderObj.camera.position.z = 250; - - // controls - renderObj.controls = new ControlsTrackball( - renderObj.camera, renderObj.domElement); - renderObj.controls.rotateSpeed = 5.5; - renderObj.controls.zoomSpeed = 1.2; - renderObj.controls.panSpeed = 0.8; - renderObj.controls.staticMoving = true; - renderObj.controls.dynamicDampingFactor = 0.3; - - // scene - renderObj.scene = new THREE.Scene(); - - // light - renderObj.light = new THREE.DirectionalLight(0xffffff, 1); - renderObj.light.position.copy(renderObj.camera.position); - renderObj.scene.add(renderObj.light); - - } - - function initRenderer2D(rendererObj) { - // renderer - rendererObj.domElement = component.getContainer().getElementsByClassName(rendererObj.domClass)[0]; - rendererObj.domElement.innerHTML = ''; - rendererObj.renderer = new THREE.WebGLRenderer({ - antialias: true, - }); - rendererObj.renderer.autoClear = false; - rendererObj.renderer.localClippingEnabled = true; - rendererObj.renderer.setSize( - rendererObj.domElement.clientWidth, rendererObj.domElement.clientHeight); - rendererObj.renderer.setClearColor(0x121212, 1); - rendererObj.renderer.domElement.id = rendererObj.targetID; - rendererObj.domElement.appendChild(rendererObj.renderer.domElement); - - // camera - rendererObj.camera = new CamerasOrthographic( - rendererObj.domElement.clientWidth / -2, - rendererObj.domElement.clientWidth / 2, - rendererObj.domElement.clientHeight / 2, - rendererObj.domElement.clientHeight / -2, - 1, 1000); - - // controls - rendererObj.controls = new ControlsOrthographic( - rendererObj.camera, rendererObj.domElement); - rendererObj.controls.staticMoving = true; - rendererObj.controls.noRotate = true; - rendererObj.camera.controls = rendererObj.controls; - - // scene - rendererObj.scene = new THREE.Scene(); - } - - function initHelpersStack(rendererObj, stack) { - rendererObj.stackHelper = new HelpersStack(stack); - rendererObj.stackHelper.bbox.visible = false; - rendererObj.stackHelper.borderColor = rendererObj.sliceColor; - rendererObj.stackHelper.slice.canvasWidth = - rendererObj.domElement.clientWidth; - rendererObj.stackHelper.slice.canvasHeight = - rendererObj.domElement.clientHeight; - - // set camera - let worldbb = stack.worldBoundingBox(); - let lpsDims = new THREE.Vector3( - (worldbb[1] - worldbb[0]) / 2, - (worldbb[3] - worldbb[2]) / 2, - (worldbb[5] - worldbb[4]) / 2 - ); - - // box: {halfDimensions, center} - let box = { - center: stack.worldCenter().clone(), - halfDimensions: - new THREE.Vector3(lpsDims.x + 10, lpsDims.y + 10, lpsDims.z + 10), - }; - - // init and zoom - let canvas = { - width: rendererObj.domElement.clientWidth, - height: rendererObj.domElement.clientHeight, - }; - - rendererObj.camera.directions = - [stack.xCosine, stack.yCosine, stack.zCosine]; - rendererObj.camera.box = box; - rendererObj.camera.canvas = canvas; - rendererObj.camera.orientation = rendererObj.sliceOrientation; - rendererObj.camera.update(); - rendererObj.camera.fitBox(2, 1); - - rendererObj.stackHelper.orientation = rendererObj.camera.stackOrientation; - rendererObj.stackHelper.index = - Math.floor(rendererObj.stackHelper.orientationMaxIndex / 2); - rendererObj.scene.add(rendererObj.stackHelper); - } - - function initHelpersLocalizer(rendererObj, stack, referencePlane, localizers) { - rendererObj.localizerHelper = new HelpersLocalizer( - stack, rendererObj.stackHelper.slice.geometry, referencePlane); - - for (let i = 0; i < localizers.length; i++) { - rendererObj.localizerHelper['plane' + (i + 1)] = localizers[i].plane; - rendererObj.localizerHelper['color' + (i + 1)] = localizers[i].color; - } - - rendererObj.localizerHelper.canvasWidth = - rendererObj.domElement.clientWidth; - rendererObj.localizerHelper.canvasHeight = - rendererObj.domElement.clientHeight; - - rendererObj.localizerScene = new THREE.Scene(); - rendererObj.localizerScene.add(rendererObj.localizerHelper); - } - - /** - * Init the quadview - */ - function init() { - /** - * Called on each animation frame - */ - function animate() { - // we are ready when both meshes have been loaded - if (ready) { - // render - r0.controls.update(); - r1.controls.update(); - r2.controls.update(); - r3.controls.update(); - - r0.light.position.copy(r0.camera.position); - r0.renderer.render(r0.scene, r0.camera); - - // r1 - r1.renderer.clear(); - r1.renderer.render(r1.scene, r1.camera); - // mesh - //r1.renderer.clearDepth(); - // data.forEach(function (object, key) { - // object.materialFront.clippingPlanes = [clipPlane1]; - // object.materialBack.clippingPlanes = [clipPlane1]; - // }); - //r1.renderer.render(sceneClip, r1.camera); - // localizer - r1.renderer.clearDepth(); - r1.renderer.render(r1.localizerScene, r1.camera); - - // r2 - r2.renderer.clear(); - r2.renderer.render(r2.scene, r2.camera); - // mesh - //r2.renderer.clearDepth(); - // data.forEach(function (object, key) { - // object.materialFront.clippingPlanes = [clipPlane2]; - // object.materialBack.clippingPlanes = [clipPlane2]; - // }); - //r2.renderer.render(sceneClip, r2.camera); - // localizer - r2.renderer.clearDepth(); - r2.renderer.render(r2.localizerScene, r2.camera); - - // r3 - r3.renderer.clear(); - r3.renderer.render(r3.scene, r3.camera); - // mesh - //r3.renderer.clearDepth(); - // data.forEach(function (object, key) { - // object.materialFront.clippingPlanes = [clipPlane3]; - // object.materialBack.clippingPlanes = [clipPlane3]; - // }); - //r3.renderer.render(sceneClip, r3.camera); - // localizer - r3.renderer.clearDepth(); - r3.renderer.render(r3.localizerScene, r3.camera); - } - - // request new frame - requestAnimationFrame(function () { - if (component.state.mode == "quad_view") { - animate(); - } - }); - } - - // renderers - initRenderer3D(r0); - initRenderer2D(r1); - initRenderer2D(r2); - initRenderer2D(r3); - - // start rendering loop - animate(); - } - - // init threeJS - init(); - - - // load sequence for each file - // instantiate the loader - // it loads and parses the dicom image - let loader = new LoadersVolume(); - loader.load(component.state.files) - .then(function () { - let series = loader.data[0].mergeSeries(loader.data)[0]; - loader.free(); - loader = null; - // get first stack from series - let stack = series.stack[0]; - stack.prepare(); - - // center 3d camera/control on the stack - let centerLPS = stack.worldCenter(); - r0.camera.lookAt(centerLPS.x, centerLPS.y, centerLPS.z); - r0.camera.updateProjectionMatrix(); - r0.controls.target.set(centerLPS.x, centerLPS.y, centerLPS.z); - - // bouding box - let boxHelper = new HelpersBoundingBox(stack); - r0.scene.add(boxHelper); - - // red slice - initHelpersStack(r1, stack); - r0.scene.add(r1.scene); - - // yellow slice - initHelpersStack(r2, stack); - r0.scene.add(r2.scene); - - // green slice - initHelpersStack(r3, stack); - r0.scene.add(r3.scene); - - // create new mesh with Localizer shaders - let plane1 = r1.stackHelper.slice.cartesianEquation(); - let plane2 = r2.stackHelper.slice.cartesianEquation(); - let plane3 = r3.stackHelper.slice.cartesianEquation(); - - // localizer red slice - initHelpersLocalizer(r1, stack, plane1, [ - { - plane: plane2, - color: new THREE.Color(r2.stackHelper.borderColor), - }, - { - plane: plane3, - color: new THREE.Color(r3.stackHelper.borderColor), - }, - ]); - - // localizer yellow slice - initHelpersLocalizer(r2, stack, plane2, [ - { - plane: plane1, - color: new THREE.Color(r1.stackHelper.borderColor), - }, - { - plane: plane3, - color: new THREE.Color(r3.stackHelper.borderColor), - }, - ]); - - // localizer green slice - initHelpersLocalizer(r3, stack, plane3, [ - { - plane: plane1, - color: new THREE.Color(r1.stackHelper.borderColor), - }, - { - plane: plane2, - color: new THREE.Color(r2.stackHelper.borderColor), - }, - ]); - - - - /** - * Update Layer Mix - */ - function updateLocalizer(refObj, targetLocalizersHelpers) { - let refHelper = refObj.stackHelper; - let localizerHelper = refObj.localizerHelper; - let plane = refHelper.slice.cartesianEquation(); - localizerHelper.referencePlane = plane; - - // bit of a hack... works fine for this application - for (let i = 0; i < targetLocalizersHelpers.length; i++) { - for (let j = 0; j < 4; j++) { - let targetPlane = targetLocalizersHelpers[i]['plane' + (j + 1)]; - if (targetPlane && - plane.x === targetPlane.x && - plane.y === targetPlane.y && - plane.z === targetPlane.z) { - targetLocalizersHelpers[i]['plane' + (j + 1)] = plane; - } - } - } - - // update the geometry will create a new mesh - localizerHelper.geometry = refHelper.slice.geometry; - } - - function updateClipPlane(refObj, clipPlane) { - const stackHelper = refObj.stackHelper; - const camera = refObj.camera; - let vertices = stackHelper.slice.geometry.vertices; - let p1 = new THREE.Vector3(vertices[0].x, vertices[0].y, vertices[0].z) - .applyMatrix4(stackHelper._stack.ijk2LPS); - let p2 = new THREE.Vector3(vertices[1].x, vertices[1].y, vertices[1].z) - .applyMatrix4(stackHelper._stack.ijk2LPS); - let p3 = new THREE.Vector3(vertices[2].x, vertices[2].y, vertices[2].z) - .applyMatrix4(stackHelper._stack.ijk2LPS); - - clipPlane.setFromCoplanarPoints(p1, p2, p3); - - let cameraDirection = new THREE.Vector3(1, 1, 1); - cameraDirection.applyQuaternion(camera.quaternion); - - if (cameraDirection.dot(clipPlane.normal) > 0) { - clipPlane.negate(); - } - } - - function onYellowChanged() { - updateLocalizer(r2, [r1.localizerHelper, r3.localizerHelper]); - updateClipPlane(r2, clipPlane2); - } - - function onRedChanged() { - updateLocalizer(r1, [r2.localizerHelper, r3.localizerHelper]); - updateClipPlane(r1, clipPlane1); - } - - function onGreenChanged() { - updateLocalizer(r3, [r1.localizerHelper, r2.localizerHelper]); - updateClipPlane(r3, clipPlane3); - } - - function onDoubleClick(event) { - const canvas = event.srcElement.parentElement; - const id = event.target.id; - const mouse = { - x: ((event.clientX - $(canvas).offset().left) / canvas.clientWidth) * 2 - 1, - y: - ((event.clientY - $(canvas).offset().top) / canvas.clientHeight) * 2 + 1, - }; - - - let camera = null; - let stackHelper = null; - let scene = null; - switch (id) { - case '0': - camera = r0.camera; - stackHelper = r1.stackHelper; - scene = r0.scene; - break; - case '1': - camera = r1.camera; - stackHelper = r1.stackHelper; - scene = r1.scene; - break; - case '2': - camera = r2.camera; - stackHelper = r2.stackHelper; - scene = r2.scene; - break; - case '3': - camera = r3.camera; - stackHelper = r3.stackHelper; - scene = r3.scene; - break; - } - - const raycaster = new THREE.Raycaster(); - raycaster.setFromCamera(mouse, camera); - - const intersects = raycaster.intersectObjects(scene.children, true); - if (intersects.length > 0) { - let ijk = - ModelsStack.worldToData(stackHelper.stack, intersects[0].point); - r1.stackHelper.index = - ijk.getComponent((r1.stackHelper.orientation + 2) % 3); - r2.stackHelper.index = - ijk.getComponent((r2.stackHelper.orientation + 2) % 3); - r3.stackHelper.index = - ijk.getComponent((r3.stackHelper.orientation + 2) % 3); - - onGreenChanged(); - onRedChanged(); - onYellowChanged(); - } - } - - // event listeners - r0.domElement.addEventListener('dblclick', onDoubleClick); - r1.domElement.addEventListener('dblclick', onDoubleClick); - r2.domElement.addEventListener('dblclick', onDoubleClick); - r3.domElement.addEventListener('dblclick', onDoubleClick); - - function onScroll(event) { - const id = $(event.target.domElement).data("id"); - let stackHelper = null; - switch (id) { - case 'r1': - stackHelper = r1.stackHelper; - break; - case 'r2': - stackHelper = r2.stackHelper; - break; - case 'r3': - stackHelper = r3.stackHelper; - break; - } - - if (event.delta > 0) { - if (stackHelper.index >= stackHelper.orientationMaxIndex - 1) { - return false; - } - stackHelper.index += 1; - } else { - if (stackHelper.index <= 0) { - return false; - } - stackHelper.index -= 1; - } - - onGreenChanged(); - onRedChanged(); - onYellowChanged(); - } - - // event listeners - r1.controls.addEventListener('OnScroll', onScroll); - r2.controls.addEventListener('OnScroll', onScroll); - r3.controls.addEventListener('OnScroll', onScroll); - - function windowResize2D(rendererObj) { - rendererObj.camera.canvas = { - width: rendererObj.domElement.clientWidth, - height: rendererObj.domElement.clientHeight, - }; - rendererObj.camera.fitBox(2, 1); - rendererObj.renderer.setSize( - rendererObj.domElement.clientWidth, - rendererObj.domElement.clientHeight); - - // update info to draw borders properly - rendererObj.stackHelper.slice.canvasWidth = - rendererObj.domElement.clientWidth; - rendererObj.stackHelper.slice.canvasHeight = - rendererObj.domElement.clientHeight; - rendererObj.localizerHelper.canvasWidth = - rendererObj.domElement.clientWidth; - rendererObj.localizerHelper.canvasHeight = - rendererObj.domElement.clientHeight; - } - - function onWindowResize() { - // update 3D - r0.camera.aspect = r0.domElement.clientWidth / r0.domElement.clientHeight; - r0.camera.updateProjectionMatrix(); - r0.renderer.setSize( - r0.domElement.clientWidth, r0.domElement.clientHeight); - - // update 2d - windowResize2D(r1); - windowResize2D(r2); - windowResize2D(r3); - } - - window.addEventListener('resize', onWindowResize, false); - - $("#" + component.props.id).on("dialogresizestop", function (event, ui) { - // update 3D - r0.camera.aspect = r0.domElement.clientWidth / r0.domElement.clientHeight; - r0.camera.updateProjectionMatrix(); - r0.renderer.setSize( - r0.domElement.clientWidth, r0.domElement.clientHeight); - - // update 2d - windowResize2D(r1); - windowResize2D(r2); - windowResize2D(r3); - - }); - - - ready = true; - - // load meshes on the stack is all set - // let meshesLoaded = 0; - // function loadSTLObject(object) { - // const stlLoader = new THREE.STLLoader(); - // stlLoader.load(object.location, function (geometry) { - // // 3D mesh - // object.material = new THREE.MeshLambertMaterial({ - // opacity: object.opacity, - // color: object.color, - // clippingPlanes: [], - // side: THREE.DoubleSide, - // transparent: true, - // }); - // object.mesh = new THREE.Mesh(geometry, object.material); - // const RASToLPS = new THREE.Matrix4(); - // RASToLPS.set(-1, 0, 0, 0, - // 0, -1, 0, 0, - // 0, 0, 1, 0, - // 0, 0, 0, 1); - // object.mesh.applyMatrix(RASToLPS); - // r0.scene.add(object.mesh); - - // // front - // object.materialFront = new THREE.MeshBasicMaterial({ - // color: object.color, - // side: THREE.FrontSide, - // depthWrite: true, - // opacity: 0, - // transparent: true, - // clippingPlanes: [], - // }); - - // object.meshFront = new THREE.Mesh(geometry, object.materialFront); - // object.meshFront.applyMatrix(RASToLPS); - // sceneClip.add(object.meshFront); - - // // back - // object.materialBack = new THREE.MeshBasicMaterial({ - // color: object.color, - // side: THREE.BackSide, - // depthWrite: true, - // opacity: object.opacity, - // transparent: true, - // clippingPlanes: [], - // }); - - // object.meshBack = new THREE.Mesh(geometry, object.materialBack); - // object.meshBack.applyMatrix(RASToLPS); - // sceneClip.add(object.meshBack); - - // meshesLoaded++; - - // onGreenChanged(); - // onRedChanged(); - // onYellowChanged(); - - // // good to go - // if (meshesLoaded === data.size) { - // ready = true; - // } - // }); - // } - - // data.forEach(function (object, key) { - // loadSTLObject(object); - // }); - }) - .catch(function (error) { - window.console.log('oops... something went wrong...'); - window.console.log(error); - }); - } -}; diff --git a/src/main/webapp/js/components/interface/dicomViewer/DicomViewerUtils.js b/src/main/webapp/js/components/interface/dicomViewer/DicomViewerUtils.js new file mode 100644 index 000000000..6500350e4 --- /dev/null +++ b/src/main/webapp/js/components/interface/dicomViewer/DicomViewerUtils.js @@ -0,0 +1,193 @@ +var AMI = require('./ami.min.js'); +// var LoadersVolume = AMI.default.Loaders.Volume; +var CamerasOrthographic = AMI.default.Cameras.Orthographic; +var ControlsOrthographic = AMI.default.Controls.TrackballOrtho; +var HelpersStack = AMI.default.Helpers.Stack; +var ControlsTrackball = AMI.default.Controls.Trackball; +// var HelpersBoundingBox = AMI.default.Helpers.BoundingBox; +// var ModelsStack = AMI.default.Models.Stack; +var HelpersLocalizer = AMI.default.Helpers.Localizer; + +module.exports = { + windowResize2D: function (rendererObj) { + rendererObj.camera.canvas = { + width: rendererObj.domElement.clientWidth, + height: rendererObj.domElement.clientHeight, + }; + rendererObj.camera.fitBox(2, 1); + rendererObj.renderer.setSize( + rendererObj.domElement.clientWidth, + rendererObj.domElement.clientHeight); + + // update info to draw borders properly + rendererObj.stackHelper.slice.canvasWidth = + rendererObj.domElement.clientWidth; + rendererObj.stackHelper.slice.canvasHeight = + rendererObj.domElement.clientHeight; + rendererObj.localizerHelper.canvasWidth = + rendererObj.domElement.clientWidth; + rendererObj.localizerHelper.canvasHeight = + rendererObj.domElement.clientHeight; + }, + + initHelpersStack: function (rendererObj, stack) { + rendererObj.stackHelper = new HelpersStack(stack); + rendererObj.stackHelper.bbox.visible = false; + rendererObj.stackHelper.borderColor = rendererObj.sliceColor; + rendererObj.stackHelper.slice.canvasWidth = + rendererObj.domElement.clientWidth; + rendererObj.stackHelper.slice.canvasHeight = + rendererObj.domElement.clientHeight; + + // set camera + let worldbb = stack.worldBoundingBox(); + let lpsDims = new THREE.Vector3( + (worldbb[1] - worldbb[0]) / 2, + (worldbb[3] - worldbb[2]) / 2, + (worldbb[5] - worldbb[4]) / 2 + ); + + // box: {halfDimensions, center} + let box = { + center: stack.worldCenter().clone(), + halfDimensions: + new THREE.Vector3(lpsDims.x + 10, lpsDims.y + 10, lpsDims.z + 10), + }; + + // init and zoom + let canvas = { + width: rendererObj.domElement.clientWidth, + height: rendererObj.domElement.clientHeight, + }; + + rendererObj.camera.directions = + [stack.xCosine, stack.yCosine, stack.zCosine]; + rendererObj.camera.box = box; + rendererObj.camera.canvas = canvas; + rendererObj.camera.orientation = rendererObj.sliceOrientation; + rendererObj.camera.update(); + rendererObj.camera.fitBox(2, 1); + + rendererObj.stackHelper.orientation = rendererObj.camera.stackOrientation; + rendererObj.stackHelper.index = + Math.floor(rendererObj.stackHelper.orientationMaxIndex / 2); + rendererObj.scene.add(rendererObj.stackHelper); + }, + + initHelpersLocalizer: function (rendererObj, stack, referencePlane, localizers) { + rendererObj.localizerHelper = new HelpersLocalizer( + stack, rendererObj.stackHelper.slice.geometry, referencePlane); + + for (let i = 0; i < localizers.length; i++) { + rendererObj.localizerHelper['plane' + (i + 1)] = localizers[i].plane; + rendererObj.localizerHelper['color' + (i + 1)] = localizers[i].color; + } + + rendererObj.localizerHelper.canvasWidth = + rendererObj.domElement.clientWidth; + rendererObj.localizerHelper.canvasHeight = + rendererObj.domElement.clientHeight; + + rendererObj.localizerScene = new THREE.Scene(); + rendererObj.localizerScene.add(rendererObj.localizerHelper); + }, + + updateLocalizer: function (refObj, targetLocalizersHelpers) { + let refHelper = refObj.stackHelper; + let localizerHelper = refObj.localizerHelper; + let plane = refHelper.slice.cartesianEquation(); + localizerHelper.referencePlane = plane; + + // bit of a hack... works fine for this application + for (let i = 0; i < targetLocalizersHelpers.length; i++) { + for (let j = 0; j < 4; j++) { + let targetPlane = targetLocalizersHelpers[i]['plane' + (j + 1)]; + if (targetPlane && + plane.x === targetPlane.x && + plane.y === targetPlane.y && + plane.z === targetPlane.z) { + targetLocalizersHelpers[i]['plane' + (j + 1)] = plane; + } + } + } + + // update the geometry will create a new mesh + localizerHelper.geometry = refHelper.slice.geometry; + }, + + initRenderer2D: function (rendererObj, parentContainer) { + // renderer + rendererObj.domElement = parentContainer.getElementsByClassName(rendererObj.domClass)[0]; + rendererObj.domElement.innerHTML = ''; + rendererObj.renderer = new THREE.WebGLRenderer({ + antialias: true, + }); + rendererObj.renderer.autoClear = false; + rendererObj.renderer.localClippingEnabled = true; + rendererObj.renderer.setSize( + rendererObj.domElement.clientWidth, rendererObj.domElement.clientHeight); + rendererObj.renderer.setClearColor(0x121212, 1); + rendererObj.renderer.setPixelRatio(window.devicePixelRatio); + rendererObj.renderer.domElement.id = rendererObj.targetID; + rendererObj.domElement.appendChild(rendererObj.renderer.domElement); + + // camera + rendererObj.camera = new CamerasOrthographic( + rendererObj.domElement.clientWidth / -2, + rendererObj.domElement.clientWidth / 2, + rendererObj.domElement.clientHeight / 2, + rendererObj.domElement.clientHeight / -2, + 1, 1000); + + // controls + rendererObj.controls = new ControlsOrthographic( + rendererObj.camera, rendererObj.domElement); + rendererObj.controls.staticMoving = true; + rendererObj.controls.noRotate = true; + rendererObj.camera.controls = rendererObj.controls; + + // scene + rendererObj.scene = new THREE.Scene(); + }, + + initRenderer3D: function (renderObj, parentContainer) { + // renderer + renderObj.domElement = parentContainer.getElementsByClassName(renderObj.domClass)[0]; + renderObj.domElement.innerHTML = ''; + + renderObj.renderer = new THREE.WebGLRenderer({ + antialias: true, + }); + renderObj.renderer.setSize( + renderObj.domElement.clientWidth, renderObj.domElement.clientHeight); + renderObj.renderer.setClearColor(renderObj.color, 1); + renderObj.renderer.domElement.id = renderObj.targetID; + renderObj.domElement.appendChild(renderObj.renderer.domElement); + + // camera + renderObj.camera = new THREE.PerspectiveCamera( + 45, renderObj.domElement.clientWidth / renderObj.domElement.clientHeight, + 0.1, 100000); + renderObj.camera.position.x = 250; + renderObj.camera.position.y = 250; + renderObj.camera.position.z = 250; + + // controls + renderObj.controls = new ControlsTrackball( + renderObj.camera, renderObj.domElement); + renderObj.controls.rotateSpeed = 5.5; + renderObj.controls.zoomSpeed = 1.2; + renderObj.controls.panSpeed = 0.8; + renderObj.controls.staticMoving = true; + renderObj.controls.dynamicDampingFactor = 0.3; + + // scene + renderObj.scene = new THREE.Scene(); + + // light + renderObj.light = new THREE.DirectionalLight(0xffffff, 1); + renderObj.light.position.copy(renderObj.camera.position); + renderObj.scene.add(renderObj.light); + + } +}; From f258266c693b5cf3340635b333cd82722c22c1f9 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Mon, 29 May 2017 20:41:11 +0100 Subject: [PATCH 024/371] exclude ami.js --- src/main/webapp/webpack.config.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/webapp/webpack.config.js b/src/main/webapp/webpack.config.js index d7a4cde42..efcef2f41 100644 --- a/src/main/webapp/webpack.config.js +++ b/src/main/webapp/webpack.config.js @@ -148,7 +148,8 @@ module.exports = { noParse: [/node_modules\/plotly.js\/dist\/plotly.js/, /js\/components\/interface\/dicomViewer\/ami.min.js/], loaders: [ { - test: /\.(js)$/, exclude: [/node_modules/, /build/, /\.bundle/], loader: ['babel-loader'], + test: /\.(js)$/, exclude: [/node_modules/, /build/, /\.bundle/, /ami.min.js/], loader: ['babel-loader'], + query: { presets: ['react', 'es2015'] } From 38460ee2a803d69269f48a5980c1257f1154112d Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Mon, 29 May 2017 21:26:57 +0100 Subject: [PATCH 025/371] change download button --- src/main/webapp/js/components/widgets/AWidget.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/webapp/js/components/widgets/AWidget.js b/src/main/webapp/js/components/widgets/AWidget.js index 10f932f4c..6d8ba3630 100644 --- a/src/main/webapp/js/components/widgets/AWidget.js +++ b/src/main/webapp/js/components/widgets/AWidget.js @@ -436,7 +436,7 @@ define(function (require) { addDownloadButton(downloadFunction){ var that = this; - this.addButtonToTitleBar($("
").click(function () { + this.addButtonToTitleBar($("
").click(function () { that.download(); })); } From 9b174debeb10d9b4744a8547a2831426384273f9 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Mon, 29 May 2017 23:03:04 +0100 Subject: [PATCH 026/371] add widget button bar component --- .../widgetButtonBar/WidgetButtonBar.js | 27 +++++++++++++++++++ .../widgetButtonBar/WidgetButtonBar.less | 14 ++++++++++ 2 files changed, 41 insertions(+) create mode 100644 src/main/webapp/js/components/controls/widgetButtonBar/WidgetButtonBar.js create mode 100644 src/main/webapp/js/components/controls/widgetButtonBar/WidgetButtonBar.less diff --git a/src/main/webapp/js/components/controls/widgetButtonBar/WidgetButtonBar.js b/src/main/webapp/js/components/controls/widgetButtonBar/WidgetButtonBar.js new file mode 100644 index 000000000..5bc450402 --- /dev/null +++ b/src/main/webapp/js/components/controls/widgetButtonBar/WidgetButtonBar.js @@ -0,0 +1,27 @@ +define(function (require) { + + var React = require('react'); + require("./WidgetButtonBar.less"); + + var WidgetButtonBar = React.createClass({ + getInitialState: function () { + return { + + }; + }, + + componentDidMount: function () { + + }, + + render: function () { + return ( +
+ {this.props.children} +
+ ); + } + }); + + return WidgetButtonBar; +}); \ No newline at end of file diff --git a/src/main/webapp/js/components/controls/widgetButtonBar/WidgetButtonBar.less b/src/main/webapp/js/components/controls/widgetButtonBar/WidgetButtonBar.less new file mode 100644 index 000000000..0e6f29825 --- /dev/null +++ b/src/main/webapp/js/components/controls/widgetButtonBar/WidgetButtonBar.less @@ -0,0 +1,14 @@ +.displayArea{ + position: absolute; + top: 0; + left: 0; + width: 20px; + margin: 6px; +} + +.displayArea > button{ + padding: 0; + z-index: 999; + border: 0; + background: transparent !important; +} From 35329541bb48e7c6cfe411de574056add2fc8906 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Mon, 29 May 2017 23:03:25 +0100 Subject: [PATCH 027/371] use widget button bar component --- .../bigImageViewer/BigImageViewer.js | 33 ++++--------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/src/main/webapp/js/components/interface/bigImageViewer/BigImageViewer.js b/src/main/webapp/js/components/interface/bigImageViewer/BigImageViewer.js index c6fbbd57b..5f2583dc1 100644 --- a/src/main/webapp/js/components/interface/bigImageViewer/BigImageViewer.js +++ b/src/main/webapp/js/components/interface/bigImageViewer/BigImageViewer.js @@ -4,6 +4,7 @@ define(function (require) { var React = require('react'); var OpenSeaDragon = require('openseadragon'); + var WidgetButtonBar = require('../../controls/widgetButtonBar/WidgetButtonBar'); var AbstractComponent = require('../../AComponent'); return class BigImageViewer extends AbstractComponent { @@ -65,32 +66,12 @@ define(function (require) { render() { return (
-
-
+ +
) } From 60de98cd8070211af98bb33b7031ef622821b0d7 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Mon, 29 May 2017 23:04:14 +0100 Subject: [PATCH 028/371] use widget button bar component, clean up, fix resize event, bug fixing --- .../interface/dicomViewer/DicomViewer.js | 53 +++++++------------ .../interface/dicomViewer/DicomViewerUtils.js | 3 -- 2 files changed, 20 insertions(+), 36 deletions(-) diff --git a/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js b/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js index cd9545c47..adc4afbac 100644 --- a/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js +++ b/src/main/webapp/js/components/interface/dicomViewer/DicomViewer.js @@ -2,12 +2,6 @@ define(function (require) { var React = require('react'); window.THREE = require('three'); - - require('./DicomViewer.less'); - var DicomViewerUtils = require('./DicomViewerUtils'); - - var AbstractComponent = require('../../AComponent'); - var AMI = require('./ami.min.js'); var LoadersVolume = AMI.default.Loaders.Volume; var HelpersStack = AMI.default.Helpers.Stack; @@ -15,6 +9,11 @@ define(function (require) { var ModelsStack = AMI.default.Models.Stack; var HelpersLocalizer = AMI.default.Helpers.Localizer; + require('./DicomViewer.less'); + var DicomViewerUtils = require('./DicomViewerUtils'); + var WidgetButtonBar = require('../../controls/widgetButtonBar/WidgetButtonBar'); + var AbstractComponent = require('../../AComponent'); + return class DicomViewer extends AbstractComponent { constructor(props) { @@ -330,7 +329,7 @@ define(function (require) { } } - function goToSingleView(event){ + function goToSingleView(event) { const id = event.target.id; let orientation = null; switch (id) { @@ -344,15 +343,18 @@ define(function (require) { orientation = "coronal"; break; } - _this.setState({ mode: "single_view", orientation: orientation }); + + if (orientation != null) { + _this.setState({ mode: "single_view", orientation: orientation }); + } } function onClick(event) { - - if (event.ctrlKey){ + + if (event.ctrlKey) { goToSingleView(event); } - else{ + else { goToPoint(event); } } @@ -407,10 +409,10 @@ define(function (require) { this.r3.controls.addEventListener('OnScroll', onScroll); - window.addEventListener('resize', this.setLayout, false); + window.addEventListener('resize', function(){_this.setLayout();}, false); - $(this.getContainer()).parent().on("dialogresizestop", function (event, ui) { - this.setLayout() + $(this.getContainer()).parent().on("resizeEnd", function (event, ui) { + _this.setLayout() }); } @@ -500,25 +502,10 @@ define(function (require) { return (
-
) From bc4cf72817ffd068ca3add64ad50b7c47b531a39 Mon Sep 17 00:00:00 2001 From: jrmartin Date: Mon, 12 Jun 2017 12:05:09 -0700 Subject: [PATCH 055/371] shows tooltip for experiment's table icon status only if it's in view --- .../interface/experimentsTable/ExperimentsTable.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/webapp/js/components/interface/experimentsTable/ExperimentsTable.js b/src/main/webapp/js/components/interface/experimentsTable/ExperimentsTable.js index 082dbc95d..47dff9fa6 100644 --- a/src/main/webapp/js/components/interface/experimentsTable/ExperimentsTable.js +++ b/src/main/webapp/js/components/interface/experimentsTable/ExperimentsTable.js @@ -678,8 +678,9 @@ define(function (require) { } }, + //Determines if an element inside the experiments table is in view isInView : function(elem){ - var docViewTop = $('#experimentsTable').scrollTop(); + var docViewTop = $('#experimentsTable').offset().top; var docViewBottom = docViewTop + $('#experimentsTable').height(); var elemTop = $(elem).offset().top; var elemBottom = elemTop + $(elem).height(); @@ -707,7 +708,7 @@ define(function (require) { tdStatus.attr("data-status", experiment.getStatus()); tdStatus.attr("data-custom-title", GEPPETTO.Resources.ExperimentStatus.Descriptions[experiment.getStatus()]); - if(self.isInView($(this))) { + if(self.isInView(tdStatus)) { // make the tooltip pop-out for a bit to attract attention tdStatus.mouseover().delay(2000).queue(function () { $(this).mouseout().dequeue(); From 1730e3b20c84ae8097f424d464b1a73cf855f372 Mon Sep 17 00:00:00 2001 From: Matt Earnshaw Date: Tue, 13 Jun 2017 14:53:10 +0100 Subject: [PATCH 056/371] keep colors when switching geometry types --- .../webapp/js/components/interface/3dCanvas/ThreeDEngine.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js b/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js index a2aec801d..f9632789c 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js +++ b/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js @@ -1155,7 +1155,7 @@ define(['jquery'], function () { color = GEPPETTO.Resources.COLORS.DEFAULT; } var material = new THREE.LineBasicMaterial(options); - material.color.setHex(color); + this.setThreeColor(material.color, color); material.defaultColor = color; material.defaultOpacity = GEPPETTO.Resources.OPACITY.DEFAULT; return material; @@ -1177,7 +1177,7 @@ define(['jquery'], function () { shading: THREE.SmoothShading }); - material.color.setHex(color); + this.setThreeColor(material.color, color); material.defaultColor = color; material.defaultOpacity = GEPPETTO.Resources.OPACITY.DEFAULT; return material; From 8c8f3b9115502ac673da58c67064bd54b7b7ec50 Mon Sep 17 00:00:00 2001 From: Matt Earnshaw Date: Tue, 13 Jun 2017 16:51:48 +0100 Subject: [PATCH 057/371] making connectivity widget colors match populations --- .../widgets/connectivity/Connectivity.js | 18 +++++++++++++++++- .../components/widgets/connectivity/chords.js | 6 ++---- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/main/webapp/js/components/widgets/connectivity/Connectivity.js b/src/main/webapp/js/components/widgets/connectivity/Connectivity.js index a9e9a6b43..55b57464c 100644 --- a/src/main/webapp/js/components/widgets/connectivity/Connectivity.js +++ b/src/main/webapp/js/components/widgets/connectivity/Connectivity.js @@ -28,6 +28,7 @@ define(function (require) { dataset: {}, connectivityOptions: {}, + nodeColormap: {}, defaultConnectivityOptions: { width: 660, height: 500, @@ -47,10 +48,25 @@ define(function (require) { } }, - + initialize: function (options) { this.options = options; + this.nodeColormap = function(){ + var pops = GEPPETTO.ModelFactory.getAllInstancesOf( + GEPPETTO.ModelFactory.getAllTypesOfType(GEPPETTO.ModelFactory.geppettoModel.neuroml.network)[0])[0].getChildren(); + var domain = []; + var range = []; + for (var i=0; i Date: Wed, 14 Jun 2017 00:47:34 +0100 Subject: [PATCH 058/371] add default parameters --- src/main/webapp/webpack.config.js | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/main/webapp/webpack.config.js b/src/main/webapp/webpack.config.js index d7a4cde42..8d6c6fd93 100644 --- a/src/main/webapp/webpack.config.js +++ b/src/main/webapp/webpack.config.js @@ -7,16 +7,24 @@ var ExtractTextPlugin = require("extract-text-webpack-plugin"); console.log("\nThe arguments passed to webpack are:\n"); console.log(process.argv); +var defaultValues = { + '--contextPath': 'org.geppetto.frontend', + '--useSsl': false, + '--embedded': false, + '--embedderURL': '/' + }; + var getCLIParameter=function(param){ - for(var i=0;i Date: Wed, 14 Jun 2017 00:48:17 +0100 Subject: [PATCH 059/371] add onchange profile --- src/main/webapp/package.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/webapp/package.json b/src/main/webapp/package.json index c13755d2a..656186cdf 100644 --- a/src/main/webapp/package.json +++ b/src/main/webapp/package.json @@ -8,7 +8,8 @@ "build": "webpack -p --progress", "build-dev": "webpack --devtool eval", "build-dev-noTest": "webpack --devtool eval --noTest", - "start": "node --max_old_space_size=2048 node_modules/webpack-dev-server/bin/webpack-dev-server.js --progress --config webpack.config.dev.js --contextPath=org.geppetto.frontend --useSsl=false --embedded=false --embedderURL=/" + "build-dev-noTest:watch": "onchange 'js/**/*.*' 'extensions/**/*.*' 'style/**/*.*' -- npm run build-dev-noTest", + "start": "node --max_old_space_size=2048 node_modules/webpack-dev-server/bin/webpack-dev-server.js --progress --config webpack.config.dev.js" }, "devDependencies": { "anchorme": "^0.7.1", @@ -54,6 +55,7 @@ "less": "^2.7.2", "less-loader": "^2.2.3", "mathjs": "^3.5.3", + "onchange": "^3.2.1", "openseadragon": "^2.2.1", "pako": "^1.0.3", "pixi.js": "4.2.3", @@ -63,7 +65,7 @@ "react": "15.4.1", "react-dom": "15.4.1", "react-jsonschema-form": "^0.48.0", - "react-player" : "^0.18.0", + "react-player": "^0.18.0", "react-router": "^4.1.1", "react-router-dom": "^4.1.1", "react-simpletabs": "^0.7.0", From cdaee5e34cbdf02ee0961354dfab3196b3067214 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Wed, 14 Jun 2017 00:51:41 +0100 Subject: [PATCH 060/371] exclude ami.js --- src/main/webapp/webpack.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/webapp/webpack.config.js b/src/main/webapp/webpack.config.js index 8d6c6fd93..3edff8408 100644 --- a/src/main/webapp/webpack.config.js +++ b/src/main/webapp/webpack.config.js @@ -156,7 +156,7 @@ module.exports = { noParse: [/node_modules\/plotly.js\/dist\/plotly.js/, /js\/components\/interface\/dicomViewer\/ami.min.js/], loaders: [ { - test: /\.(js)$/, exclude: [/node_modules/, /build/, /\.bundle/], loader: ['babel-loader'], + test: /\.(js)$/, exclude: [/node_modules/, /build/, /\.bundle/, /ami.min.js/], loader: ['babel-loader'], query: { presets: ['react', 'es2015'] } From 93cfe82e5a61061d0fbf9b19c54ef1c42e2fa35a Mon Sep 17 00:00:00 2001 From: Matteo Cantarelli Date: Wed, 14 Jun 2017 12:34:42 +0100 Subject: [PATCH 061/371] Progress, not working yet --- .../geppettoJupyter/GeppettoJupyterGUISync.js | 4 ++- .../webapp/js/components/ComponentFactory.js | 35 ++++++++++++------- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/main/webapp/js/communication/geppettoJupyter/GeppettoJupyterGUISync.js b/src/main/webapp/js/communication/geppettoJupyter/GeppettoJupyterGUISync.js index b1cfd62af..0981790b8 100644 --- a/src/main/webapp/js/communication/geppettoJupyter/GeppettoJupyterGUISync.js +++ b/src/main/webapp/js/communication/geppettoJupyter/GeppettoJupyterGUISync.js @@ -103,7 +103,9 @@ define(function (require, exports, module) { }, display: function () { - this.set('component', GEPPETTO.ComponentFactory.renderComponent(this.getComponent())); + var comp = this.getComponent(); + this.set('component', GEPPETTO.ComponentFactory._addComponent(comp, "PANEL", { items: this.getChildren(), parentStyle: this.get('parentStyle') }, + document.getElementById('widgetContainer'), undefined, true)); // On close send a message to python to remove objects var that = this; diff --git a/src/main/webapp/js/components/ComponentFactory.js b/src/main/webapp/js/components/ComponentFactory.js index e3a90ab72..f4eb04d19 100644 --- a/src/main/webapp/js/components/ComponentFactory.js +++ b/src/main/webapp/js/components/ComponentFactory.js @@ -104,16 +104,14 @@ define(function (require) { return this.camelize(id); }, - _createComponent: function(componentType, properties, container, callback, isWidget){ - var that=this; - require(["./" + GEPPETTO.ComponentFactory.components[componentType]], function(loadedModule){ + _addComponent: function(componentToAdd, componentType, properties, container, callback, isWidget){ // Prepare properties if (properties === undefined){ properties = {}; } properties.componentType = componentType; if (!("id" in properties)){ - properties["id"] = that.getAvailableComponentId(componentType); + properties["id"] = this.getAvailableComponentId(componentType); } if (!("isStateless" in properties)){ properties["isStateless"] = false; @@ -121,18 +119,12 @@ define(function (require) { properties["parentContainer"] = container; // Create component/widget - var type = loadedModule; + var type = componentToAdd; if (isWidget){ - type = addWidget(loadedModule); + type = addWidget(componentToAdd); } var component = React.createFactory(type)(properties); - var renderedComponent = window[properties.id] = that.renderComponent(component, container, callback); - - // Register in component map - if (!(componentType in that.componentsMap)){ - that.componentsMap[componentType] = [] - } - that.componentsMap[componentType].push(renderedComponent); + var renderedComponent = window[properties.id] = this.renderComponent(component, container, callback); // Register widget/controller for events, etc if (isWidget){ @@ -144,6 +136,23 @@ define(function (require) { renderedComponent.container = container; } + return renderedComponent; + + }, + + _createComponent: function(componentType, properties, container, callback, isWidget){ + var that=this; + + require(["./" + GEPPETTO.ComponentFactory.components[componentType]], function(loadedModule){ + + var renderedComponent = that._addComponent(loadedModule, componentType, properties, container, callback, isWidget); + + // Register in component map + if (!(componentType in that.componentsMap)){ + that.componentsMap[componentType] = [] + } + that.componentsMap[componentType].push(renderedComponent); + return renderedComponent; }); }, From df1af37093afa72ea1c2c2be77698d0814aab273 Mon Sep 17 00:00:00 2001 From: Matteo Cantarelli Date: Wed, 14 Jun 2017 12:41:30 +0100 Subject: [PATCH 062/371] Progress --- .../geppettoJupyter/GeppettoJupyterGUISync.js | 17 ++++---- .../webapp/js/components/ComponentFactory.js | 40 +++++++++++++------ 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/src/main/webapp/js/communication/geppettoJupyter/GeppettoJupyterGUISync.js b/src/main/webapp/js/communication/geppettoJupyter/GeppettoJupyterGUISync.js index b1cfd62af..fd1039b36 100644 --- a/src/main/webapp/js/communication/geppettoJupyter/GeppettoJupyterGUISync.js +++ b/src/main/webapp/js/communication/geppettoJupyter/GeppettoJupyterGUISync.js @@ -13,7 +13,7 @@ define(function (require, exports, module) { var jupyter_widgets = require('jupyter-js-widgets'); var GEPPETTO = require('geppetto'); - var $ = require('jquery'); + var $ = require('jquery'); var _ = require('underscore'); var ComponentSync = jupyter_widgets.WidgetModel.extend({ @@ -41,15 +41,16 @@ define(function (require, exports, module) { model.send({ event: 'blur', data: value }); }, - getComponent: function (componentItem, parameters) { + getComponent: function (componentItem, parameters, container, isWidget) { parameters['id'] = this.get('widget_id'); parameters['name'] = this.get('widget_name'); parameters['sync_value'] = this.get('sync_value'); parameters['handleChange'] = this.handleChange.bind(null, this); parameters['handleBlur'] = this.handleBlur.bind(null, this); - var component = React.createFactory(componentItem)(parameters); - this.set('component', component); + this.set('component', GEPPETTO.ComponentFactory._addComponent(componentItem, this.componentType, parameters, + container, undefined, isWidget)); + return component; } }); @@ -61,6 +62,7 @@ define(function (require, exports, module) { position_y: null, width: null, height: null, + componentType: "PANEL", properties: {}, triggerClose: true }), @@ -80,7 +82,7 @@ define(function (require, exports, module) { getComponent: function () { var parameters = { items: this.getChildren(), parentStyle: this.get('parentStyle') }; - return PanelSync.__super__.getComponent.apply(this, [PanelComp, parameters]); + return PanelSync.__super__.getComponent.apply(this, [PanelComp, parameters, null, true]); }, forceRender: function () { @@ -213,14 +215,15 @@ define(function (require, exports, module) { var ButtonSync = ComponentSync.extend({ defaults: _.extend({}, ComponentSync.prototype.defaults, { - + componentType: "RAISEDBUTTON" }), + initialize: function (options) { ButtonSync.__super__.initialize.apply(this, arguments); }, getComponent: function () { var parameters = { handleClick: this.handleClick.bind(null, this) }; - return ButtonSync.__super__.getComponent.apply(this, [RaisedButtonComp, parameters]); + return ButtonSync.__super__.getComponent.apply(this, [RaisedButtonComp, parameters, null, false]); }, handleClick: function (model) { diff --git a/src/main/webapp/js/components/ComponentFactory.js b/src/main/webapp/js/components/ComponentFactory.js index e3a90ab72..252748267 100644 --- a/src/main/webapp/js/components/ComponentFactory.js +++ b/src/main/webapp/js/components/ComponentFactory.js @@ -104,35 +104,32 @@ define(function (require) { return this.camelize(id); }, - _createComponent: function(componentType, properties, container, callback, isWidget){ - var that=this; - require(["./" + GEPPETTO.ComponentFactory.components[componentType]], function(loadedModule){ + _addComponent: function(componentToAdd, componentType, properties, container, callback, isWidget){ // Prepare properties if (properties === undefined){ properties = {}; } properties.componentType = componentType; if (!("id" in properties)){ - properties["id"] = that.getAvailableComponentId(componentType); + properties["id"] = this.getAvailableComponentId(componentType); } if (!("isStateless" in properties)){ properties["isStateless"] = false; } + + if(container==null && isWidget){ + //FIXME Redundant, see addWidget + container=document.getElementById('widgetContainer'); + } properties["parentContainer"] = container; // Create component/widget - var type = loadedModule; + var type = componentToAdd; if (isWidget){ - type = addWidget(loadedModule); + type = addWidget(componentToAdd); } var component = React.createFactory(type)(properties); - var renderedComponent = window[properties.id] = that.renderComponent(component, container, callback); - - // Register in component map - if (!(componentType in that.componentsMap)){ - that.componentsMap[componentType] = [] - } - that.componentsMap[componentType].push(renderedComponent); + var renderedComponent = window[properties.id] = this.renderComponent(component, container, callback); // Register widget/controller for events, etc if (isWidget){ @@ -144,6 +141,23 @@ define(function (require) { renderedComponent.container = container; } + return renderedComponent; + + }, + + _createComponent: function(componentType, properties, container, callback, isWidget){ + var that=this; + + require(["./" + GEPPETTO.ComponentFactory.components[componentType]], function(loadedModule){ + + var renderedComponent = that._addComponent(loadedModule, componentType, properties, container, callback, isWidget); + + // Register in component map + if (!(componentType in that.componentsMap)){ + that.componentsMap[componentType] = [] + } + that.componentsMap[componentType].push(renderedComponent); + return renderedComponent; }); }, From 46662e45273e3b82f60a9048a635d05409b7728d Mon Sep 17 00:00:00 2001 From: Matteo Cantarelli Date: Wed, 14 Jun 2017 12:43:50 +0100 Subject: [PATCH 063/371] Progress --- .../webapp/js/components/ComponentFactory.js | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/webapp/js/components/ComponentFactory.js b/src/main/webapp/js/components/ComponentFactory.js index 252748267..9d7fec846 100644 --- a/src/main/webapp/js/components/ComponentFactory.js +++ b/src/main/webapp/js/components/ComponentFactory.js @@ -129,6 +129,18 @@ define(function (require) { type = addWidget(componentToAdd); } var component = React.createFactory(type)(properties); + + return component; + + }, + + _createComponent: function(componentType, properties, container, callback, isWidget){ + var that=this; + + require(["./" + GEPPETTO.ComponentFactory.components[componentType]], function(loadedModule){ + + var component = that._addComponent(loadedModule, componentType, properties, container, callback, isWidget); + var renderedComponent = window[properties.id] = this.renderComponent(component, container, callback); // Register widget/controller for events, etc @@ -140,18 +152,6 @@ define(function (require) { GEPPETTO.Console.updateTags(componentType, renderedComponent); renderedComponent.container = container; } - - return renderedComponent; - - }, - - _createComponent: function(componentType, properties, container, callback, isWidget){ - var that=this; - - require(["./" + GEPPETTO.ComponentFactory.components[componentType]], function(loadedModule){ - - var renderedComponent = that._addComponent(loadedModule, componentType, properties, container, callback, isWidget); - // Register in component map if (!(componentType in that.componentsMap)){ that.componentsMap[componentType] = [] From 42bd2138a175726392ccc7e0528f2199534cc11a Mon Sep 17 00:00:00 2001 From: Matteo Cantarelli Date: Wed, 14 Jun 2017 12:44:14 +0100 Subject: [PATCH 064/371] Progress --- src/main/webapp/js/components/ComponentFactory.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/webapp/js/components/ComponentFactory.js b/src/main/webapp/js/components/ComponentFactory.js index 9d7fec846..02a28668e 100644 --- a/src/main/webapp/js/components/ComponentFactory.js +++ b/src/main/webapp/js/components/ComponentFactory.js @@ -141,7 +141,7 @@ define(function (require) { var component = that._addComponent(loadedModule, componentType, properties, container, callback, isWidget); - var renderedComponent = window[properties.id] = this.renderComponent(component, container, callback); + var renderedComponent = window[properties.id] = that.renderComponent(component, container, callback); // Register widget/controller for events, etc if (isWidget){ From aa9998606b3338a468a0b84ece53d892b8cbb097 Mon Sep 17 00:00:00 2001 From: Matt Earnshaw Date: Wed, 14 Jun 2017 12:57:14 +0100 Subject: [PATCH 065/371] extend to hives, fix legend for forces --- .../widgets/connectivity/Connectivity.js | 37 ++++++++++--------- .../components/widgets/connectivity/forces.js | 3 +- .../components/widgets/connectivity/hives.js | 2 +- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/main/webapp/js/components/widgets/connectivity/Connectivity.js b/src/main/webapp/js/components/widgets/connectivity/Connectivity.js index 55b57464c..07722a72a 100644 --- a/src/main/webapp/js/components/widgets/connectivity/Connectivity.js +++ b/src/main/webapp/js/components/widgets/connectivity/Connectivity.js @@ -27,8 +27,8 @@ define(function (require) { return Widget.View.extend({ dataset: {}, - connectivityOptions: {}, nodeColormap: {}, + connectivityOptions: {}, defaultConnectivityOptions: { width: 660, height: 500, @@ -47,25 +47,11 @@ define(function (require) { return 1; } }, - initialize: function (options) { this.options = options; - this.nodeColormap = function(){ - var pops = GEPPETTO.ModelFactory.getAllInstancesOf( - GEPPETTO.ModelFactory.getAllTypesOfType(GEPPETTO.ModelFactory.geppettoModel.neuroml.network)[0])[0].getChildren(); - var domain = []; - var range = []; - for (var i=0; i Date: Wed, 14 Jun 2017 14:03:44 +0100 Subject: [PATCH 066/371] modifying jupyter gui and component factory to work with latest geppetto --- .../geppettoJupyter/GeppettoJupyterGUISync.js | 21 +- .../webapp/js/components/ComponentFactory.js | 180 +++++++++--------- .../js/components/controls/panel/Panel.js | 115 +++++------ 3 files changed, 166 insertions(+), 150 deletions(-) diff --git a/src/main/webapp/js/communication/geppettoJupyter/GeppettoJupyterGUISync.js b/src/main/webapp/js/communication/geppettoJupyter/GeppettoJupyterGUISync.js index c441efd16..e4555db64 100644 --- a/src/main/webapp/js/communication/geppettoJupyter/GeppettoJupyterGUISync.js +++ b/src/main/webapp/js/communication/geppettoJupyter/GeppettoJupyterGUISync.js @@ -41,15 +41,20 @@ define(function (require, exports, module) { model.send({ event: 'blur', data: value }); }, - getComponent: function (componentItem, parameters, container, isWidget) { + getParameters: function(parameters){ parameters['id'] = this.get('widget_id'); parameters['name'] = this.get('widget_name'); parameters['sync_value'] = this.get('sync_value'); parameters['handleChange'] = this.handleChange.bind(null, this); parameters['handleBlur'] = this.handleBlur.bind(null, this); + return parameters; + }, - this.set('component', GEPPETTO.ComponentFactory._addComponent(componentItem, this.componentType, parameters, - container, undefined, isWidget)); + getComponent: function (componentItem, parameters, container) { + + var component = GEPPETTO.ComponentFactory._addComponent(componentItem, this.componentType, this.getParameters(parameters), + container, undefined, (this.get("embedded") == false)); + this.set('component', component); return component; } @@ -82,7 +87,7 @@ define(function (require, exports, module) { getComponent: function () { var parameters = { items: this.getChildren(), parentStyle: this.get('parentStyle') }; - return PanelSync.__super__.getComponent.apply(this, [PanelComp, parameters, null, true]); + return PanelSync.__super__.getComponent.apply(this, [PanelComp, parameters, null]); }, forceRender: function () { @@ -106,8 +111,10 @@ define(function (require, exports, module) { display: function () { var comp = this.getComponent(); - this.set('component', GEPPETTO.ComponentFactory._addComponent(comp, "PANEL", { items: this.getChildren(), parentStyle: this.get('parentStyle') }, - document.getElementById('widgetContainer'), undefined, true)); + //this.set('component', GEPPETTO.ComponentFactory._addComponent(comp, "PANEL", { items: this.getChildren(), parentStyle: this.get('parentStyle') }, + // document.getElementById('widgetContainer'), undefined, true)); + GEPPETTO.ComponentFactory._renderComponent(comp, "PANEL", this.getParameters({ items: this.getChildren(), parentStyle: this.get('parentStyle') }), + document.getElementById('widgetContainer'), undefined, true); // On close send a message to python to remove objects var that = this; @@ -225,7 +232,7 @@ define(function (require, exports, module) { }, getComponent: function () { var parameters = { handleClick: this.handleClick.bind(null, this) }; - return ButtonSync.__super__.getComponent.apply(this, [RaisedButtonComp, parameters, null, false]); + return ButtonSync.__super__.getComponent.apply(this, [RaisedButtonComp, parameters, null]); }, handleClick: function (model) { diff --git a/src/main/webapp/js/components/ComponentFactory.js b/src/main/webapp/js/components/ComponentFactory.js index 02a28668e..e8c12d798 100644 --- a/src/main/webapp/js/components/ComponentFactory.js +++ b/src/main/webapp/js/components/ComponentFactory.js @@ -5,38 +5,38 @@ define(function (require) { var React = require('react'); var ReactDOM = require('react-dom'); - var spinner=require('./interface/loadingSpinner/LoadingSpinner.js'); + var spinner = require('./interface/loadingSpinner/LoadingSpinner.js'); var addWidget = require('./widgets/AWidget.js'); - + GEPPETTO.ComponentFactory = { //All the components potentially instantiable go here - components : { - 'FORM':'interface/form/Form', - 'PANEL':'controls/panel/Panel', - 'LOGO':'interface/logo/Logo', - 'LOADINGSPINNER':'interface/loadingSpinner/LoadingSpinner', - 'SAVECONTROL':'interface/save/SaveControl', - 'TOGGLEBUTTON' : 'controls/toggleButton/ToggleButton', - 'CONTROLPANEL':'interface/controlPanel/controlpanel', - 'SPOTLIGHT':'interface/spotlight/spotlight', - 'MENUBUTTON':'controls/menuButton/MenuButton', - 'FOREGROUND':'interface/foregroundControls/ForegroundControls', - 'EXPERIMENTSTABLE':'interface/experimentsTable/ExperimentsTable', - 'HOME':'interface/home/HomeControl', - 'SIMULATIONCONTROLS':'interface/simulationControls/ExperimentControls', + components: { + 'FORM': 'interface/form/Form', + 'PANEL': 'controls/panel/Panel', + 'LOGO': 'interface/logo/Logo', + 'LOADINGSPINNER': 'interface/loadingSpinner/LoadingSpinner', + 'SAVECONTROL': 'interface/save/SaveControl', + 'TOGGLEBUTTON': 'controls/toggleButton/ToggleButton', + 'CONTROLPANEL': 'interface/controlPanel/controlpanel', + 'SPOTLIGHT': 'interface/spotlight/spotlight', + 'MENUBUTTON': 'controls/menuButton/MenuButton', + 'FOREGROUND': 'interface/foregroundControls/ForegroundControls', + 'EXPERIMENTSTABLE': 'interface/experimentsTable/ExperimentsTable', + 'HOME': 'interface/home/HomeControl', + 'SIMULATIONCONTROLS': 'interface/simulationControls/ExperimentControls', 'CAMERACONTROLS': 'interface/cameraControls/CameraControls', - 'SHARE':'interface/share/Share', - 'INFOMODAL':'controls/modals/InfoModal', - 'MDMODAL':'controls/modals/MarkDownModal', - 'QUERY':'interface/query/query', - 'TUTORIAL':'interface/tutorial/Tutorial', + 'SHARE': 'interface/share/Share', + 'INFOMODAL': 'controls/modals/InfoModal', + 'MDMODAL': 'controls/modals/MarkDownModal', + 'QUERY': 'interface/query/query', + 'TUTORIAL': 'interface/tutorial/Tutorial', 'PYTHONCONSOLE': 'interface/pythonConsole/PythonConsole', 'CHECKBOX': 'controls/Checkbox', 'TEXTFIELD': 'controls/TextField', 'RAISEDBUTTON': 'controls/RaisedButton', - 'BUTTON':'controls/button/Button', + 'BUTTON': 'controls/button/Button', 'DICOMVIEWER': 'interface/dicomViewer/DicomViewer', 'GOOGLEVIEWER': 'interface/googleViewer/GoogleViewer', 'BIGIMAGEVIEWER': 'interface/bigImageViewer/BigImageViewer', @@ -50,20 +50,20 @@ define(function (require) { // componentsShortcut : { // "1": "POPUP" // }, - - loadSpinner:function(){ + + loadSpinner: function () { //We require this synchronously to properly show spinner when loading projects - this.renderComponent(React.createFactory(spinner)(),document.getElementById("load-spinner")); + this.renderComponent(React.createFactory(spinner)(), document.getElementById("load-spinner")); }, componentsMap: {}, - getComponents: function(){ + getComponents: function () { return this.componentsMap; }, camelize(str) { - return str.replace(/(?:^\w|[A-Z]|\b\w|\s+)/g, function(match, index) { + return str.replace(/(?:^\w|[A-Z]|\b\w|\s+)/g, function (match, index) { if (+match === 0) return ""; // or if (/\s+/.test(match)) for white spaces return index == 0 ? match.toUpperCase() : match.toLowerCase(); }); @@ -77,13 +77,13 @@ define(function (require) { * @param {Array} widgetsList * @returns {String} id - Available id for a widget */ - getAvailableComponentId (componentType) { + getAvailableComponentId(componentType) { var index = 0; var id = ""; var available; var components = []; - if (componentType in this.componentsMap){ + if (componentType in this.componentsMap) { components = this.componentsMap[componentType]; } @@ -91,7 +91,7 @@ define(function (require) { index++; id = componentType + index; available = true; - + for (var componentsIndex in components) { if (components[componentsIndex].props.id.toUpperCase() == id.toUpperCase()) { available = false; @@ -104,93 +104,99 @@ define(function (require) { return this.camelize(id); }, - _addComponent: function(componentToAdd, componentType, properties, container, callback, isWidget){ - // Prepare properties - if (properties === undefined){ - properties = {}; - } - properties.componentType = componentType; - if (!("id" in properties)){ - properties["id"] = this.getAvailableComponentId(componentType); - } - if (!("isStateless" in properties)){ - properties["isStateless"] = false; - } + _addComponent: function (componentToAdd, componentType, properties, container, callback, isWidget) { + // Prepare properties + if (properties === undefined) { + properties = {}; + } + properties.componentType = componentType; + if (!("id" in properties)) { + properties["id"] = this.getAvailableComponentId(componentType); + } + if (!("isStateless" in properties)) { + properties["isStateless"] = false; + } - if(container==null && isWidget){ - //FIXME Redundant, see addWidget - container=document.getElementById('widgetContainer'); - } - properties["parentContainer"] = container; - - // Create component/widget - var type = componentToAdd; - if (isWidget){ - type = addWidget(componentToAdd); - } - var component = React.createFactory(type)(properties); - - return component; + if (container == null && isWidget) { + //FIXME Redundant, see addWidget + container = document.getElementById('widgetContainer'); + } + properties["parentContainer"] = container; + + // Create component/widget + var type = componentToAdd; + if (isWidget) { + type = addWidget(componentToAdd); + } + var component = React.createFactory(type)(properties); + + return component; }, - _createComponent: function(componentType, properties, container, callback, isWidget){ - var that=this; + _createComponent: function (componentType, properties, container, callback, isWidget) { + var that = this; - require(["./" + GEPPETTO.ComponentFactory.components[componentType]], function(loadedModule){ + require(["./" + GEPPETTO.ComponentFactory.components[componentType]], function (loadedModule) { var component = that._addComponent(loadedModule, componentType, properties, container, callback, isWidget); - var renderedComponent = window[properties.id] = that.renderComponent(component, container, callback); + var renderedComponent = that._renderComponent(component, componentType, properties, container, callback, isWidget) + - // Register widget/controller for events, etc - if (isWidget){ - var widgetController = GEPPETTO.NewWidgetFactory.getController(componentType); - widgetController.registerWidget(renderedComponent) - } - else{ - GEPPETTO.Console.updateTags(componentType, renderedComponent); - renderedComponent.container = container; - } - // Register in component map - if (!(componentType in that.componentsMap)){ - that.componentsMap[componentType] = [] - } - that.componentsMap[componentType].push(renderedComponent); - return renderedComponent; }); }, - - addComponent: function(componentType, properties, container, callback){ + + _renderComponent: function (component, componentType, properties, container, callback, isWidget) { + var renderedComponent = window[properties.id] = this.renderComponent(component, container, callback); + + // Register widget/controller for events, etc + if (isWidget) { + var widgetController = GEPPETTO.NewWidgetFactory.getController(componentType); + widgetController.registerWidget(renderedComponent) + } + else { + GEPPETTO.Console.updateTags(componentType, renderedComponent); + renderedComponent.container = container; + } + // Register in component map + if (!(componentType in this.componentsMap)) { + this.componentsMap[componentType] = [] + } + this.componentsMap[componentType].push(renderedComponent); + return renderedComponent; + }, + + addComponent: function (componentType, properties, container, callback) { this._createComponent(componentType, properties, container, callback, false); }, - - addWidget: function(componentType, properties, callback){ - - if (componentType in this.components){ + + addWidget: function (componentType, properties, callback) { + + if (componentType in this.components) { this._createComponent(componentType, properties, document.getElementById('widgetContainer'), callback, true); } - else{ + else { var isStateless = false; - if (properties !== undefined && "isStateless" in properties){ + if (properties !== undefined && "isStateless" in properties) { isStateless = properties["isStateless"]; } var widget = GEPPETTO.WidgetFactory.addWidget(componentType, isStateless); // Register in component map - if (!(componentType in this.componentsMap)){ + if (!(componentType in this.componentsMap)) { this.componentsMap[componentType] = [] } this.componentsMap[componentType].push(widget); return widget; } - + }, - renderComponent: function(component, container, callback){ + renderComponent: function (component, container, callback) { return ReactDOM.render(component, container, callback); } - }; + }; }; }); diff --git a/src/main/webapp/js/components/controls/panel/Panel.js b/src/main/webapp/js/components/controls/panel/Panel.js index 09c2ffd4b..92463ee1d 100644 --- a/src/main/webapp/js/components/controls/panel/Panel.js +++ b/src/main/webapp/js/components/controls/panel/Panel.js @@ -2,60 +2,63 @@ define(function (require) { var React = require('react'); require("./Panel.less") - var defaultChildStyle = {'alignSelf': 'auto', 'flexGrow': 0, 'order': 0}; - - var panelComponent = React.createClass({ - - getInitialState: function() { - var defaultParentStyle = {'flexDirection':'column','justifyContent':'flex-start','alignItems':'flex-start','flexWrap':'nowrap','alignContent':'flex-start','display':'flex'}; - - return { - parentStyle: $.extend(defaultParentStyle, this.props.parentStyle), - items: this.props.items - }; - }, - - addChildren: function(items){ - this.setState({ items: this.state.items.concat(items) }); - }, - - setChildren: function(items){ - this.setState({ items: items }); - }, - - componentWillReceiveProps: function(nextProps) { - this.setState({ - items: nextProps.items - }); - }, - - setDirection: function(direction){ - var currentStyle = this.state.parentStyle; - currentStyle['flexDirection'] = direction; - this.setState({ parentStyle: currentStyle}); - }, - - componentDidMount : function(){ - var comp = $('#' + this.props.id); - if (comp.parent().hasClass('dialog')){ - comp.parent().height(comp.height() + 10); - comp.parent().parent().width(comp.width() + 70); - } - }, - - render: function(){ - var itemComponents = this.state.items.map(function (item) { - return (
{item}
); - }); - - return ( -
- {itemComponents} -
- ); - } - }); - - return panelComponent; - + var defaultChildStyle = { 'alignSelf': 'auto', 'flexGrow': 0, 'order': 0 }; + + var AbstractComponent = require('../../AComponent'); + + return class Panel extends AbstractComponent { + + constructor(props) { + super(props); + + var defaultParentStyle = { 'flexDirection': 'column', 'justifyContent': 'flex-start', 'alignItems': 'flex-start', 'flexWrap': 'nowrap', 'alignContent': 'flex-start', 'display': 'flex' }; + + this.state = { + parentStyle: $.extend(defaultParentStyle, this.props.parentStyle), + items: this.props.items + }; + + + } + + addChildren(items) { + this.setState({ items: this.state.items.concat(items) }); + } + + setChildren(items) { + this.setState({ items: items }); + } + + componentWillReceiveProps(nextProps) { + this.setState({ + items: nextProps.items + }); + } + + setDirection(direction) { + var currentStyle = this.state.parentStyle; + currentStyle['flexDirection'] = direction; + this.setState({ parentStyle: currentStyle }); + } + + componentDidMount() { + var comp = $('#' + this.props.id); + if (comp.parent().hasClass('dialog')) { + comp.parent().height(comp.height() + 10); + comp.parent().parent().width(comp.width() + 70); + } + } + + render() { + var itemComponents = this.state.items.map(function (item) { + return (
{item}
); + }); + + return ( +
+ {itemComponents} +
+ ); + } + }; }); \ No newline at end of file From 70d8af55126030a6755d7e2243744b7ae883a3ab Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Wed, 14 Jun 2017 14:17:54 +0100 Subject: [PATCH 067/371] more fixes --- .../communication/geppettoJupyter/GeppettoJupyterGUISync.js | 4 ++-- .../communication/geppettoJupyter/GeppettoJupyterModelSync.js | 3 ++- src/main/webapp/js/components/controls/panel/Panel.js | 3 --- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/webapp/js/communication/geppettoJupyter/GeppettoJupyterGUISync.js b/src/main/webapp/js/communication/geppettoJupyter/GeppettoJupyterGUISync.js index e4555db64..b3e09f7a0 100644 --- a/src/main/webapp/js/communication/geppettoJupyter/GeppettoJupyterGUISync.js +++ b/src/main/webapp/js/communication/geppettoJupyter/GeppettoJupyterGUISync.js @@ -113,8 +113,8 @@ define(function (require, exports, module) { var comp = this.getComponent(); //this.set('component', GEPPETTO.ComponentFactory._addComponent(comp, "PANEL", { items: this.getChildren(), parentStyle: this.get('parentStyle') }, // document.getElementById('widgetContainer'), undefined, true)); - GEPPETTO.ComponentFactory._renderComponent(comp, "PANEL", this.getParameters({ items: this.getChildren(), parentStyle: this.get('parentStyle') }), - document.getElementById('widgetContainer'), undefined, true); + this.set('component', GEPPETTO.ComponentFactory._renderComponent(comp, "PANEL", this.getParameters({ items: this.getChildren(), parentStyle: this.get('parentStyle') }), + document.getElementById('widgetContainer'), undefined, true)); // On close send a message to python to remove objects var that = this; diff --git a/src/main/webapp/js/communication/geppettoJupyter/GeppettoJupyterModelSync.js b/src/main/webapp/js/communication/geppettoJupyter/GeppettoJupyterModelSync.js index 2c3f1cc90..fc8b528ee 100644 --- a/src/main/webapp/js/communication/geppettoJupyter/GeppettoJupyterModelSync.js +++ b/src/main/webapp/js/communication/geppettoJupyter/GeppettoJupyterModelSync.js @@ -227,7 +227,8 @@ define(function (require, exports, module) { for (var i = 0; i < this.get('geometries').length; i++) { elements[this.get('geometries')[i].id] = ""; } - GEPPETTO.SceneController.splitGroups(window.Instances[0], elements); + //GEPPETTO.SceneController.splitGroups(window.Instances[0], elements); + Canvas1.engine.splitGroups(window.Instances[0], elements); } }, diff --git a/src/main/webapp/js/components/controls/panel/Panel.js b/src/main/webapp/js/components/controls/panel/Panel.js index 92463ee1d..fb3e5f897 100644 --- a/src/main/webapp/js/components/controls/panel/Panel.js +++ b/src/main/webapp/js/components/controls/panel/Panel.js @@ -12,13 +12,10 @@ define(function (require) { super(props); var defaultParentStyle = { 'flexDirection': 'column', 'justifyContent': 'flex-start', 'alignItems': 'flex-start', 'flexWrap': 'nowrap', 'alignContent': 'flex-start', 'display': 'flex' }; - this.state = { parentStyle: $.extend(defaultParentStyle, this.props.parentStyle), items: this.props.items }; - - } addChildren(items) { From 6168bc1198d10baafc3c321377fd6b2d2531da6a Mon Sep 17 00:00:00 2001 From: jrmartin Date: Wed, 14 Jun 2017 20:37:38 -0700 Subject: [PATCH 068/371] mmore caspers tests --- .../widgets/connectivity/Connectivity.js | 4 +- .../js/pages/tests/casperjs/CoreTests.js | 103 +++++++++++++++++- .../pages/tests/casperjs/CoreTestsUtility.js | 2 +- .../webapp/js/pages/tests/casperjs/UITests.js | 75 ++++++++++++- 4 files changed, 175 insertions(+), 9 deletions(-) diff --git a/src/main/webapp/js/components/widgets/connectivity/Connectivity.js b/src/main/webapp/js/components/widgets/connectivity/Connectivity.js index a9e9a6b43..1725ba109 100644 --- a/src/main/webapp/js/components/widgets/connectivity/Connectivity.js +++ b/src/main/webapp/js/components/widgets/connectivity/Connectivity.js @@ -429,7 +429,9 @@ define(function (require) { // add data baseView.dataType = 'object'; - baseView.data = this.dataset["root"].getPath(); + if(this.dataset["root"]!=undefined){ + baseView.data = this.dataset["root"].getPath(); + } return baseView; }, diff --git a/src/main/webapp/js/pages/tests/casperjs/CoreTests.js b/src/main/webapp/js/pages/tests/casperjs/CoreTests.js index 9ef313071..f64e7e833 100644 --- a/src/main/webapp/js/pages/tests/casperjs/CoreTests.js +++ b/src/main/webapp/js/pages/tests/casperjs/CoreTests.js @@ -165,14 +165,25 @@ function hhcellTest(test,name){ GEPPETTO.SceneController.addColorFunction(GEPPETTO.ModelFactory.instances.getInstance(GEPPETTO.ModelFactory.getAllPotentialInstancesEndingWith('.v'),false), window.voltage_color); Project.getActiveExperiment().play({step:1}); Plot1.setPosition(0,300); + G.addWidget(1).setMessage("Hhcell popup"); + G.addWidget(1).setData(hhcell); + var customHandler = function(node, path, widget) {}; + Popup2.addCustomNodeHandler(customHandler,'click'); }); + casper.mouseEvent('click', 'button#tutorialBtn', "attempting to open tutorial"); + var mesh = casper.evaluate(function(){ var mesh = Canvas2.engine.getRealMeshesForInstancePath("hhcell.hhpop[0]").length; return mesh; }); test.assertEquals(mesh, 1, "Canvas widget has hhcell"); + casper.evaluate(function(){ + $(".nextBtn").click(); + $(".nextBtn").click(); + }); + casper.echo("-------Testing Camera Controls--------"); testCameraControlsWithCanvasWidget(test, [0,0,30.90193733102435]); casper.wait(1000, function(){ @@ -182,7 +193,48 @@ function hhcellTest(test,name){ test3DMeshColorNotEquals(test,defaultColor,"hhcell.hhpop[0]"); casper.echo("Done Playing, now exiting"); }) - }) + }); + + casper.then(function(){launchTest(test,"Hhcell",30000);}); + + casper.then(function(){ + test.assertVisible('div#Canvas2', "Canvas2 is correctly open on reload."); + test.assertVisible('div#Plot1', "Plot1 is correctly open on reload"); + test.assertVisible('div#Popup1', "Popup1 is correctly open on reload"); + test.assertVisible('div#Popup2', "Popup2 is correctly open on reload"); + test.assertVisible('div#Tutorial1', "Tutorial1 is correctly open on reload"); + + var tutorialStep = casper.evaluate(function() { + return Tutorial1.state.currentStep; + }); + + test.assertEquals(tutorialStep, 2, "Tutorial1 step restored correctly"); + + var popUpMessage = casper.evaluate(function() { + return $("#Popup1").html(); + }); + + test.assertEquals(popUpMessage, "Hhcell popup", "Popup1 message restored correctly"); + + var popUpCustomHandler = casper.evaluate(function() { + return Popup2.customHandlers; + }); + + test.assertEquals(popUpCustomHandler.length, 1, "Popup2 custom handlers restored correctly"); + test.assertEquals(popUpCustomHandler[0]["event"], "click", "Popup2 custom handlers event restored correctly"); + + var meshInCanvas2Exists = casper.evaluate(function() { + var mesh = Canvas1.engine.meshes["hhcell.hhpop[0]"]; + + if(mesh!=null && mesh!=undefined){ + return true; + } + + return false; + }); + + test.assertEquals(meshInCanvas2Exists, true, "Canvas2 hhcell set correctly"); + }); }; /** @@ -191,7 +243,7 @@ function hhcellTest(test,name){ function acnetTest(test){ casper.echo("------------STARTING ACNET TEST--------------"); casper.then(function(){ - removeAllDialogs(); + removeAllPlots(); }); casper.then(function(){ casper.echo("-------Testing Camera Controls--------"); @@ -287,6 +339,51 @@ function acnetTest(test){ test3DMeshOpacity(test,1, "acnet2.baskets_12[4]"); test3DMeshOpacity(test,1, "acnet2.baskets_12[1]"); + casper.evaluate(function(){ + acnet2.pyramidals_48[0].setGeometryType("cylinders") + }); + + //test mesh set geometry + var meshType = casper.evaluate(function(){ + return Canvas1.engine.getRealMeshesForInstancePath("acnet2.pyramidals_48[0]")[0].type; + }); + test.assertEquals(meshType, "Mesh", "Correctly set mesh to cylinders"); + + var meshTotal = casper.evaluate(function(){ + return Object.keys(Canvas1.engine.meshes).length; + }); + test.assertEquals(meshTotal, 60, "Correctly amount of meshes after applying cylinders"); + + casper.evaluate(function(){ + acnet2.pyramidals_48[0].setGeometryType("lines") + }); + + //test mesh set geometry + meshType = casper.evaluate(function(){ + return Canvas1.engine.getRealMeshesForInstancePath("acnet2.pyramidals_48[0]")[0].type; + }); + test.assertEquals(meshType, "LineSegments", "Correctly set mesh to lines"); + + var meshTotal = casper.evaluate(function(){ + return Object.keys(Canvas1.engine.meshes).length; + }); + test.assertEquals(meshTotal, 60, "Correctly amount of meshes after applying cylinders"); + + casper.evaluate(function(){ + acnet2.pyramidals_48[0].setGeometryType("cylinders") + }); + + //test mesh set geometry + var meshType = casper.evaluate(function(){ + return Canvas1.engine.getRealMeshesForInstancePath("acnet2.pyramidals_48[0]")[0].type; + }); + test.assertEquals(meshType, "Mesh", "Correctly set mesh to cylinders"); + + var meshTotal = casper.evaluate(function(){ + return Object.keys(Canvas1.engine.meshes).length; + }); + test.assertEquals(meshTotal, 60, "Correctly amount of meshes after applying cylinders"); + //test color function casper.wait(2000, function(){ test3DMeshColorNotEquals(test,defaultColor,"acnet2.baskets_12[2].soma_0"); @@ -312,7 +409,7 @@ function c302Test(test){ }); casper.then(function(){ - removeAllDialogs(); + removeAllPlots(); }); casper.then(function(){ diff --git a/src/main/webapp/js/pages/tests/casperjs/CoreTestsUtility.js b/src/main/webapp/js/pages/tests/casperjs/CoreTestsUtility.js index 61abd3888..0f73382a1 100644 --- a/src/main/webapp/js/pages/tests/casperjs/CoreTestsUtility.js +++ b/src/main/webapp/js/pages/tests/casperjs/CoreTestsUtility.js @@ -60,7 +60,7 @@ function removeAllPlots(){ function removeAllDialogs(){ casper.then(function(){ casper.evaluate(function() { - $("div.ui-widget").remove(); + $("div.dialog").remove(); }); }); } diff --git a/src/main/webapp/js/pages/tests/casperjs/UITests.js b/src/main/webapp/js/pages/tests/casperjs/UITests.js index 893906118..bdbe4cc60 100644 --- a/src/main/webapp/js/pages/tests/casperjs/UITests.js +++ b/src/main/webapp/js/pages/tests/casperjs/UITests.js @@ -5,7 +5,7 @@ var PROJECT_URL_SUFFIX_2 = "?load_project_from_url=https://raw.githubusercontent var PROJECT_URL_SUFFIX_3 = "?load_project_from_url=https://raw.githubusercontent.com/openworm/org.geppetto.samples/development/UsedInUnitTests/balanced/project.json"; var projectID; -casper.test.begin('Geppetto basic tests', 109, function suite(test) { +casper.test.begin('Geppetto basic tests', 127, function suite(test) { casper.options.viewportSize = { width: 1340, height: 768 @@ -64,6 +64,10 @@ casper.test.begin('Geppetto basic tests', 109, function suite(test) { this.echo("Project id to delete : "+projectID); }); + casper.then(function () { + reloadProjectTest(test, TARGET_URL + port+"/org.geppetto.frontend/geppetto?load_project_from_id="+projectID); + }); + casper.then(function () { deleteProject(test, TARGET_URL + port+"/org.geppetto.frontend",projectID); }); @@ -80,6 +84,10 @@ casper.test.begin('Geppetto basic tests', 109, function suite(test) { this.echo("Project id to delete : "+projectID); }); + casper.then(function () { + reloadProjectTest(test, TARGET_URL + port+"/org.geppetto.frontend/geppetto?load_project_from_id="+projectID,2); + }); + casper.then(function () { deleteProject(test, TARGET_URL + port+"/org.geppetto.frontend",projectID); }); @@ -96,6 +104,10 @@ casper.test.begin('Geppetto basic tests', 109, function suite(test) { this.echo("Project id to delete : "+projectID); }); + casper.then(function () { + reloadProjectTest(test, TARGET_URL + port+"/org.geppetto.frontend/geppetto?load_project_from_id="+projectID,1); + }); + casper.then(function () { deleteProject(test, TARGET_URL + port+"/org.geppetto.frontend",projectID); }); @@ -103,6 +115,19 @@ casper.test.begin('Geppetto basic tests', 109, function suite(test) { //TODO: log back in as other users. Check more things //TODO: exercise the run loop, check the changing experiment status, try to make experiment fail + casper.thenOpen(TARGET_URL + port+"/org.geppetto.frontend/logout", function () { + }); + + casper.thenOpen(TARGET_URL + port+"/org.geppetto.frontend/login?username=admin&password=admin", function () { + }); + + casper.thenOpen(TARGET_URL+ port+"/org.geppetto.frontend/admin", function () { + this.waitForSelector('div[class="griddle"]', function () { + this.echo("I've waited for the admin panel to load."); + }, null, 30000); + }); + + casper.run(function () { test.done(); }); @@ -110,7 +135,7 @@ casper.test.begin('Geppetto basic tests', 109, function suite(test) { function deleteProject(test, url,id){ casper.thenOpen(url, function () { - this.echo("Loading an external model that is not persisted at " + url); + this.echo("Loading an external model that is persisted at " + url); casper.then(function () { @@ -145,6 +170,38 @@ function deleteProject(test, url,id){ }); } + +function reloadProjectTest(test, url, customHandlers,widgetCanvasObject){ + casper.thenOpen(url, function () { + this.echo("Reloading persisted project at " + url); + + casper.waitWhileVisible('div[id="loading-spinner"]', function () { + this.echo("I've waited for "+url+" project to load."); + + casper.then(function () { + casper.wait(2000, function () {}); + test.assertVisible('div#Canvas2', "Canvas2 is correctly open on reload."); + test.assertVisible('div#Popup1', "Popup1 is correctly open on reload"); + test.assertVisible('div#Tutorial1', "Tutorial1 is correctly open on reload"); + test.assertVisible('div#Connectivity1', "Connectivity1 is correctly open on reload"); + + var tutorialStep = casper.evaluate(function() { + return Tutorial1.state.currentStep; + }); + + test.assertEquals(tutorialStep, 2, "Tutorial1 step restored correctly"); + + var popUpCustomHandler = casper.evaluate(function() { + return Popup1.customHandlers; + }); + + test.assertEquals(popUpCustomHandler.length, customHandlers, "Popup1 custom handlers restored correctly"); + test.assertEquals(popUpCustomHandler[0]["event"], "click", "Popup2 custom handlers event restored correctly"); + }); + },null,300000); + }); +} + function testProject(test, url, expect_error, persisted, spotlight_record_variable, spotlight_set_parameter, testConsole) { casper.thenOpen(url, function () { @@ -171,7 +228,7 @@ function testProject(test, url, expect_error, persisted, spotlight_record_variab //do checks on the state of the project if it is not persisted if (persisted == false) { - casper.then(function () { + casper.then(function () { // make sure experiment panel is open this.evaluate(function() { $('a[href=experiments]').click(); @@ -182,6 +239,16 @@ function testProject(test, url, expect_error, persisted, spotlight_record_variab }); casper.then(function () { + casper.mouseEvent('click', 'button#tutorialBtn', "attempting to open tutorial"); + casper.evaluate(function(widgetCanvasObject) { + G.addWidget(6); + GEPPETTO.ComponentFactory.addWidget('CANVAS', {name: '3D Canvas',}, function () {this.setName('Widget Canvas');this.setPosition();}); + G.addWidget(1).setMessage("Hhcell popup"); + var customHandler = function(node, path, widget) {}; + Popup1.addCustomNodeHandler(customHandler,'click'); + $(".nextBtn").click(); + $(".nextBtn").click(); + }); this.echo("Checking content of experiment row"); // test or wait for control panel stuff to be there if(this.exists('a[href="#experiments"]')){ @@ -206,7 +273,7 @@ function testProject(test, url, expect_error, persisted, spotlight_record_variab }); casper.then(function () { - + this.waitForSelector('button.btn.SaveButton', function () { test.assertVisible('button.btn.SaveButton', "Persist button is present"); }); From 291b9ed0a98a772ee38becfafeb9dfe95c662c62 Mon Sep 17 00:00:00 2001 From: jrmartin Date: Wed, 14 Jun 2017 21:08:00 -0700 Subject: [PATCH 069/371] checks content of canvas --- .../webapp/js/pages/tests/casperjs/UITests.js | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/main/webapp/js/pages/tests/casperjs/UITests.js b/src/main/webapp/js/pages/tests/casperjs/UITests.js index bdbe4cc60..9075b820d 100644 --- a/src/main/webapp/js/pages/tests/casperjs/UITests.js +++ b/src/main/webapp/js/pages/tests/casperjs/UITests.js @@ -5,7 +5,7 @@ var PROJECT_URL_SUFFIX_2 = "?load_project_from_url=https://raw.githubusercontent var PROJECT_URL_SUFFIX_3 = "?load_project_from_url=https://raw.githubusercontent.com/openworm/org.geppetto.samples/development/UsedInUnitTests/balanced/project.json"; var projectID; -casper.test.begin('Geppetto basic tests', 127, function suite(test) { +casper.test.begin('Geppetto basic tests', 133, function suite(test) { casper.options.viewportSize = { width: 1340, height: 768 @@ -54,7 +54,7 @@ casper.test.begin('Geppetto basic tests', 127, function suite(test) { casper.then(function () { testProject(test, TARGET_URL + port+"/org.geppetto.frontend/geppetto" + PROJECT_URL_SUFFIX, true, - false, 'hhcell.hhpop[0].v', 'Model.neuroml.pulseGen1.delay', true); + false, 'hhcell.hhpop[0].v', 'Model.neuroml.pulseGen1.delay', true,"hhcell"); }); casper.then(function () { @@ -65,7 +65,7 @@ casper.test.begin('Geppetto basic tests', 127, function suite(test) { }); casper.then(function () { - reloadProjectTest(test, TARGET_URL + port+"/org.geppetto.frontend/geppetto?load_project_from_id="+projectID); + reloadProjectTest(test, TARGET_URL + port+"/org.geppetto.frontend/geppetto?load_project_from_id="+projectID,1); }); casper.then(function () { @@ -74,7 +74,7 @@ casper.test.begin('Geppetto basic tests', 127, function suite(test) { casper.then(function () { testProject(test, TARGET_URL + port+"/org.geppetto.frontend/geppetto" + PROJECT_URL_SUFFIX_2, false, - false, 'c302_A_Pharyngeal.M1[0].v', 'Model.neuroml.generic_neuron_iaf_cell.C', false); + false, 'c302_A_Pharyngeal.M1[0].v', 'Model.neuroml.generic_neuron_iaf_cell.C', false,"c302_A_Pharyngeal"); }); casper.then(function () { @@ -94,7 +94,7 @@ casper.test.begin('Geppetto basic tests', 127, function suite(test) { casper.then(function () { testProject(test, TARGET_URL +port+"/org.geppetto.frontend/geppetto" + PROJECT_URL_SUFFIX_3, false, - false, '', '', false); + false, '', '', false,"Balanced_240cells_36926conns"); }); casper.then(function () { @@ -197,12 +197,20 @@ function reloadProjectTest(test, url, customHandlers,widgetCanvasObject){ test.assertEquals(popUpCustomHandler.length, customHandlers, "Popup1 custom handlers restored correctly"); test.assertEquals(popUpCustomHandler[0]["event"], "click", "Popup2 custom handlers event restored correctly"); + + var meshInCanvas2Exists = casper.evaluate(function() { + var mesh = $.isEmptyObject(Canvas1.engine.meshes); + + return mesh; + }); + + test.assertEquals(meshInCanvas2Exists, false, "Canvas2 has mesh set correctly"); }); },null,300000); }); } -function testProject(test, url, expect_error, persisted, spotlight_record_variable, spotlight_set_parameter, testConsole) { +function testProject(test, url, expect_error, persisted, spotlight_record_variable, spotlight_set_parameter, testConsole,widgetCanvasObject) { casper.thenOpen(url, function () { this.echo("Loading an external model that is not persisted at " + url); @@ -241,14 +249,22 @@ function testProject(test, url, expect_error, persisted, spotlight_record_variab casper.then(function () { casper.mouseEvent('click', 'button#tutorialBtn', "attempting to open tutorial"); casper.evaluate(function(widgetCanvasObject) { + var canvasObject = null; + if(widgetCanvasObject=="hhcell"){ + canvasObject = hhcell; + }else if(widgetCanvasObject=="c302_A_Pharyngeal"){ + canvasObject = c302_A_Pharyngeal; + }else if(widgetCanvasObject=="Balanced_240cells_36926conns"){ + canvasObject = Balanced_240cells_36926conns; + } G.addWidget(6); - GEPPETTO.ComponentFactory.addWidget('CANVAS', {name: '3D Canvas',}, function () {this.setName('Widget Canvas');this.setPosition();}); + GEPPETTO.ComponentFactory.addWidget('CANVAS', {name: '3D Canvas',}, function () {this.setName('Widget Canvas');this.setPosition();this.display([canvasObject])}); G.addWidget(1).setMessage("Hhcell popup"); var customHandler = function(node, path, widget) {}; Popup1.addCustomNodeHandler(customHandler,'click'); $(".nextBtn").click(); $(".nextBtn").click(); - }); + },widgetCanvasObject); this.echo("Checking content of experiment row"); // test or wait for control panel stuff to be there if(this.exists('a[href="#experiments"]')){ From 7c5fffe2202dd6f2dd7d0b03281c9683dd9ae0f2 Mon Sep 17 00:00:00 2001 From: Matt Earnshaw Date: Thu, 15 Jun 2017 12:09:26 +0100 Subject: [PATCH 070/371] start removing neuroml specific code --- .../widgets/connectivity/Connectivity.js | 28 ++++++------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/src/main/webapp/js/components/widgets/connectivity/Connectivity.js b/src/main/webapp/js/components/widgets/connectivity/Connectivity.js index 07722a72a..095604b58 100644 --- a/src/main/webapp/js/components/widgets/connectivity/Connectivity.js +++ b/src/main/webapp/js/components/widgets/connectivity/Connectivity.js @@ -51,8 +51,6 @@ define(function (require) { initialize: function (options) { this.options = options; - this.nodeColormap = this.getNodeColormap(); - Widget.View.prototype.initialize.call(this, options); this.setOptions(this.defaultConnectivityOptions); @@ -80,23 +78,6 @@ define(function (require) { }); }, - getNodeColormap: function () { - var cells = GEPPETTO.ModelFactory.getAllInstancesOf( - GEPPETTO.ModelFactory.getAllTypesOfType(GEPPETTO.ModelFactory.geppettoModel.neuroml.network)[0])[0].getChildren(); - var domain = []; - var range = []; - for (var i=0; i Date: Thu, 15 Jun 2017 13:42:48 +0100 Subject: [PATCH 071/371] add nodecolormap to state --- .../webapp/js/components/widgets/connectivity/Connectivity.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/webapp/js/components/widgets/connectivity/Connectivity.js b/src/main/webapp/js/components/widgets/connectivity/Connectivity.js index 095604b58..7aa5be7af 100644 --- a/src/main/webapp/js/components/widgets/connectivity/Connectivity.js +++ b/src/main/webapp/js/components/widgets/connectivity/Connectivity.js @@ -437,6 +437,8 @@ define(function (require) { // add data baseView.dataType = 'object'; baseView.data = this.dataset["root"].getPath(); + baseView.nodeColormap = {domain: this.nodeColormap.domain(), + range: this.nodeColormap.range()}; return baseView; }, @@ -459,7 +461,7 @@ define(function (require) { var that = this; // resolve connections and pass the line below as a callback Model.neuroml.resolveAllImportTypes(function(){ - that.setData(obj, deserializedOptions); + that.setData(obj, deserializedOptions, view.nodeColormap); }); } From b28f469b980e5cd283cbb3be17786e474909a6d9 Mon Sep 17 00:00:00 2001 From: Matt Earnshaw Date: Thu, 15 Jun 2017 14:30:10 +0100 Subject: [PATCH 072/371] uniform handling of default color formats --- .../webapp/js/common/GEPPETTO.Resources.js | 42 +++++++++---------- .../interface/3dCanvas/ThreeDEngine.js | 10 ++--- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/main/webapp/js/common/GEPPETTO.Resources.js b/src/main/webapp/js/common/GEPPETTO.Resources.js index 9b9d2adee..515f83c07 100644 --- a/src/main/webapp/js/common/GEPPETTO.Resources.js +++ b/src/main/webapp/js/common/GEPPETTO.Resources.js @@ -38,27 +38,27 @@ define(function (require) { }, COLORS: { - DEFAULT: "0X199e8", - SELECTED: "0Xffcc00", - INPUT_TO_SELECTED: "0Xffdfc6", - OUTPUT_TO_SELECTED: "0Xff5a02", - HIGHLIGHTED: "0Xff1a02", - INPUT_AND_OUTPUT: "0X649615", - SPLIT: "0XCFCFA6", - ENTITY_NODE: "0xcc0000", - ASPECT_NODE: "0xcc6600", - ASPECT_SUBTREE_NODE: "0xcccc00", - COMPOSITE_NODE: "0x66cc00", - CONNECTION_NODE: "0x00cc00", - DYNAMICS_SPECIFICATION_NODE: "0x00cc66", - FUNCTION_NODE: "0x00cccc", - PARAMETER_NODE: "0x0066cc", - PARAMETER_SPECIFICATION_NODE: "0x0000cc", - TEXT_METADATA_NODE: "0x6600cc", - URL_METADATA_NODE: "0xcc00cc", - VARIABLE_NODE: "0xcc0066", - VISUAL_OBJECT_REFERENCE_NODE: "0x606060", - VISUAL_GROUP_ELEMENT_NODE: "0xffffff", + DEFAULT: "#0199e8", + SELECTED: "#ffcc00", + INPUT_TO_SELECTED: "#ffdfc6", + OUTPUT_TO_SELECTED: "#ff5a02", + HIGHLIGHTED: "#ff1a02", + INPUT_AND_OUTPUT: "#649615", + SPLIT: "#CFCFA6", + ENTITY_NODE: "#cc0000", + ASPECT_NODE: "#cc6600", + ASPECT_SUBTREE_NODE: "#cccc00", + COMPOSITE_NODE: "#66cc00", + CONNECTION_NODE: "#00cc00", + DYNAMICS_SPECIFICATION_NODE: "#00cc66", + FUNCTION_NODE: "#00cccc", + PARAMETER_NODE: "#0066cc", + PARAMETER_SPECIFICATION_NODE: "#0000cc", + TEXT_METADATA_NODE: "#6600cc", + URL_METADATA_NODE: "#cc00cc", + VARIABLE_NODE: "#cc0066", + VISUAL_OBJECT_REFERENCE_NODE: "#606060", + VISUAL_GROUP_ELEMENT_NODE: "#ffffff", }, /** diff --git a/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js b/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js index a2aec801d..574446801 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js +++ b/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js @@ -899,7 +899,7 @@ define(['jquery'], function () { scene.traverse(function (child) { if (child instanceof THREE.Mesh) { - child.material.color.setHex(GEPPETTO.Resources.COLORS.DEFAULT); + this.setThreeColor(child.material.color, GEPPETTO.Resources.COLORS.DEFAULT); child.material.wireframe = this.wireframe; child.material.defaultColor = GEPPETTO.Resources.COLORS.DEFAULT; child.material.defaultOpacity = GEPPETTO.Resources.OPACITY.DEFAULT; @@ -1155,7 +1155,7 @@ define(['jquery'], function () { color = GEPPETTO.Resources.COLORS.DEFAULT; } var material = new THREE.LineBasicMaterial(options); - material.color.setHex(color); + this.setThreeColor(material.color, color) material.defaultColor = color; material.defaultOpacity = GEPPETTO.Resources.OPACITY.DEFAULT; return material; @@ -1177,7 +1177,7 @@ define(['jquery'], function () { shading: THREE.SmoothShading }); - material.color.setHex(color); + this.setThreeColor(material.color, color); material.defaultColor = color; material.defaultOpacity = GEPPETTO.Resources.OPACITY.DEFAULT; return material; @@ -1197,7 +1197,7 @@ define(['jquery'], function () { depthTest: false, transparent: true }); - pMaterial.color.setHex(GEPPETTO.Resources.COLORS.DEFAULT); + this.setThreeColor(pMaterial.color, GEPPETTO.Resources.COLORS.DEFAULT); pMaterial.defaultColor = GEPPETTO.Resources.COLORS.DEFAULT; pMaterial.opacity = GEPPETTO.Resources.OPACITY.DEFAULT; pMaterial.defaultOpacity = GEPPETTO.Resources.OPACITY.DEFAULT; @@ -2263,7 +2263,7 @@ define(['jquery'], function () { } var material = new THREE.LineDashedMaterial({dashSize: 3, gapSize: 1}); - material.color.setHex(colour); + this.setThreeColor(material.color, colour); var line = new THREE.LineSegments(geometry, material); line.updateMatrixWorld(true); From 9e46baf4b2882379b3dca75b9e1291bb7a3d27c3 Mon Sep 17 00:00:00 2001 From: Matt Earnshaw Date: Thu, 15 Jun 2017 15:58:41 +0100 Subject: [PATCH 073/371] factor out more neuroml explicit references --- .../components/widgets/connectivity/Connectivity.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/main/webapp/js/components/widgets/connectivity/Connectivity.js b/src/main/webapp/js/components/widgets/connectivity/Connectivity.js index 7aa5be7af..e18312cc9 100644 --- a/src/main/webapp/js/components/widgets/connectivity/Connectivity.js +++ b/src/main/webapp/js/components/widgets/connectivity/Connectivity.js @@ -117,10 +117,9 @@ define(function (require) { }, createDataFromConnections: function () { - //TODO: remove Hardcoded NeuroML stuff - if(Model.neuroml.connection){ + if(this.options.library.connection){ - var connectionVariables = Model.neuroml.connection.getVariableReferences(); + var connectionVariables = this.options.library.connection.getVariableReferences(); if(connectionVariables.length>0) { if (this.dataset["root"].getMetaType() == GEPPETTO.Resources.INSTANCE_NODE) { @@ -385,12 +384,11 @@ define(function (require) { var modalContent=$('') .append(this.createLayoutSelector()[0].outerHTML).modal(); function handleFirstClick(event) { - //TODO: remove Hardcoded NeuroML stuff - var netTypes = GEPPETTO.ModelFactory.getAllTypesOfType(GEPPETTO.ModelFactory.geppettoModel.neuroml.network); - var netInstances = _.flatten(_.map(netTypes, function(x){return GEPPETTO.ModelFactory.getAllInstancesOf(x)})); + var netTypes = GEPPETTO.ModelFactory.getAllTypesOfType(that.options.library.network); + var netInstances = _.flatten(_.map(netTypes, function(x){return GEPPETTO.ModelFactory.getAllInstancesOf(x)})) function synapseFromConnection(conn) { - var synapses=GEPPETTO.ModelFactory.getAllVariablesOfType(conn.getParent(),GEPPETTO.ModelFactory.geppettoModel.neuroml.synapse); + var synapses=GEPPETTO.ModelFactory.getAllVariablesOfType(conn.getParent(), that.options.library.synapse); if(synapses.length>0){ return synapses[0].getId(); } From c322ebdd48604c4afa9e76afbecf6f81d4b64d95 Mon Sep 17 00:00:00 2001 From: Matt Earnshaw Date: Thu, 15 Jun 2017 15:59:48 +0100 Subject: [PATCH 074/371] no overflow for dashboard (prevent global scrollbar, cf. issue #618) --- src/main/webapp/js/pages/dashboard/css/application.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/webapp/js/pages/dashboard/css/application.css b/src/main/webapp/js/pages/dashboard/css/application.css index d4863d0fb..b8d43ca6b 100644 --- a/src/main/webapp/js/pages/dashboard/css/application.css +++ b/src/main/webapp/js/pages/dashboard/css/application.css @@ -8,6 +8,7 @@ body { font-weight: 200; -webkit-font-smoothing: antialiased; background-color: #f5f5f5; + overflow: hidden; } .dashboardAlert{ From 276c332de8f7cd965e57900aa64237ddf088f14d Mon Sep 17 00:00:00 2001 From: Matt Earnshaw Date: Thu, 15 Jun 2017 16:49:55 +0100 Subject: [PATCH 075/371] use d3 colorscheme if no custom population colors --- .../components/widgets/connectivity/Connectivity.js | 11 ++++++++--- .../js/components/widgets/connectivity/chords.js | 5 +++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/main/webapp/js/components/widgets/connectivity/Connectivity.js b/src/main/webapp/js/components/widgets/connectivity/Connectivity.js index e18312cc9..f3358766c 100644 --- a/src/main/webapp/js/components/widgets/connectivity/Connectivity.js +++ b/src/main/webapp/js/components/widgets/connectivity/Connectivity.js @@ -92,9 +92,14 @@ define(function (require) { }, setNodeColormap: function(nodeColormap) { - if (typeof nodeColormap != 'undefined') - this.nodeColormap = d3.scaleOrdinal(nodeColormap.range) - .domain(nodeColormap.domain); + if (typeof nodeColormap != 'undefined') { + if (typeof nodeColormap.range == 'undefined') + this.nodeColormap = d3.scaleOrdinal(d3.schemeCategory20) + .domain(nodeColormap.domain); + else + this.nodeColormap = d3.scaleOrdinal(nodeColormap.range) + .domain(nodeColormap.domain); + } }, setData: function (root, options, nodeColormap) { diff --git a/src/main/webapp/js/components/widgets/connectivity/chords.js b/src/main/webapp/js/components/widgets/connectivity/chords.js index dceb14de3..926586dca 100644 --- a/src/main/webapp/js/components/widgets/connectivity/chords.js +++ b/src/main/webapp/js/components/widgets/connectivity/chords.js @@ -15,8 +15,9 @@ define(function (require) { var innerRadius = Math.min(context.options.innerWidth, context.options.innerHeight) * .41, outerRadius = innerRadius * 1.05; - var fill = context.nodeColormap - .domain(context.dataset.nodeTypes); + // if no custom colors set then use d3 provided color scheme + var fill = context.nodeColormap.range ? context.nodeColormap : d3.scaleOrdinal(d3.schemeCategory20); + fill.domain(context.dataset.nodeTypes); var svg = context.svg.append("g") .attr("transform", "translate(" + context.options.innerWidth / 2 + "," + context.options.innerHeight / 2 + ")"); From 42ed7f0d49da0cc8165c4c533082476d3d3a8d64 Mon Sep 17 00:00:00 2001 From: jrmartin Date: Thu, 15 Jun 2017 19:57:01 -0700 Subject: [PATCH 076/371] tests for setgeometrytype and more checks for default extension --- .../extensions/extensionsConfiguration.json | 4 +- .../js/pages/tests/casperjs/CoreTests.js | 54 +++++++++++++------ .../pages/tests/casperjs/CoreTestsUtility.js | 12 +++++ .../webapp/js/pages/tests/casperjs/UITests.js | 21 +++++--- 4 files changed, 67 insertions(+), 24 deletions(-) diff --git a/src/main/webapp/extensions/extensionsConfiguration.json b/src/main/webapp/extensions/extensionsConfiguration.json index 1cc5f3dd8..5bac2b9a4 100644 --- a/src/main/webapp/extensions/extensionsConfiguration.json +++ b/src/main/webapp/extensions/extensionsConfiguration.json @@ -1,6 +1,6 @@ { - "geppetto-default/ComponentsInitialization": true, - "geppetto-osb/ComponentsInitialization": false, + "geppetto-default/ComponentsInitialization": false, + "geppetto-osb/ComponentsInitialization": true, "geppetto-vfb/ComponentsInitialization": false, "geppetto-neuron/ComponentsInitialization": false, "geppetto-hm/ComponentsInitialization": false diff --git a/src/main/webapp/js/pages/tests/casperjs/CoreTests.js b/src/main/webapp/js/pages/tests/casperjs/CoreTests.js index f64e7e833..e9b2fa9cc 100644 --- a/src/main/webapp/js/pages/tests/casperjs/CoreTests.js +++ b/src/main/webapp/js/pages/tests/casperjs/CoreTests.js @@ -159,6 +159,7 @@ function hhcellTest(test,name){ casper.then(function(){ closeSpotlight(); casper.echo("-------Testing Canvas Widget and Color Function--------"); + //adding few widgets to the project to test View state later casper.evaluate(function(){ hhcell.deselect(); GEPPETTO.ComponentFactory.addWidget('CANVAS', {name: '3D Canvas',}, function () {this.setName('Widget Canvas');this.setPosition();this.display([hhcell])}); @@ -171,14 +172,19 @@ function hhcellTest(test,name){ Popup2.addCustomNodeHandler(customHandler,'click'); }); - casper.mouseEvent('click', 'button#tutorialBtn', "attempting to open tutorial"); - + //toggle tutorial if tutorial button exists + if(casper.exists('#tutorialBtn')){ + casper.mouseEvent('click', 'button#tutorialBtn', "attempting to open tutorial"); + } + + //tests widget canvas has mesh var mesh = casper.evaluate(function(){ var mesh = Canvas2.engine.getRealMeshesForInstancePath("hhcell.hhpop[0]").length; return mesh; }); test.assertEquals(mesh, 1, "Canvas widget has hhcell"); + //click on next step for Tutorial casper.evaluate(function(){ $(".nextBtn").click(); $(".nextBtn").click(); @@ -195,44 +201,45 @@ function hhcellTest(test,name){ }) }); + //reload test, needed for testing view comes up casper.then(function(){launchTest(test,"Hhcell",30000);}); + //testing widgets stored in View state come up casper.then(function(){ test.assertVisible('div#Canvas2', "Canvas2 is correctly open on reload."); test.assertVisible('div#Plot1', "Plot1 is correctly open on reload"); test.assertVisible('div#Popup1', "Popup1 is correctly open on reload"); test.assertVisible('div#Popup2', "Popup2 is correctly open on reload"); - test.assertVisible('div#Tutorial1', "Tutorial1 is correctly open on reload"); - - var tutorialStep = casper.evaluate(function() { - return Tutorial1.state.currentStep; - }); - - test.assertEquals(tutorialStep, 2, "Tutorial1 step restored correctly"); + //if tutorial button exists, tests existence of Tutorial + if(casper.exists('#tutorialBtn')){ + test.assertVisible('div#Tutorial1', "Tutorial1 is correctly open on reload"); + var tutorialStep = casper.evaluate(function() { + return Tutorial1.state.currentStep; + }); + test.assertEquals(tutorialStep, 2, "Tutorial1 step restored correctly"); + } + //Tests content of Popup1 var popUpMessage = casper.evaluate(function() { return $("#Popup1").html(); }); - test.assertEquals(popUpMessage, "Hhcell popup", "Popup1 message restored correctly"); + //Tests popup has custom handlers var popUpCustomHandler = casper.evaluate(function() { return Popup2.customHandlers; }); - test.assertEquals(popUpCustomHandler.length, 1, "Popup2 custom handlers restored correctly"); test.assertEquals(popUpCustomHandler[0]["event"], "click", "Popup2 custom handlers event restored correctly"); + //Test canvas widget has mesh var meshInCanvas2Exists = casper.evaluate(function() { var mesh = Canvas1.engine.meshes["hhcell.hhpop[0]"]; - if(mesh!=null && mesh!=undefined){ return true; } - return false; }); - test.assertEquals(meshInCanvas2Exists, true, "Canvas2 hhcell set correctly"); }); }; @@ -320,6 +327,8 @@ function acnetTest(test){ casper.then(function(){ closeSpotlight(); //close spotlight before continuing casper.echo("-------Testing Canvas Widget and Color Function--------"); + + //adding few widgets to the project to test View state later casper.evaluate(function(){ acnet2.pyramidals_48[0].deselect(); GEPPETTO.ComponentFactory.addWidget('CANVAS', {name: '3D Canvas',}, function () {this.setName('Widget Canvas');this.setPosition();this.display([acnet2])}); @@ -329,8 +338,10 @@ function acnetTest(test){ acnet2.baskets_12[4].getVisualGroups()[0].show(true); }); + //tests camera controls are working by checking camera has moved testCameraControlsWithCanvasWidget(test,[231.95608349343888,508.36555704435455,1849.8390363191731]); + //applies visual group to instance and tests colors testVisualGroup(test,"acnet2.baskets_12[0]",2,[[],[0,0.4,1],[0.6,0.8,0]]); testVisualGroup(test,"acnet2.baskets_12[5]",2,[[],[0,0.4,1],[0.6,0.8,0]]); @@ -339,6 +350,8 @@ function acnetTest(test){ test3DMeshOpacity(test,1, "acnet2.baskets_12[4]"); test3DMeshOpacity(test,1, "acnet2.baskets_12[1]"); + casper.echo("Testing setGeometry"); + casper.evaluate(function(){ acnet2.pyramidals_48[0].setGeometryType("cylinders") }); @@ -354,31 +367,42 @@ function acnetTest(test){ }); test.assertEquals(meshTotal, 60, "Correctly amount of meshes after applying cylinders"); + //retrieve original color pre setGeomtry + var color = getMeshColor(test,"acnet2.pyramidals_48[0]"); casper.evaluate(function(){ acnet2.pyramidals_48[0].setGeometryType("lines") }); + casper.echo("Testing color post setGeometryType"); + test3DMeshColor(test,color,"acnet2.pyramidals_48[0]"); + //test mesh set geometry meshType = casper.evaluate(function(){ return Canvas1.engine.getRealMeshesForInstancePath("acnet2.pyramidals_48[0]")[0].type; }); test.assertEquals(meshType, "LineSegments", "Correctly set mesh to lines"); + //testsing same amount of meshes exists after changing a mesh to lines var meshTotal = casper.evaluate(function(){ return Object.keys(Canvas1.engine.meshes).length; }); test.assertEquals(meshTotal, 60, "Correctly amount of meshes after applying cylinders"); + //Set geometry type to cylinders casper.evaluate(function(){ acnet2.pyramidals_48[0].setGeometryType("cylinders") }); - //test mesh set geometry + //test set geometry type in a mesh var meshType = casper.evaluate(function(){ return Canvas1.engine.getRealMeshesForInstancePath("acnet2.pyramidals_48[0]")[0].type; }); test.assertEquals(meshType, "Mesh", "Correctly set mesh to cylinders"); + casper.echo("Testing color post setGeometryType to cylinders"); + test3DMeshColor(test,color,"acnet2.pyramidals_48[0]"); + + //testing same amount of meshes exists after changing a mesh to cylinders var meshTotal = casper.evaluate(function(){ return Object.keys(Canvas1.engine.meshes).length; }); diff --git a/src/main/webapp/js/pages/tests/casperjs/CoreTestsUtility.js b/src/main/webapp/js/pages/tests/casperjs/CoreTestsUtility.js index 0f73382a1..3b737de8e 100644 --- a/src/main/webapp/js/pages/tests/casperjs/CoreTestsUtility.js +++ b/src/main/webapp/js/pages/tests/casperjs/CoreTestsUtility.js @@ -153,6 +153,18 @@ function test3DMeshColor(test,testColor,variableName,index){ test.assertEquals(color[2],testColor[2],"Black default color is correct for " +variableName); } +function getMeshColor(test,variableName,index){ + if(index==undefined){ + index=0; + } + var color = casper.evaluate(function(variableName,index) { + var color = Canvas1.engine.getRealMeshesForInstancePath(variableName)[index].material.color; + return [color.r, color.g, color.b]; + },variableName,index); + + return color; +} + function test3DMeshOpacity(test,opactityExpected,variableName,index){ if(index==undefined){ index=0; diff --git a/src/main/webapp/js/pages/tests/casperjs/UITests.js b/src/main/webapp/js/pages/tests/casperjs/UITests.js index 9075b820d..f85b91ca2 100644 --- a/src/main/webapp/js/pages/tests/casperjs/UITests.js +++ b/src/main/webapp/js/pages/tests/casperjs/UITests.js @@ -182,14 +182,17 @@ function reloadProjectTest(test, url, customHandlers,widgetCanvasObject){ casper.wait(2000, function () {}); test.assertVisible('div#Canvas2', "Canvas2 is correctly open on reload."); test.assertVisible('div#Popup1', "Popup1 is correctly open on reload"); - test.assertVisible('div#Tutorial1', "Tutorial1 is correctly open on reload"); test.assertVisible('div#Connectivity1', "Connectivity1 is correctly open on reload"); - var tutorialStep = casper.evaluate(function() { - return Tutorial1.state.currentStep; - }); - - test.assertEquals(tutorialStep, 2, "Tutorial1 step restored correctly"); + if(casper.exists('#tutorialBtn')){ + test.assertVisible('div#Tutorial1', "Tutorial1 is correctly open on reload"); + + var tutorialStep = casper.evaluate(function() { + return Tutorial1.state.currentStep; + }); + + test.assertEquals(tutorialStep, 2, "Tutorial1 step restored correctly"); + } var popUpCustomHandler = casper.evaluate(function() { return Popup1.customHandlers; @@ -247,7 +250,9 @@ function testProject(test, url, expect_error, persisted, spotlight_record_variab }); casper.then(function () { - casper.mouseEvent('click', 'button#tutorialBtn', "attempting to open tutorial"); + if(casper.exists('#tutorialBtn')){ + casper.mouseEvent('click', 'button#tutorialBtn', "attempting to open tutorial"); + } casper.evaluate(function(widgetCanvasObject) { var canvasObject = null; if(widgetCanvasObject=="hhcell"){ @@ -265,6 +270,8 @@ function testProject(test, url, expect_error, persisted, spotlight_record_variab $(".nextBtn").click(); $(".nextBtn").click(); },widgetCanvasObject); + + casper.wait(2000, function () {}); this.echo("Checking content of experiment row"); // test or wait for control panel stuff to be there if(this.exists('a[href="#experiments"]')){ From c410a684270902422a7a84d9c9fdab7775a37d7c Mon Sep 17 00:00:00 2001 From: Matt Earnshaw Date: Fri, 16 Jun 2017 12:08:16 +0100 Subject: [PATCH 077/371] better handling of state (library as path) --- .../js/components/widgets/connectivity/Connectivity.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/webapp/js/components/widgets/connectivity/Connectivity.js b/src/main/webapp/js/components/widgets/connectivity/Connectivity.js index f3358766c..26292a087 100644 --- a/src/main/webapp/js/components/widgets/connectivity/Connectivity.js +++ b/src/main/webapp/js/components/widgets/connectivity/Connectivity.js @@ -401,7 +401,7 @@ define(function (require) { return "Gap junction"; } } - that.setData(netInstances[0], {layout: event.currentTarget.id, linkType: synapseFromConnection}); //TODO: add option to select what to plot if #netInstance>1? + that.setData(netInstances[0], {layout: event.currentTarget.id, linkType: synapseFromConnection, library: that.connectivityOptions.library}); //TODO: add option to select what to plot if #netInstance>1? firstClick=true; } @@ -429,6 +429,9 @@ define(function (require) { if (typeof this.connectivityOptions[item] === "function") { serializedItem.value = this.connectivityOptions[item].toString(); serializedItem.type = 'function'; + } else if (item === "library") { + serializedItem.value = this.connectivityOptions[item].getPath(); + serializedItem.type = 'library'; } else { serializedItem.value = this.connectivityOptions[item]; serializedItem.type = 'primitive'; @@ -454,7 +457,7 @@ define(function (require) { var obj = eval(view.data); var deserializedOptions = {}; for(var item in view.options){ - if(view.options[item].type == "function"){ + if(view.options[item].type == "function" || view.options[item].type == "library"){ deserializedOptions[item] = eval('(' + view.options[item].value + ')'); } else { deserializedOptions[item] = view.options[item].value; From 85ca8dea72b29ff5fa83f0e483e564bc2619c469 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Mon, 19 Jun 2017 14:27:50 +0100 Subject: [PATCH 078/371] fix for split volumens --- .../webapp/js/components/interface/3dCanvas/ThreeDEngine.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js b/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js index f9632789c..f8771b0fb 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js +++ b/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js @@ -1465,7 +1465,7 @@ define(['jquery'], function () { var instance = eval(instancePath); // Behaviour: help exploration of networks by ghosting and not highlighting non connected or selected - if (instance.getConnections().length > 0) { + if (instance !== undefined && instance.getConnections().length > 0) { // allOtherMeshes will contain a list of all the non connected entities in the scene var allOtherMeshes = $.extend({}, this.meshes); // look on the simulation selection options and perform necessary operations From 4f48c51adc2c440dee789fa97192359134e317d2 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Mon, 19 Jun 2017 15:40:27 +0100 Subject: [PATCH 079/371] fix plit space and split groups --- .../geppettoJupyter/GeppettoJupyterModelSync.js | 3 +-- .../js/components/interface/3dCanvas/Canvas.js | 14 ++++++++++++-- .../interface/3dCanvas/SceneController.js | 16 ++++++++++++++-- .../interface/3dCanvas/ThreeDEngine.js | 9 ++++----- .../js/geppettoModel/model/VisualGroupElement.js | 2 +- 5 files changed, 32 insertions(+), 12 deletions(-) diff --git a/src/main/webapp/js/communication/geppettoJupyter/GeppettoJupyterModelSync.js b/src/main/webapp/js/communication/geppettoJupyter/GeppettoJupyterModelSync.js index fc8b528ee..2c3f1cc90 100644 --- a/src/main/webapp/js/communication/geppettoJupyter/GeppettoJupyterModelSync.js +++ b/src/main/webapp/js/communication/geppettoJupyter/GeppettoJupyterModelSync.js @@ -227,8 +227,7 @@ define(function (require, exports, module) { for (var i = 0; i < this.get('geometries').length; i++) { elements[this.get('geometries')[i].id] = ""; } - //GEPPETTO.SceneController.splitGroups(window.Instances[0], elements); - Canvas1.engine.splitGroups(window.Instances[0], elements); + GEPPETTO.SceneController.splitGroups(window.Instances[0], elements); } }, diff --git a/src/main/webapp/js/components/interface/3dCanvas/Canvas.js b/src/main/webapp/js/components/interface/3dCanvas/Canvas.js index 83055875f..ee2e3c7a9 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/Canvas.js +++ b/src/main/webapp/js/components/interface/3dCanvas/Canvas.js @@ -345,6 +345,16 @@ define(function (require) { return this; } + /** + * Split merged mesh into individual meshes + * @param instances + * @param groupElements + */ + splitGroups(instance, groupElements){ + this.engine.splitGroups(instance, groupElements); + return this; + } + /** * Associate a color function to a group of instances * @@ -382,8 +392,8 @@ define(function (require) { * @param instance * @returns {Canvas} */ - showVisualGroupsForInstance(instance) { - this.engine.showVisualGroupsForInstance(instance); + showVisualGroupsForInstance(instance, visualGroupElement) { + this.engine.showVisualGroupsForInstance(instance, visualGroupElement); return this; } diff --git a/src/main/webapp/js/components/interface/3dCanvas/SceneController.js b/src/main/webapp/js/components/interface/3dCanvas/SceneController.js index d9b03814d..c48adce94 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/SceneController.js +++ b/src/main/webapp/js/components/interface/3dCanvas/SceneController.js @@ -313,9 +313,9 @@ define(['jquery'], function () { * * @param instance */ - showVisualGroupsForInstance: function (instance) { + showVisualGroupsForInstance: function (instance, visualGroupElement) { for (var i = 0; i < this.canvasComponents.length; i++) { - this.canvasComponents[i].showVisualGroupsForInstance(instance); + this.canvasComponents[i].showVisualGroupsForInstance(instance, visualGroupElement); } }, @@ -331,6 +331,18 @@ define(['jquery'], function () { } }, + + /** + * Split merged mesh into individual meshes + * @param instances + * @param groupElements + */ + splitGroups: function (instance, groupElements) { + for (var i = 0; i < this.canvasComponents.length; i++) { + this.canvasComponents[i].splitGroups(instance, groupElements); + } + }, + /** * Associate a color function to a group of instances for all the existing canvas. * The visual instance that will be colored is the one associated with the composite type diff --git a/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js b/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js index f8771b0fb..ad342292e 100644 --- a/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js +++ b/src/main/webapp/js/components/interface/3dCanvas/ThreeDEngine.js @@ -2391,8 +2391,7 @@ define(['jquery'], function () { this.setThreeColor(groupMesh.material.color, splitHighlightedGroups[groupName].color.getHex()); } } - } - , + }, /** * Split merged mesh into individual meshes @@ -2614,7 +2613,7 @@ define(['jquery'], function () { * @param instance * @param meshesContainer */ - showVisualGroupsForInstance: function (instance) { + showVisualGroupsForInstance: function (instance, visualGroupElement) { if (!this.hasInstance(instance)) { return; } @@ -2634,8 +2633,8 @@ define(['jquery'], function () { var groupElementsReference = object.getInitialValue().value.groupElements; for (var i = 0; i < groupElementsReference.length; i++) { var objectGroup = GEPPETTO.ModelFactory.resolve(groupElementsReference[i].$ref).getId(); - if (objectGroup == this.getId()) { - elements[object.getId()] = {'color': this.getColor()} + if (objectGroup == visualGroupElement.getId()) { + elements[object.getId()] = {'color': visualGroupElement.getColor()} } } } diff --git a/src/main/webapp/js/geppettoModel/model/VisualGroupElement.js b/src/main/webapp/js/geppettoModel/model/VisualGroupElement.js index 7735ff760..d801b59bf 100644 --- a/src/main/webapp/js/geppettoModel/model/VisualGroupElement.js +++ b/src/main/webapp/js/geppettoModel/model/VisualGroupElement.js @@ -76,7 +76,7 @@ define(function (require) { } for (var i = 0; i < instances.length; i++) { - GEPPETTO.SceneController.showVisualGroup(instances[i]); + GEPPETTO.SceneController.showVisualGroupsForInstance(instances[i], this); } }; From 75cea75647180ca78a31b36abd55f98e2466dcc7 Mon Sep 17 00:00:00 2001 From: Matt Earnshaw Date: Mon, 19 Jun 2017 16:03:56 +0100 Subject: [PATCH 080/371] initial working connectivity matrix pre/post pop indicators --- .../widgets/connectivity/matrices.js | 42 +++++++++++++++++-- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/src/main/webapp/js/components/widgets/connectivity/matrices.js b/src/main/webapp/js/components/widgets/connectivity/matrices.js index 574e9e505..4feaaf5db 100644 --- a/src/main/webapp/js/components/widgets/connectivity/matrices.js +++ b/src/main/webapp/js/components/widgets/connectivity/matrices.js @@ -7,7 +7,7 @@ define(function (require) { createMatrixLayout: function (context) { var d3 = require("d3"); - var margin = { top: 30, right: 10, bottom: 10, left: 0 }; + var margin = { top: 45, right: 10, bottom: 10, left: 15 }; var legendWidth = 120; var matrixDim = (context.options.innerHeight < (context.options.innerWidth - legendWidth)) ? (context.options.innerHeight) : (context.options.innerWidth - legendWidth); @@ -18,7 +18,7 @@ define(function (require) { // Colors c = d3.scaleOrdinal(d3.schemeCategory10); - var labelTop = margin.top - 10; + var labelTop = margin.top - 25; var tooltip = context.svg .append("g") .attr("transform", "translate(" + margin.left + "," + labelTop + ")") @@ -80,9 +80,45 @@ define(function (require) { var rect = container .append("rect") .attr("class", "background") - .attr("width", matrixDim - margin.left) + .attr("width", matrixDim - margin.left - 30) .attr("height", matrixDim - margin.top); + var types = nodes.map(function(x) { return x.id; }); + + var popIndicator = function(pos) { + return function(d,i) { + d3.select(this).selectAll(".cell") + .data(d) + .enter().append("circle") + .attr("class", "cell") + .attr(pos, function (d, i) { + return x(i); + }) + .attr("r", x.bandwidth()/2) + .attr("title", function (d) { + return d.id; + }) + .style("fill", function (d) { + return eval(GEPPETTO.ModelFactory.getAllPotentialInstancesEndingWith(d)[0]).getColor(); + }) + }; + }; + + var prePop = container.selectAll(".prePop") + .data([types]) + .enter() + .append("g") + .attr("class", "prePop") + .attr("transform", "translate(4,-10)") + .each(popIndicator("cx")); + var postPop = container.selectAll(".postPop") + .data([types]) + .enter() + .append("g") + .attr("class", "postPop") + .attr("transform", "translate(-10,4)") + .each(popIndicator("cy")); + var row = container.selectAll(".row") .data(matrix) .enter().append("g") From f2f5390f307b594757c6a37cd368ca23fd1f4b24 Mon Sep 17 00:00:00 2001 From: johnidol Date: Mon, 19 Jun 2017 16:18:20 +0100 Subject: [PATCH 081/371] more generic implementation - the other version wasn't working on chrome macosx --- .../experimentsTable/ExperimentsTable.js | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/main/webapp/js/components/interface/experimentsTable/ExperimentsTable.js b/src/main/webapp/js/components/interface/experimentsTable/ExperimentsTable.js index 47dff9fa6..4e0c63f33 100644 --- a/src/main/webapp/js/components/interface/experimentsTable/ExperimentsTable.js +++ b/src/main/webapp/js/components/interface/experimentsTable/ExperimentsTable.js @@ -679,12 +679,20 @@ define(function (require) { }, //Determines if an element inside the experiments table is in view - isInView : function(elem){ - var docViewTop = $('#experimentsTable').offset().top; - var docViewBottom = docViewTop + $('#experimentsTable').height(); - var elemTop = $(elem).offset().top; - var elemBottom = elemTop + $(elem).height(); - return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop)); + isInView : function(el){ + // grab vanilla dom element from jquery element + el = el[0]; + var rect = el.getBoundingClientRect(), top = rect.top, height = rect.height, + el = el.parentNode; + do { + rect = el.getBoundingClientRect(); + if (top <= rect.bottom === false) return false; + // Check if the element is out of view due to a container scrolling + if ((top + height) <= rect.top) return false; + el = el.parentNode; + } while (el != document.body); + // Check its within the document viewport + return top <= document.documentElement.clientHeight; }, /** From ecd425c119c6f6c7c937d3427a3f43d9376a37d0 Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Mon, 19 Jun 2017 16:36:31 +0100 Subject: [PATCH 082/371] remove wrong condition --- src/main/webapp/js/geppettoProject/ExperimentsController.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/webapp/js/geppettoProject/ExperimentsController.js b/src/main/webapp/js/geppettoProject/ExperimentsController.js index 02b4ce3f6..e248ff3ce 100644 --- a/src/main/webapp/js/geppettoProject/ExperimentsController.js +++ b/src/main/webapp/js/geppettoProject/ExperimentsController.js @@ -262,7 +262,7 @@ define(function (require) { if (experiment.status == GEPPETTO.Resources.ExperimentStatus.COMPLETED) { // playExperimentReady true even if some variables // do not have value, so if playAll then we get state here - if (!this.playExperimentReady || this.playOptions.playAll) { + if (!this.playExperimentReady) { this.getExperimentState(experiment.getParent().getId(), experiment.id, null); return "Play Experiment"; } else { From d1d542c17d366356204d3fa085f97bf910927639 Mon Sep 17 00:00:00 2001 From: johnidol Date: Mon, 19 Jun 2017 16:37:05 +0100 Subject: [PATCH 083/371] Semi-colon-fest 2017 --- .../js/pages/tests/casperjs/CoreTests.js | 22 +++++++++---------- .../pages/tests/casperjs/CoreTestsUtility.js | 6 ++--- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/main/webapp/js/pages/tests/casperjs/CoreTests.js b/src/main/webapp/js/pages/tests/casperjs/CoreTests.js index e9b2fa9cc..781145d47 100644 --- a/src/main/webapp/js/pages/tests/casperjs/CoreTests.js +++ b/src/main/webapp/js/pages/tests/casperjs/CoreTests.js @@ -114,11 +114,11 @@ function hhcellTest(test,name){ casper.then(function(){ casper.echo("-------Testing Control Panel--------"); testInitialControlPanelValues(test,3); - }) + }); casper.then(function(){ casper.echo("-------Testing Mesh Visibility--------"); testVisibility(test,"hhcell.hhpop[0]","#hhcell_hhpop_0__visibility_ctrlPanel_btn"); - }) + }); casper.then(function () { casper.echo("-------Testing Plotting from Control Panel--------"); buttonClick("#stateVariablesFilterBtn"); @@ -242,7 +242,7 @@ function hhcellTest(test,name){ }); test.assertEquals(meshInCanvas2Exists, true, "Canvas2 hhcell set correctly"); }); -}; +} /** * Main method for testing ACNet @@ -270,7 +270,7 @@ function acnetTest(test){ casper.then(function(){ casper.echo("-------Testing Mesh Visibility--------"); testVisibility(test,"acnet2.pyramidals_48[0]","#acnet2_pyramidals_48_0__visibility_ctrlPanel_btn"); - }) + }); casper.then(function () { casper.echo("-------Testing Plotting from Control Panel--------"); buttonClick("#stateVariablesFilterBtn"); @@ -458,7 +458,7 @@ function c302Test(test){ this.waitUntilVisible('button[id="c302_ADAL_0__v_plot_ctrlPanel_btn"]', function () { buttonClick("#c302_ADAL_0__v_plot_ctrlPanel_btn"); }); - }) + }); casper.then(function(){ this.waitUntilVisible('div[id="Plot1"]', function () { @@ -477,12 +477,12 @@ function c302Test(test){ testSpotlight(test, "c302.ADAL[0].v",'div[id="Plot2"]',true,false); }); }); - }) + }); casper.then(function(){ casper.echo("-------Testing Spotlight--------"); testSpotlight(test, "c302.ADAL[0].v",'div[id="Plot2"]',true,false); - }) + }); casper.then(function(){ closeSpotlight(); //close spotlight before continuing @@ -504,8 +504,8 @@ function c302Test(test){ //test color function test3DMeshColorNotEquals(test,defaultColor,"c302.PVDR[0]"); casper.echo("Done Playing, now exiting"); - }) - }) + }); + }); } function ca1Test(test){ @@ -528,7 +528,7 @@ function ca1Test(test){ this.evaluate(function(){ $("#anyProjectFilterBtn").click(); }); - }) + }); casper.then(function(){ casper.evaluate(function() { @@ -552,7 +552,7 @@ function pharyngealTest(test){ casper.then(function(){ testInitialControlPanelValues(test,10); }); -}; +} function nwbSampleTest(test){ casper.waitForSelector('div[id="Popup1"]', function() { diff --git a/src/main/webapp/js/pages/tests/casperjs/CoreTestsUtility.js b/src/main/webapp/js/pages/tests/casperjs/CoreTestsUtility.js index 3b737de8e..cbacb48a9 100644 --- a/src/main/webapp/js/pages/tests/casperjs/CoreTestsUtility.js +++ b/src/main/webapp/js/pages/tests/casperjs/CoreTestsUtility.js @@ -33,7 +33,7 @@ function resetCameraTestWithCanvasWidget(test,expectedCameraPosition){ buttonClick("#panHomeBtn"); casper.evaluate(function(){ $("#Canvas2_component").find(".position-toolbar").find(".pan-home").click(); - }) + }); testCameraPosition(test,expectedCameraPosition); } @@ -297,7 +297,7 @@ function testSpotlight(test, variableName,plotName,expectButton,testSelect, sele */ function testCameraControls(test, expectedCameraPosition){ casper.then(function(){ - casper.echo("------Zoom-------") + casper.echo("------Zoom-------"); casper.repeat(zoomClicks, function() { this.thenClick("button#zoomInBtn", function() {}); }); @@ -334,7 +334,7 @@ function testCameraControls(test, expectedCameraPosition){ function testCameraControlsWithCanvasWidget(test, expectedCameraPosition){ casper.echo("-------Testing Camera Controls while playing experiment--------"); casper.then(function(){ - casper.echo("------Zoom-------") + casper.echo("------Zoom-------"); casper.repeat(zoomClicks*2, function() { this.thenClick("button#zoomInBtn", function() {}); this.thenClick("#Canvas2 button#zoomInBtn", function() {}); From fa7ed15b0a7a330a70d860ce9571b818273cb16e Mon Sep 17 00:00:00 2001 From: Adrian Quintana Date: Mon, 19 Jun 2017 17:10:35 +0100 Subject: [PATCH 084/371] implementing a quick solution for now to the add3dsphere and modify3dsphere --- .../communication/geppettoJupyter/GeppettoJupyterModelSync.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/webapp/js/communication/geppettoJupyter/GeppettoJupyterModelSync.js b/src/main/webapp/js/communication/geppettoJupyter/GeppettoJupyterModelSync.js index 2c3f1cc90..5cadb5525 100644 --- a/src/main/webapp/js/communication/geppettoJupyter/GeppettoJupyterModelSync.js +++ b/src/main/webapp/js/communication/geppettoJupyter/GeppettoJupyterModelSync.js @@ -381,11 +381,11 @@ define(function (require, exports, module) { else if (msg.type === 'draw_sphere') { var content = msg.content; if (this.point_process_sphere) { - this.point_process_sphere = GEPPETTO.SceneFactory.modify3DSphere(this.point_process_sphere, content.x, content.y, content.z, content.radius); + this.point_process_sphere = Canvas1.engine.modify3DSphere(this.point_process_sphere, content.x, content.y, content.z, content.radius); this.point_process_sphere.visible = true; } else { - this.point_process_sphere = GEPPETTO.SceneFactory.add3DSphere(content.x, content.y, content.z, content.radius); + this.point_process_sphere = Canvas1.engine.add3DSphere(content.x, content.y, content.z, content.radius); } } else if (msg.type === 'remove_sphere') { From 76914aa5f9a52e8166a19b17238442caea5570b3 Mon Sep 17 00:00:00 2001 From: Matt Earnshaw Date: Mon, 19 Jun 2017 17:22:11 +0100 Subject: [PATCH 085/371] use d3 palette when no custom colors set --- .../js/components/widgets/connectivity/matrices.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/webapp/js/components/widgets/connectivity/matrices.js b/src/main/webapp/js/components/widgets/connectivity/matrices.js index 4feaaf5db..817c7b8b0 100644 --- a/src/main/webapp/js/components/widgets/connectivity/matrices.js +++ b/src/main/webapp/js/components/widgets/connectivity/matrices.js @@ -85,9 +85,9 @@ define(function (require) { var types = nodes.map(function(x) { return x.id; }); - var popIndicator = function(pos) { + var popIndicator = function(pos, colormap) { return function(d,i) { - d3.select(this).selectAll(".cell") + d3.select(this).selectAll(".cell") .data(d) .enter().append("circle") .attr("class", "cell") @@ -99,7 +99,9 @@ define(function (require) { return d.id; }) .style("fill", function (d) { - return eval(GEPPETTO.ModelFactory.getAllPotentialInstancesEndingWith(d)[0]).getColor(); + return colormap( + eval(GEPPETTO.ModelFactory.getAllPotentialInstancesEndingWith(d)[0]) + .getType().getId()); }) }; }; @@ -109,15 +111,15 @@ define(function (require) { .enter() .append("g") .attr("class", "prePop") - .attr("transform", "translate(4,-10)") - .each(popIndicator("cx")); + .attr("transform", "translate(3,-10)") + .each(popIndicator("cx", context.nodeColormap)); var postPop = container.selectAll(".postPop") .data([types]) .enter() .append("g") .attr("class", "postPop") .attr("transform", "translate(-10,4)") - .each(popIndicator("cy")); + .each(popIndicator("cy", context.nodeColormap)); var row = container.selectAll(".row") .data(matrix) From 123b81bb9083f55a5931b3585128a41fa9a10d8d Mon Sep 17 00:00:00 2001 From: Matteo Cantarelli Date: Mon, 19 Jun 2017 17:57:07 +0100 Subject: [PATCH 086/371] Use API instead of directly tinkering with the DOM --- .../geppettoJupyter/GeppettoJupyterGUISync.js | 26 +- .../webapp/js/components/widgets/AWidget.js | 18 +- .../webapp/js/components/widgets/Widget.js | 413 +++++++++--------- 3 files changed, 237 insertions(+), 220 deletions(-) diff --git a/src/main/webapp/js/communication/geppettoJupyter/GeppettoJupyterGUISync.js b/src/main/webapp/js/communication/geppettoJupyter/GeppettoJupyterGUISync.js index b3e09f7a0..a99db7320 100644 --- a/src/main/webapp/js/communication/geppettoJupyter/GeppettoJupyterGUISync.js +++ b/src/main/webapp/js/communication/geppettoJupyter/GeppettoJupyterGUISync.js @@ -47,6 +47,7 @@ define(function (require, exports, module) { parameters['sync_value'] = this.get('sync_value'); parameters['handleChange'] = this.handleChange.bind(null, this); parameters['handleBlur'] = this.handleBlur.bind(null, this); + parameters['isStateless'] = true; return parameters; }, @@ -77,9 +78,10 @@ define(function (require, exports, module) { this.on("msg:custom", this.handle_custom_messages, this); this.on("comm:close", this.close_panel, this); this.on("change:widget_name", function (model, value, options) { - $("#" + this.get('widget_id') + "_dialog").dialog('option', 'title', this.get("widget_name")); + this.get("component").setName(this.get("widget_name")); }); }, + close_panel: function (msg) { this.set('triggerClose', false); $("." + this.get('widget_id') + "_dialog").find(".ui-dialog-titlebar-close").click(); @@ -124,6 +126,7 @@ define(function (require, exports, module) { } }); + // Do not allow resizable for parent panel var selector = $("." + this.get('widget_id') + "_dialog"); selector.resizable('destroy'); @@ -131,23 +134,14 @@ define(function (require, exports, module) { // Do not allow close depending on property for (var propertyName in this.get("properties")){ if (propertyName == "closable" && this.get("properties")["closable"] == false){ - selector.find(".ui-dialog-titlebar-close").hide(); + this.get("component").showCloseButton(false); } } - // Resize widget dim and pos - if (this.get('position_x') > 0) { - selector.css({ left: this.get('position_x') }); - } - if (this.get('position_y') > 0) { - selector.css({ top: this.get('position_y') }); - } - if (this.get('width') > 0) { - selector.css({ width: this.get('width') }); - } - if (this.get('height') > 0) { - selector.css({ height: this.get('height') }); - } + this.get("component").setPosition(this.get('position_x'), this.get('position_y')); + this.get("component").setSize(this.get('height'), this.get('width')); + this.get("component").showHistoryIcon(false); + }, handle_custom_messages: function (msg) { @@ -155,7 +149,7 @@ define(function (require, exports, module) { this.display(); } else if (msg.type === 'shake') { - $('#' + this.get('widget_id') + "_dialog").parent().effect('shake', {distance:5, times: 3}, 500); + this.get("component").shake(); } } }, { diff --git a/src/main/webapp/js/components/widgets/AWidget.js b/src/main/webapp/js/components/widgets/AWidget.js index ce835618e..b3c26d91c 100644 --- a/src/main/webapp/js/components/widgets/AWidget.js +++ b/src/main/webapp/js/components/widgets/AWidget.js @@ -119,15 +119,21 @@ define(function (require) { return this; } + + /** * @command setPosition(left,top) * @param {Integer} left -Left position of the widget * @param {Integer} top - Top position of the widget */ setPosition(left, top) { - this.position.left = left; - this.position.top = top; + if (left != null && left != undefined) { + this.position.left = left; + } + if (top != null && top != undefined) { + this.position.top = top; + } this.$el.dialog( 'option', 'position', { my: "left+" + this.position.left + " top+" + this.position.top, @@ -148,7 +154,13 @@ define(function (require) { * @param {Integer} w - Width of the widget */ setSize(h, w) { - this.size = {height: h, width:w}; + if (h != null && h != undefined && h!=-1) { + this.size.height = h; + } + if (w != null && w != undefined && w!=-1) { + this.size.width = w; + } + this.$el.dialog({ height: this.size.height, width: this.size.width }).dialogExtend(); this.$el.trigger('resizeEnd'); diff --git a/src/main/webapp/js/components/widgets/Widget.js b/src/main/webapp/js/components/widgets/Widget.js index 3be4eb6d7..130d1fef7 100644 --- a/src/main/webapp/js/components/widgets/Widget.js +++ b/src/main/webapp/js/components/widgets/Widget.js @@ -10,7 +10,7 @@ define(function (require) { var Backbone = require('backbone'); var $ = require('jquery'); require("./jquery.dialogextend.min"); - + return { /** @@ -30,20 +30,20 @@ define(function (require) { size: {}, position: {}, registeredEvents: null, - executedAction : 0, - title : null, - previousMaxTransparency : false, - previousMaxSize : {}, - maximize : false, - collapsed : false, + executedAction: 0, + title: null, + previousMaxTransparency: false, + previousMaxSize: {}, + maximize: false, + collapsed: false, widgetType: null, stateless: false, showTitleBar: true, transparentBackground: false, dirtyView: false, - defaultSize : function(){return {height: 300, width: 350}}, - defaultPosition : function(){return {left: "50%", top: "50%"}}, + defaultSize: function () { return { height: 300, width: 350 } }, + defaultPosition: function () { return { left: "50%", top: "50%" } }, /** * Initializes the widget * @@ -63,24 +63,24 @@ define(function (require) { this.stateless = (options.stateless != undefined) ? options.stateless : false; this.registeredEvents = []; this.dirtyView = false; - + var self = this; $(self.historyMenu.el).on('click', function (event) { - var itemId = $(event.target).attr('id'); - var registeredItem = self.historyMenu.getClickedItem(itemId); - if(registeredItem != null || registeredItem!=undefined){ - var label = registeredItem["label"]; - self.title = label; - $("#"+self.id).parent().find(".ui-dialog-title").html(self.title); - } + var itemId = $(event.target).attr('id'); + var registeredItem = self.historyMenu.getClickedItem(itemId); + if (registeredItem != null || registeredItem != undefined) { + var label = registeredItem["label"]; + self.title = label; + $("#" + self.id).parent().find(".ui-dialog-title").html(self.title); + } }); - window.addEventListener('resize', function(event){ - if(self.maximize){ - self.maximize = false; - self.setSize(window.innerHeight,window.innerWidth); - self.$el.trigger('resizeEnd', ["maximize"]); - self.maximize = true; - } + window.addEventListener('resize', function (event) { + if (self.maximize) { + self.maximize = false; + self.setSize(window.innerHeight, window.innerWidth); + self.$el.trigger('resizeEnd', ["maximize"]); + self.maximize = true; + } }); }, @@ -131,7 +131,7 @@ define(function (require) { * * @returns {int} */ - getComponentType: function(){ + getComponentType: function () { return this.widgetType; }, @@ -168,20 +168,24 @@ define(function (require) { * @param {Integer} top - Top position of the widget */ setPosition: function (left, top) { - this.position.left = left; - this.position.top = top; + if (left != null && left != undefined) { + this.position.left = left; + } + if (top != null && top != undefined) { + this.position.top = top; + } - this.$el.dialog( - 'option', 'position', { - my: "left+" + this.position.left + " top+" + this.position.top, - at: "left top", - of: $(window) - }).dialogExtend(); + this.$el.dialog( + 'option', 'position', { + my: "left+" + this.position.left + " top+" + this.position.top, + at: "left top", + of: $(window) + }).dialogExtend(); // set flag to indicate something changed this.dirtyView = true; - return this; + return this; }, /** @@ -191,15 +195,21 @@ define(function (require) { * @param {Integer} w - Width of the widget */ setSize: function (h, w) { - this.size.height = h; - this.size.width = w; - this.$el.dialog({height: this.size.height, width: this.size.width}).dialogExtend(); - this.$el.trigger('resizeEnd'); + if (h != null && h != undefined && h!=-1) { + this.size.height = h; + } + if (w != null && w != undefined && w!=-1) { + this.size.width = w; + } + this.$el.dialog({ height: this.size.height, width: this.size.width }).dialogExtend(); + + this.$el.trigger('resizeEnd'); + // set flag to indicate something changed this.dirtyView = true; - return this; + return this; }, /** @@ -236,7 +246,7 @@ define(function (require) { * @param {Boolean} true|false - enables / disables resizability */ setResizable: function (resize) { - this.$el.dialog('option', 'resizable', resize).dialogExtend(); + this.$el.dialog('option', 'resizable', resize).dialogExtend(); return this; }, @@ -244,7 +254,7 @@ define(function (require) { * @command setAutoWidth() */ setAutoWidth: function () { - this.$el.dialog('option', 'width', 'auto').dialogExtend(); + this.$el.dialog('option', 'width', 'auto').dialogExtend(); return this; }, @@ -252,7 +262,7 @@ define(function (require) { * @command setAutoHeigth() */ setAutoHeight: function () { - this.$el.dialog('option', 'height', 'auto').dialogExtend(); + this.$el.dialog('option', 'height', 'auto').dialogExtend(); return this; }, @@ -357,7 +367,7 @@ define(function (require) { getItems: function (history, name) { var data = []; for (var i = 0; i < history.length; i++) { - var action = this.getId() + "[" + this.getId() + "."+name+"[" + i + "].method].apply(" + this.getId() + ", " + this.getId() + "."+name+"[" + i + "].arguments)"; + var action = this.getId() + "[" + this.getId() + "." + name + "[" + i + "].method].apply(" + this.getId() + ", " + this.getId() + "." + name + "[" + i + "].arguments)"; data.push({ "label": history[i].label, "action": [action], @@ -418,9 +428,9 @@ define(function (require) { this.showTitleBar = show; if (show) { - this.$el.parent().find(".ui-dialog-titlebar").show(); + this.$el.parent().find(".ui-dialog-titlebar").show(); } else { - this.$el.parent().find(".ui-dialog-titlebar").hide(); + this.$el.parent().find(".ui-dialog-titlebar").hide(); } // set flag to indicate something changed @@ -428,59 +438,59 @@ define(function (require) { return this; }, - - updateNavigationHistoryBar : function(){ - var disabled = "arrow-disabled"; - if(this.getItems(this.controller.history, "controller.history").length<=1){ - if(!$("#"+this.id + "-left-nav").hasClass(disabled)){ - $("#"+this.id + "-left-nav").addClass(disabled); - $("#"+this.id + "-right-nav").addClass(disabled); - } - }else{ - if($("#"+this.id + "-left-nav").hasClass(disabled)){ - $("#"+this.id + "-left-nav").removeClass(disabled); - $("#"+this.id + "-right-nav").removeClass(disabled); - } - } - }, - - showHistoryNavigationBar : function(show){ - var leftNav = $("#"+this.id + "-left-nav"); - var rightNav = $("#"+this.id + "-right-nav"); - - if (show) { - if((leftNav.length ==0) && (rightNav.length == 0)){ - - var disabled = ""; - if(this.getItems(this.controller.history, "controller.history").length<=1){ - disabled = "arrow-disabled "; - } - - var that = this; - var button = $("
"+ - "
").click(function (event) { - var historyItems = that.getItems(that.controller.history, "controller.history"); - var item; - if(event.target.id == (that.id + "-left-nav") || (that.id + "-right-nav")){ - that.executedAction = historyItems.length-1; - } - item = historyItems[that.executedAction].action[0]; - GEPPETTO.Console.executeImplicitCommand(item); - $("#"+that.id).parent().find(".ui-dialog-title").html(historyItems[that.executedAction].label); - event.stopPropagation(); - }); - - var dialogParent = this.$el.parent(); - button.insertBefore(dialogParent.find("span.ui-dialog-title")); - $(button).addClass("widget-title-bar-button"); - } - } else { - if(leftNav.is(":visible") && rightNav.is(":visible")){ - leftNav.remove(); - rightNav.remove(); - this.executedAction =0; - } - } + + updateNavigationHistoryBar: function () { + var disabled = "arrow-disabled"; + if (this.getItems(this.controller.history, "controller.history").length <= 1) { + if (!$("#" + this.id + "-left-nav").hasClass(disabled)) { + $("#" + this.id + "-left-nav").addClass(disabled); + $("#" + this.id + "-right-nav").addClass(disabled); + } + } else { + if ($("#" + this.id + "-left-nav").hasClass(disabled)) { + $("#" + this.id + "-left-nav").removeClass(disabled); + $("#" + this.id + "-right-nav").removeClass(disabled); + } + } + }, + + showHistoryNavigationBar: function (show) { + var leftNav = $("#" + this.id + "-left-nav"); + var rightNav = $("#" + this.id + "-right-nav"); + + if (show) { + if ((leftNav.length == 0) && (rightNav.length == 0)) { + + var disabled = ""; + if (this.getItems(this.controller.history, "controller.history").length <= 1) { + disabled = "arrow-disabled "; + } + + var that = this; + var button = $("
" + + "
").click(function (event) { + var historyItems = that.getItems(that.controller.history, "controller.history"); + var item; + if (event.target.id == (that.id + "-left-nav") || (that.id + "-right-nav")) { + that.executedAction = historyItems.length - 1; + } + item = historyItems[that.executedAction].action[0]; + GEPPETTO.Console.executeImplicitCommand(item); + $("#" + that.id).parent().find(".ui-dialog-title").html(historyItems[that.executedAction].label); + event.stopPropagation(); + }); + + var dialogParent = this.$el.parent(); + button.insertBefore(dialogParent.find("span.ui-dialog-title")); + $(button).addClass("widget-title-bar-button"); + } + } else { + if (leftNav.is(":visible") && rightNav.is(":visible")) { + leftNav.remove(); + rightNav.remove(); + this.executedAction = 0; + } + } }, /** @@ -488,23 +498,23 @@ define(function (require) { */ showCloseButton: function (show) { if (show) { - this.$el.parent().find(".ui-dialog-titlebar-close").show(); + this.$el.parent().find(".ui-dialog-titlebar-close").show(); } else { - this.$el.parent().find(".ui-dialog-titlebar-close").hide(); + this.$el.parent().find(".ui-dialog-titlebar-close").hide(); } }, - addButtonToTitleBar: function(button){ - var dialogParent = this.$el.parent(); - dialogParent.find("div.ui-dialog-titlebar").prepend(button); - $(button).addClass("widget-title-bar-button"); + addButtonToTitleBar: function (button) { + var dialogParent = this.$el.parent(); + dialogParent.find("div.ui-dialog-titlebar").prepend(button); + $(button).addClass("widget-title-bar-button"); }, - + addHelpButton: function () { - var that=this; + var that = this; this.addButtonToTitleBar($("
").click(function () { GEPPETTO.ComponentFactory.addComponent('MDMODAL', { - title: that.id.slice(0,-1) + ' help', + title: that.id.slice(0, -1) + ' help', content: that.getHelp(), show: true }, document.getElementById("modal-region")); @@ -518,11 +528,11 @@ define(function (require) { */ setDraggable: function (draggable) { if (draggable) { - this.$el.parent().draggable({disabled: false}); + this.$el.parent().draggable({ disabled: false }); // NOTE: this will wipe any class applied to the widget... this.setClass(''); } else { - this.$el.parent().draggable({disabled: true}); + this.$el.parent().draggable({ disabled: true }); this.setClass('noStyleDisableDrag'); } }, @@ -532,14 +542,14 @@ define(function (require) { * * @param isTransparent */ - setTrasparentBackground: function(isTransparent) { + setTrasparentBackground: function (isTransparent) { this.transparentBackground = isTransparent; - if(isTransparent){ - this.$el.parent().addClass('transparent-back'); - this.previousMaxTransparency = true; + if (isTransparent) { + this.$el.parent().addClass('transparent-back'); + this.previousMaxTransparency = true; } else { - this.$el.parent().removeClass('transparent-back'); + this.$el.parent().removeClass('transparent-back'); } return this; }, @@ -548,27 +558,27 @@ define(function (require) { * Inject CSS for custom behaviour */ setClass: function (className) { - this.$el.dialog({dialogClass: className}).dialogExtend(); + this.$el.dialog({ dialogClass: className }).dialogExtend(); }, /** * Perform a jquery ui effect to the widget */ - perfomEffect: function (effect, options, speed){ + perfomEffect: function (effect, options, speed) { this.$el.parent().effect(effect, options, speed); }, /** * Perform a shake effect to the widget */ - shake: function (options, speed){ - if (options === undefined){ - options = {distance:5, times: 3} + shake: function (options, speed) { + if (options === undefined) { + options = { distance: 5, times: 3 } } - if (speed === undefined){ + if (speed === undefined) { speed = 500 } - + this.$el.parent().effect('shake', options, speed) }, @@ -576,8 +586,8 @@ define(function (require) { * Renders the widget dialog window */ render: function () { - - var that = this; + + var that = this; //create the dialog window for the widget this.dialog = $("
").dialog( @@ -593,64 +603,65 @@ define(function (require) { that.destroy(); } } - }).dialogExtend({"closable" : true, - "maximizable" : true, - "minimizable" : true, - "collapsable" : true, - "restore" : true, + }).dialogExtend({ + "closable": true, + "maximizable": true, + "minimizable": true, + "collapsable": true, + "restore": true, "minimizeLocation": "right", - "icons" : { - "maximize" : "fa fa-window-maximize", - "minimize" : "fa fa-window-minimize", - "collapse" : "fa fa-chevron-circle-up", - "restore" : "fa fa-window-restore", - }, - "load" : function(evt, dlg){ - var icons = $("#"+that.id).parent().find(".ui-icon"); - for(var i =0 ; ionline documentation instead.'; + 'Try the online documentation instead.'; }, - - setController : function(controller){ - this.controller = controller; + + setController: function (controller) { + this.controller = controller; }, - - showHistoryIcon: function(show){ - var that=this; - if(show && this.$el.parent().find(".history-icon").length==0){ + + showHistoryIcon: function (show) { + var that = this; + if (show && this.$el.parent().find(".history-icon").length == 0) { this.addButtonToTitleBar($("
").click(function (event) { that.showHistoryMenu(event); event.stopPropagation(); })); - } - else{ - this.$el.parent().find(".history-icon").remove(); - } + } + else { + this.$el.parent().find(".history-icon").remove(); + } }, /** @@ -714,11 +725,11 @@ define(function (require) { * * @returns {{size: {height: *, width: *}, position: {left: *, top: *}}} */ - getView: function(){ + getView: function () { // get default stuff such as id, position and size return { widgetType: this.widgetType, - isWidget: this.isWidget(), + isWidget: this.isWidget(), showTitleBar: this.showTitleBar, transparentBackground: this.transparentBackground, name: this.name, @@ -738,9 +749,9 @@ define(function (require) { * * @param view */ - setView: function(view){ + setView: function (view) { // set default stuff such as position and size - if(view.size != undefined && view.size.height != 0 && view.size.width != 0){ + if (view.size != undefined && view.size.height != 0 && view.size.width != 0) { this.setSize(view.size.height, view.size.width); } else { // trigger auto size if we have no size info @@ -748,19 +759,19 @@ define(function (require) { this.setAutoHeight(); } - if(view.position != undefined){ + if (view.position != undefined) { this.setPosition(view.position.left, view.position.top); } - if(view.name != undefined){ + if (view.name != undefined) { this.setName(view.name); } - if(view.showTitleBar != undefined){ + if (view.showTitleBar != undefined) { this.showTitleBar(view.showTitleBar); } - if(view.transparentBackground != undefined){ + if (view.transparentBackground != undefined) { this.setTrasparentBackground(view.transparentBackground); } @@ -768,11 +779,11 @@ define(function (require) { this.dirtyView = false; }, - isStateLess(){ + isStateLess() { return this.stateless; }, - isWidget (){ + isWidget() { return true; } }) From 59deb7a8766a74942801d7ecd5b9f00f93780dc4 Mon Sep 17 00:00:00 2001 From: johnidol Date: Mon, 19 Jun 2017 18:05:12 +0100 Subject: [PATCH 087/371] optimize selector --- src/main/webapp/js/pages/tests/casperjs/CoreTests.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/webapp/js/pages/tests/casperjs/CoreTests.js b/src/main/webapp/js/pages/tests/casperjs/CoreTests.js index 781145d47..13e9b94ee 100644 --- a/src/main/webapp/js/pages/tests/casperjs/CoreTests.js +++ b/src/main/webapp/js/pages/tests/casperjs/CoreTests.js @@ -185,9 +185,10 @@ function hhcellTest(test,name){ test.assertEquals(mesh, 1, "Canvas widget has hhcell"); //click on next step for Tutorial - casper.evaluate(function(){ - $(".nextBtn").click(); - $(".nextBtn").click(); + casper.evaluate(function () { + var nextBtnSelector = $(".nextBtn"); + nextBtnSelector.click(); + nextBtnSelector.click(); }); casper.echo("-------Testing Camera Controls--------"); From 1712989e0182bb907d00f038dba75034549784c5 Mon Sep 17 00:00:00 2001 From: jrmartin Date: Mon, 19 Jun 2017 13:10:56 -0700 Subject: [PATCH 088/371] Fix failing tests --- src/main/webapp/js/pages/tests/casperjs/UITests.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/webapp/js/pages/tests/casperjs/UITests.js b/src/main/webapp/js/pages/tests/casperjs/UITests.js index f85b91ca2..a3981fa9b 100644 --- a/src/main/webapp/js/pages/tests/casperjs/UITests.js +++ b/src/main/webapp/js/pages/tests/casperjs/UITests.js @@ -85,7 +85,7 @@ casper.test.begin('Geppetto basic tests', 133, function suite(test) { }); casper.then(function () { - reloadProjectTest(test, TARGET_URL + port+"/org.geppetto.frontend/geppetto?load_project_from_id="+projectID,2); + reloadProjectTest(test, TARGET_URL + port+"/org.geppetto.frontend/geppetto?load_project_from_id="+projectID,1); }); casper.then(function () { @@ -179,7 +179,7 @@ function reloadProjectTest(test, url, customHandlers,widgetCanvasObject){ this.echo("I've waited for "+url+" project to load."); casper.then(function () { - casper.wait(2000, function () {}); + casper.wait(5000, function () {}); test.assertVisible('div#Canvas2', "Canvas2 is correctly open on reload."); test.assertVisible('div#Popup1', "Popup1 is correctly open on reload"); test.assertVisible('div#Connectivity1', "Connectivity1 is correctly open on reload"); @@ -271,7 +271,7 @@ function testProject(test, url, expect_error, persisted, spotlight_record_variab $(".nextBtn").click(); },widgetCanvasObject); - casper.wait(2000, function () {}); + casper.wait(5000, function () {}); this.echo("Checking content of experiment row"); // test or wait for control panel stuff to be there if(this.exists('a[href="#experiments"]')){ From 79ca6bf8583fcb9e04a2c4d9aafe6c94d1d8ff25 Mon Sep 17 00:00:00 2001 From: Matteo Cantarelli Date: Tue, 20 Jun 2017 11:05:47 +0100 Subject: [PATCH 089/371] Fix for panel name --- .../js/communication/geppettoJupyter/GeppettoJupyterGUISync.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/webapp/js/communication/geppettoJupyter/GeppettoJupyterGUISync.js b/src/main/webapp/js/communication/geppettoJupyter/GeppettoJupyterGUISync.js index a99db7320..fefde4762 100644 --- a/src/main/webapp/js/communication/geppettoJupyter/GeppettoJupyterGUISync.js +++ b/src/main/webapp/js/communication/geppettoJupyter/GeppettoJupyterGUISync.js @@ -141,7 +141,7 @@ define(function (require, exports, module) { this.get("component").setPosition(this.get('position_x'), this.get('position_y')); this.get("component").setSize(this.get('height'), this.get('width')); this.get("component").showHistoryIcon(false); - + this.get("component").setName(this.get("widget_name")); }, handle_custom_messages: function (msg) { From 305ceb4ab5f2f507ce891b6441ee22616041c780 Mon Sep 17 00:00:00 2001 From: Matt Earnshaw Date: Tue, 20 Jun 2017 12:07:14 +0100 Subject: [PATCH 090/371] minor refactoring --- .../widgets/connectivity/matrices.js | 85 +++++++++++-------- 1 file changed, 51 insertions(+), 34 deletions(-) diff --git a/src/main/webapp/js/components/widgets/connectivity/matrices.js b/src/main/webapp/js/components/widgets/connectivity/matrices.js index 817c7b8b0..5bc92693f 100644 --- a/src/main/webapp/js/components/widgets/connectivity/matrices.js +++ b/src/main/webapp/js/components/widgets/connectivity/matrices.js @@ -83,41 +83,67 @@ define(function (require) { .attr("width", matrixDim - margin.left - 30) .attr("height", matrixDim - margin.top); - var types = nodes.map(function(x) { return x.id; }); + // we store the 'conn' key in case we want to + // eg. conditionally colour the indicator if there + // are actually connections in that row/column + var pre = nodes.map(function(x,i) { return {id: x.id, conn: (matrix[i].filter((d)=>d.z).length > 0) ? true : false }}); + var matrixT = matrix[0].map(function(col, i) { + return matrix.map(function(row) { + return row[i]; + }) + }); + var post = nodes.map(function(x,i) { return {id: x.id, conn: (matrixT[i].filter((d)=>d.z).length > 0) ? true : false }}); + + var typeIdFromId = function(id) { + return eval(GEPPETTO.ModelFactory.getAllPotentialInstancesEndingWith(id)[0]).getType().getId(); + }; + + var showTooltip = function(msg) { + d3.select(this.parentNode.appendChild(this)).transition().duration(100). + style('stroke-opacity', 1).style('stroke', 'white').style('stroke-width', 2); + d3.select("body").style('cursor', 'pointer'); + return tooltip.transition().duration(100).text(msg); + } + + var hideTooltip = function() { + d3.select(this).transition().duration(100).style('stroke-opacity', 0).style('stroke', 'white'); + d3.select("body").style('cursor', 'default'); + return tooltip.text(""); + }; var popIndicator = function(pos, colormap) { return function(d,i) { d3.select(this).selectAll(".cell") - .data(d) - .enter().append("circle") - .attr("class", "cell") - .attr(pos, function (d, i) { - return x(i); - }) - .attr("r", x.bandwidth()/2) - .attr("title", function (d) { - return d.id; - }) - .style("fill", function (d) { - return colormap( - eval(GEPPETTO.ModelFactory.getAllPotentialInstancesEndingWith(d)[0]) - .getType().getId()); - }) + .data(d) + .enter().append("circle") + .attr("class", "cell") + .attr(pos, function (d, i) { + return x(i); + }) + .attr("r", x.bandwidth()/2) + .attr("title", function (d) { + return d.id; + }) + .style("fill", function (d) { + return colormap(typeIdFromId(d.id)); + }) + .on("mouseover", function(d){ $.proxy(showTooltip, this)(typeIdFromId(d.id)) }) + .on("mouseout", $.proxy(hideTooltip)) }; }; - var prePop = container.selectAll(".prePop") - .data([types]) + var postPop = container.selectAll(".postPop") + .data([post]) .enter() .append("g") - .attr("class", "prePop") + .attr("class", "postPop") .attr("transform", "translate(3,-10)") .each(popIndicator("cx", context.nodeColormap)); - var postPop = container.selectAll(".postPop") - .data([types]) + var prePop = container.selectAll(".prePop") + .data([pre]) .enter() .append("g") - .attr("class", "postPop") + .attr("class", "prePop") .attr("transform", "translate(-10,4)") .each(popIndicator("cy", context.nodeColormap)); @@ -221,19 +247,10 @@ define(function (require) { eval(root.getId() + "." + nodes[d.y].id).select(); eval(root.getId() + "." + nodes[d.y].id).showConnectionLines(false); }) - .on("mouseover", function (d) { - d3.select(this.parentNode.appendChild(this)).transition().duration(100). - style('stroke-opacity', 1).style('stroke', 'white').style('stroke-width', 2); - d3.select("body").style('cursor', 'pointer'); - return tooltip.transition().duration(100).text(nodes[d.y].id + " is connected to " + nodes[d.x].id); - }) - .on("mouseout", function () { - d3.select(this).transition().duration(100).style('stroke-opacity', 0).style('stroke', 'white'); - d3.select("body").style('cursor', 'default'); - return tooltip.text(""); - }); + .on("mouseover", function (d) { $.proxy(showTooltip, this)(nodes[d.y].id + " is connected to " + nodes[d.x].id); }) + .on("mouseout", $.proxy(hideTooltip)); } } } -}); \ No newline at end of file +}); From 70f8dffa952e0e8a89bfe3d727a10f9f82b7514b Mon Sep 17 00:00:00 2001 From: Matt Earnshaw Date: Tue, 20 Jun 2017 14:29:55 +0100 Subject: [PATCH 091/371] transitions --- .../widgets/connectivity/matrices.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/main/webapp/js/components/widgets/connectivity/matrices.js b/src/main/webapp/js/components/widgets/connectivity/matrices.js index 5bc92693f..a5499b9fe 100644 --- a/src/main/webapp/js/components/widgets/connectivity/matrices.js +++ b/src/main/webapp/js/components/widgets/connectivity/matrices.js @@ -205,10 +205,26 @@ define(function (require) { .delay(function (d) { return x(d.x) * 4; }) - .attr("x", function (d) { + .attr("x", function (d) { return x(d.x); }); + t.selectAll(".postPop .cell") + .delay(function (d, i) { + return x(i) * 4; + }) + .attr("cx", function (d, i) { + return x(i); + }); + + t.selectAll(".prePop .cell") + .delay(function (d, i) { + return x(i) * 4; + }) + .attr("cy", function (d, i) { + return x(i); + }); + t.selectAll(".column") .delay(function (d, i) { return x(i) * 4; From 33ca53437d201ced09afa1174b344bed5712c55a Mon Sep 17 00:00:00 2001 From: Matt Earnshaw Date: Tue, 20 Jun 2017 14:53:46 +0100 Subject: [PATCH 092/371] minor refactoring and show populations in legend (not instances) --- .../widgets/connectivity/matrices.js | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/main/webapp/js/components/widgets/connectivity/matrices.js b/src/main/webapp/js/components/widgets/connectivity/matrices.js index a5499b9fe..5f5193a7f 100644 --- a/src/main/webapp/js/components/widgets/connectivity/matrices.js +++ b/src/main/webapp/js/components/widgets/connectivity/matrices.js @@ -18,13 +18,14 @@ define(function (require) { // Colors c = d3.scaleOrdinal(d3.schemeCategory10); - var labelTop = margin.top - 25; + var labelTop = margin.top - 25; + var defaultTooltipText = "Hover the squares to see the connections."; var tooltip = context.svg .append("g") .attr("transform", "translate(" + margin.left + "," + labelTop + ")") .append("text") .attr('class', 'connectionlabel') - .text("Hover the squares to see the connections."); + .text(defaultTooltipText); var container = context.svg .append("g") @@ -98,17 +99,17 @@ define(function (require) { return eval(GEPPETTO.ModelFactory.getAllPotentialInstancesEndingWith(id)[0]).getType().getId(); }; - var showTooltip = function(msg) { + var mouseoverCell = function(msg) { d3.select(this.parentNode.appendChild(this)).transition().duration(100). style('stroke-opacity', 1).style('stroke', 'white').style('stroke-width', 2); d3.select("body").style('cursor', 'pointer'); return tooltip.transition().duration(100).text(msg); } - var hideTooltip = function() { + var mouseoutCell = function() { d3.select(this).transition().duration(100).style('stroke-opacity', 0).style('stroke', 'white'); d3.select("body").style('cursor', 'default'); - return tooltip.text(""); + return tooltip.text(defaultTooltipText); }; var popIndicator = function(pos, colormap) { @@ -127,8 +128,8 @@ define(function (require) { .style("fill", function (d) { return colormap(typeIdFromId(d.id)); }) - .on("mouseover", function(d){ $.proxy(showTooltip, this)(typeIdFromId(d.id)) }) - .on("mouseout", $.proxy(hideTooltip)) + .on("mouseover", function(d){ $.proxy(mouseoverCell, this)(typeIdFromId(d.id)) }) + .on("mouseout", $.proxy(mouseoutCell)) }; }; @@ -170,7 +171,7 @@ define(function (require) { column.append("line") .attr("x1", -context.options.innerWidth); - context.createLegend('legend', c, { x: matrixDim, y: 0 }); + context.createLegend('legend', context.nodeColormap, { x: matrixDim, y: 0 }); //Sorting matrix entries by criteria specified via combobox var orderContainer = $('
', { @@ -263,8 +264,8 @@ define(function (require) { eval(root.getId() + "." + nodes[d.y].id).select(); eval(root.getId() + "." + nodes[d.y].id).showConnectionLines(false); }) - .on("mouseover", function (d) { $.proxy(showTooltip, this)(nodes[d.y].id + " is connected to " + nodes[d.x].id); }) - .on("mouseout", $.proxy(hideTooltip)); + .on("mouseover", function (d) { $.proxy(mouseoverCell, this)(nodes[d.y].id + " is connected to " + nodes[d.x].id); }) + .on("mouseout", $.proxy(mouseoutCell)); } } } From 34fc90684585fe76c77fd40ba441d29fc096901c Mon Sep 17 00:00:00 2001 From: Matt Earnshaw Date: Tue, 20 Jun 2017 14:54:35 +0100 Subject: [PATCH 093/371] formatting --- .../widgets/connectivity/matrices.js | 518 +++++++++--------- 1 file changed, 259 insertions(+), 259 deletions(-) diff --git a/src/main/webapp/js/components/widgets/connectivity/matrices.js b/src/main/webapp/js/components/widgets/connectivity/matrices.js index 5f5193a7f..0faa17dc7 100644 --- a/src/main/webapp/js/components/widgets/connectivity/matrices.js +++ b/src/main/webapp/js/components/widgets/connectivity/matrices.js @@ -3,271 +3,271 @@ */ define(function (require) { - return { - createMatrixLayout: function (context) { - var d3 = require("d3"); - - var margin = { top: 45, right: 10, bottom: 10, left: 15 }; - var legendWidth = 120; - - var matrixDim = (context.options.innerHeight < (context.options.innerWidth - legendWidth)) ? (context.options.innerHeight) : (context.options.innerWidth - legendWidth); - - var x = d3.scaleBand().range([0, matrixDim - margin.top]), - // Opacity - z = d3.scaleLinear().domain([0, 4]).clamp(true), - // Colors - c = d3.scaleOrdinal(d3.schemeCategory10); - - var labelTop = margin.top - 25; - var defaultTooltipText = "Hover the squares to see the connections."; - var tooltip = context.svg - .append("g") - .attr("transform", "translate(" + margin.left + "," + labelTop + ")") - .append("text") - .attr('class', 'connectionlabel') - .text(defaultTooltipText); - - var container = context.svg - .append("g") - .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); - - - var matrix = []; - var nodes = context.dataset.nodes; - var root = context.dataset.root; - var n = nodes.length; - - // Compute index per node. - nodes.forEach(function (node, i) { - node.pre_count = 0; - node.post_count = 0; - matrix[i] = d3.range(n).map(function (j) { - return { x: j, y: i, z: 0 }; - }); + return { + createMatrixLayout: function (context) { + var d3 = require("d3"); + + var margin = { top: 45, right: 10, bottom: 10, left: 15 }; + var legendWidth = 120; + + var matrixDim = (context.options.innerHeight < (context.options.innerWidth - legendWidth)) ? (context.options.innerHeight) : (context.options.innerWidth - legendWidth); + + var x = d3.scaleBand().range([0, matrixDim - margin.top]), + // Opacity + z = d3.scaleLinear().domain([0, 4]).clamp(true), + // Colors + c = d3.scaleOrdinal(d3.schemeCategory10); + + var labelTop = margin.top - 25; + var defaultTooltipText = "Hover the squares to see the connections."; + var tooltip = context.svg + .append("g") + .attr("transform", "translate(" + margin.left + "," + labelTop + ")") + .append("text") + .attr('class', 'connectionlabel') + .text(defaultTooltipText); + + var container = context.svg + .append("g") + .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); + + + var matrix = []; + var nodes = context.dataset.nodes; + var root = context.dataset.root; + var n = nodes.length; + + // Compute index per node. + nodes.forEach(function (node, i) { + node.pre_count = 0; + node.post_count = 0; + matrix[i] = d3.range(n).map(function (j) { + return { x: j, y: i, z: 0 }; + }); + }); + + // Convert links to matrix; count pre / post conns. + context.dataset.links.forEach(function (link) { + //TODO: think about zero weight lines + //matrix[link.source][link.target].z = link.weight ? link.type : 0; + matrix[link.source][link.target].z = link.type; + nodes[link.source].pre_count += 1; + nodes[link.target].post_count += 1; + }); + + //Sorting matrix entries. + //TODO: runtime specified sorting criteria + var sortOptions = { + 'id': 'By entity name', + 'pre_count': 'By # pre', + 'post_count': 'By # post' + }; + // Precompute the orders. + var orders = { + id: d3.range(n).sort(function (a, b) { + return d3.ascending(nodes[a].id, nodes[b].id); + }), + pre_count: d3.range(n).sort(function (a, b) { + return nodes[b].pre_count - nodes[a].pre_count; + }), + post_count: d3.range(n).sort(function (a, b) { + return nodes[b].post_count - nodes[a].post_count; + }), + //community: d3.range(n).sort(function(a, b) { return nodes[b].community - nodes[a].community; }), + }; + // Default sort order. + x.domain(orders.id); + + var rect = container + .append("rect") + .attr("class", "background") + .attr("width", matrixDim - margin.left - 30) + .attr("height", matrixDim - margin.top); + + // we store the 'conn' key in case we want to + // eg. conditionally colour the indicator if there + // are actually connections in that row/column + var pre = nodes.map(function(x,i) { return {id: x.id, conn: (matrix[i].filter((d)=>d.z).length > 0) ? true : false }}); + var matrixT = matrix[0].map(function(col, i) { + return matrix.map(function(row) { + return row[i]; + }) + }); + var post = nodes.map(function(x,i) { return {id: x.id, conn: (matrixT[i].filter((d)=>d.z).length > 0) ? true : false }}); + + var typeIdFromId = function(id) { + return eval(GEPPETTO.ModelFactory.getAllPotentialInstancesEndingWith(id)[0]).getType().getId(); + }; + + var mouseoverCell = function(msg) { + d3.select(this.parentNode.appendChild(this)).transition().duration(100). + style('stroke-opacity', 1).style('stroke', 'white').style('stroke-width', 2); + d3.select("body").style('cursor', 'pointer'); + return tooltip.transition().duration(100).text(msg); + } + + var mouseoutCell = function() { + d3.select(this).transition().duration(100).style('stroke-opacity', 0).style('stroke', 'white'); + d3.select("body").style('cursor', 'default'); + return tooltip.text(defaultTooltipText); + }; + + var popIndicator = function(pos, colormap) { + return function(d,i) { + d3.select(this).selectAll(".cell") + .data(d) + .enter().append("circle") + .attr("class", "cell") + .attr(pos, function (d, i) { + return x(i); + }) + .attr("r", x.bandwidth()/2) + .attr("title", function (d) { + return d.id; + }) + .style("fill", function (d) { + return colormap(typeIdFromId(d.id)); + }) + .on("mouseover", function(d){ $.proxy(mouseoverCell, this)(typeIdFromId(d.id)) }) + .on("mouseout", $.proxy(mouseoutCell)) + }; + }; + + var postPop = container.selectAll(".postPop") + .data([post]) + .enter() + .append("g") + .attr("class", "postPop") + .attr("transform", "translate(3,-10)") + .each(popIndicator("cx", context.nodeColormap)); + var prePop = container.selectAll(".prePop") + .data([pre]) + .enter() + .append("g") + .attr("class", "prePop") + .attr("transform", "translate(-10,4)") + .each(popIndicator("cy", context.nodeColormap)); + + var row = container.selectAll(".row") + .data(matrix) + .enter().append("g") + .attr("class", "row") + .attr("transform", function (d, i) { + return "translate(0," + x(i) + ")"; + }) + .each(row); + + row.append("line") + .attr("x2", context.options.innerWidth); + + var column = container.selectAll(".column") + .data(matrix) + .enter().append("g") + .attr("class", "column") + .attr("transform", function (d, i) { + return "translate(" + x(i) + ")rotate(-90)"; + }); + + column.append("line") + .attr("x1", -context.options.innerWidth); + + context.createLegend('legend', context.nodeColormap, { x: matrixDim, y: 0 }); + + //Sorting matrix entries by criteria specified via combobox + var orderContainer = $('
', { + id: context.id + '-ordering', + style: 'width:' + legendWidth + 'px;left:' + (matrixDim + context.widgetMargin) + 'px;top:' + (matrixDim - 32) + 'px;', + class: 'connectivity-ordering' + }).appendTo(context.connectivityContainer); + + var orderCombo = $(''); - $.each(sortOptions, function (k, v) { - $('