| /******************************************************************************* |
| * Copyright (c) 2018 Agence spatiale canadienne / Canadian Space Agency. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Pierre Allard, |
| * Regent L'Archeveque, |
| * Olivier L. Larouche - initial API and implementation |
| * |
| * SPDX-License-Identifier: EPL-1.0 |
| *******************************************************************************/ |
| package org.eclipse.apogy.core.topology.ui.parts; |
| |
| import java.awt.image.BufferedImage; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import javax.inject.Inject; |
| |
| import org.eclipse.apogy.common.ApogyCommonOSGiUtilities; |
| import org.eclipse.apogy.common.emf.FeaturePathAdapter; |
| import org.eclipse.apogy.common.emf.impl.FeaturePathAdapterCustomImpl; |
| import org.eclipse.apogy.common.images.AbstractEImage; |
| import org.eclipse.apogy.common.images.ApogyCommonImagesFactory; |
| import org.eclipse.apogy.common.images.EImage; |
| import org.eclipse.apogy.common.images.EImagesUtilities; |
| import org.eclipse.apogy.common.topology.AbstractViewPoint; |
| import org.eclipse.apogy.common.topology.AbstractViewPointReference; |
| import org.eclipse.apogy.common.topology.ApogyCommonTopologyFactory; |
| import org.eclipse.apogy.common.topology.Node; |
| import org.eclipse.apogy.common.topology.ui.ApogyCommonTopologyUIFacade; |
| import org.eclipse.apogy.common.topology.ui.GraphicsContext; |
| import org.eclipse.apogy.common.topology.ui.NodeSelection; |
| import org.eclipse.apogy.common.topology.ui.jme3.JME3RenderEngineDelegate; |
| import org.eclipse.apogy.common.topology.ui.viewer.ApogyCommonTopologyUIViewerPackage; |
| import org.eclipse.apogy.common.topology.ui.viewer.TopologyViewer; |
| import org.eclipse.apogy.common.topology.ui.viewer.TopologyViewerProvider; |
| import org.eclipse.apogy.common.topology.ui.viewer.TopologyViewerRegistry; |
| import org.eclipse.apogy.common.ui.composites.NoContentComposite; |
| import org.eclipse.apogy.core.ApogyTopology; |
| import org.eclipse.apogy.core.environment.AbstractApogyEnvironmentItem; |
| import org.eclipse.apogy.core.environment.ApogyEnvironment; |
| import org.eclipse.apogy.core.environment.ViewPointList; |
| import org.eclipse.apogy.core.invocator.ApogyCoreInvocatorFacade; |
| import org.eclipse.apogy.core.invocator.ApogyCoreInvocatorPackage; |
| import org.eclipse.apogy.core.invocator.ui.ApogyCoreInvocatorUIFacade; |
| import org.eclipse.apogy.core.topology.ApogyCoreTopologyFacade; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.e4.ui.model.application.ui.advanced.MPerspective; |
| import org.eclipse.e4.ui.model.application.ui.basic.MPart; |
| import org.eclipse.e4.ui.workbench.modeling.EModelService; |
| import org.eclipse.emf.common.notify.Adapter; |
| import org.eclipse.emf.common.notify.Notification; |
| import org.eclipse.emf.common.notify.impl.AdapterImpl; |
| import org.eclipse.emf.common.util.EList; |
| import org.eclipse.emf.ecore.EStructuralFeature; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.FileDialog; |
| import org.eclipse.swt.widgets.MessageBox; |
| import org.eclipse.swt.widgets.Shell; |
| import org.eclipse.ui.progress.UIJob; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| public abstract class AbstractApogy3dPart extends AbstractApogyTopologyBasedPart implements TopologyViewerProvider { |
| private static final Logger Logger = LoggerFactory.getLogger(AbstractApogy3dPart.class); |
| |
| protected static MPart activeAbstractApogy3dPartMPart = null; |
| |
| public static final String VIEW_POINT_ID = "VIEW_POINT_ID"; |
| private AbstractViewPoint abstractViewPoint; |
| private Node topologyRoot; |
| |
| private String currentDirectory = System.getProperty("user.dir"); |
| protected TopologyViewer topologyViewer; |
| private Adapter topologyTopologyViewerRegistryAdapter = null; |
| |
| private FeaturePathAdapter variableInstanciationAdapter = null; |
| |
| @Inject |
| Shell shell; |
| |
| @Inject |
| EModelService modelService; |
| |
| @Override |
| public void userPostConstruct(MPart mPart) { |
| // Reset the ViewPoint. |
| AbstractViewPointReference ref = (AbstractViewPointReference) ApogyCoreInvocatorUIFacade.INSTANCE |
| .readFromPersistedState(mPart, VIEW_POINT_ID); |
| if (ref != null) { |
| this.abstractViewPoint = ref.getAbstractViewPoint(); |
| } else { |
| mPart.setLabel(getPartDisplayedName()); |
| } |
| |
| // Calls variablesInstanciated is variable are already instantiated. |
| if (ApogyCoreInvocatorFacade.INSTANCE.getInitVariableInstancesDate() != null) { |
| variablesInstanciated(); |
| } |
| |
| // Attaches listener to variable instantiation. |
| getVariableInstanciationAdapter().init(ApogyCoreInvocatorFacade.INSTANCE); |
| |
| } |
| |
| @Override |
| public void userPreDestroy(MPart mPart) { |
| |
| getVariableInstanciationAdapter().dispose(); |
| super.userPreDestroy(mPart); |
| } |
| |
| @Override |
| protected void createContentComposite(Composite parent, int style) { |
| if (activeAbstractApogy3dPartMPart == null) { |
| JME3RenderEngineDelegate jme3RenderEngineDelegate = createJME3RenderEngineDelegate(); |
| this.topologyViewer = new TopologyViewer(parent, jme3RenderEngineDelegate); |
| |
| jme3RenderEngineDelegate.setMaximumFrameRate(this.topologyViewer.getMaximumFrameRate()); |
| jme3RenderEngineDelegate.setAntiAliasing(this.topologyViewer.isAntiAliasingEnabled()); |
| jme3RenderEngineDelegate.setShowStatisticsEnabled(this.topologyViewer.isShowStatisticsEnabled()); |
| |
| // Listens for selection in the 3D viewer. |
| org.eclipse.apogy.common.topology.ui.viewer.Activator.getTopologyViewerRegistry().eAdapters() |
| .add(getTopologyTopologyViewerRegistryAdapter()); |
| |
| setActiveViewPoint(this.abstractViewPoint); |
| |
| activeAbstractApogy3dPartMPart = this.mPart; |
| } else { |
| new NoContentComposite(parent, SWT.None) { |
| @Override |
| protected String getMessage() { |
| MPerspective mPerspective = AbstractApogy3dPart.this.modelService |
| .getPerspectiveFor(activeAbstractApogy3dPartMPart); |
| |
| String partLabel = activeAbstractApogy3dPartMPart.getLabel(); |
| String elementId = mPerspective.getLabel(); |
| |
| return "3D Viewer is already instanciated in part <" + partLabel + "> in the perspective named <" |
| + elementId + "> !"; |
| } |
| }; |
| } |
| } |
| |
| @Override |
| public void userPersistState(MPart mPart) { |
| try { |
| if (this.abstractViewPoint != null) { |
| AbstractViewPointReference ref = ApogyCommonTopologyFactory.eINSTANCE |
| .createAbstractViewPointReference(); |
| ref.setAbstractViewPoint(this.abstractViewPoint); |
| ApogyCoreInvocatorUIFacade.INSTANCE.saveToPersistedState(mPart, VIEW_POINT_ID, ref); |
| } |
| } catch (Throwable t) { |
| Logger.error(t.getMessage(), t); |
| } |
| |
| } |
| |
| @Override |
| public TopologyViewer getTopologyViewer() { |
| return this.topologyViewer; |
| } |
| |
| /** |
| * Return the lable prefic of the part. |
| * |
| * @return |
| */ |
| public String getPartDisplayedName() { |
| return "3D Viewer"; |
| } |
| |
| /** |
| * Method called when the variable are instanciated. |
| */ |
| protected void variablesInstanciated() { |
| // Initialize the viewPoints. |
| initialiseViewPoints(this.topologyRoot); |
| } |
| |
| @Override |
| protected void newTopology(ApogyTopology apogyTopology) { |
| if (activeAbstractApogy3dPartMPart == this.mPart) { |
| GraphicsContext graphicsContext = ApogyCommonTopologyUIFacade.INSTANCE |
| .createGraphicsContext(apogyTopology.getRootNode()); |
| this.topologyViewer.setInput(graphicsContext); |
| |
| if (apogyTopology != null) { |
| this.topologyRoot = apogyTopology.getRootNode(); |
| } |
| |
| // Initialize the view points. |
| initialiseViewPoints(this.topologyRoot); |
| |
| // Reset the current ViewPoint. |
| AbstractViewPointReference ref = (AbstractViewPointReference) ApogyCoreInvocatorUIFacade.INSTANCE |
| .readFromPersistedState(this.mPart, VIEW_POINT_ID); |
| if (ref != null) { |
| this.abstractViewPoint = ref.getAbstractViewPoint(); |
| setActiveViewPoint(this.abstractViewPoint); |
| } |
| } |
| } |
| |
| @Override |
| public void dispose() { |
| // Unregister listener to 3D selection. |
| org.eclipse.apogy.common.topology.ui.viewer.Activator.getTopologyViewerRegistry().eAdapters() |
| .remove(getTopologyTopologyViewerRegistryAdapter()); |
| |
| // Calls super |
| super.dispose(); |
| |
| if (activeAbstractApogy3dPartMPart == this.mPart) { |
| activeAbstractApogy3dPartMPart = null; |
| } |
| } |
| |
| /* |
| * Methods that create the JME3RenderEngineDelegate to be used. |
| * |
| * @return The JME3RenderEngineDelegate to be used. |
| */ |
| protected abstract JME3RenderEngineDelegate createJME3RenderEngineDelegate(); |
| |
| /* |
| * Sets the actual zoom to display the entire loaded scene. |
| */ |
| public void zoomToFit() { |
| getTopologyViewer().zoomToFit(); |
| } |
| |
| /* |
| * Enables or disables anti-aliasing rendering. |
| * |
| * @param enable true enables the anti-aliasing. |
| */ |
| public void setAntiAliasing(boolean enable) { |
| getTopologyViewer().setAntiAliasing(enable); |
| } |
| |
| /* |
| * Enables or disables high speed motion while using the mouse. |
| * |
| * @param enable true enables high speed. |
| */ |
| public void setHighSpeedMode(boolean enable) { |
| getTopologyViewer().setHighSpeedMotionEnabled(enable); |
| } |
| |
| /* |
| * Enables or disables the picking mode. |
| * |
| * @param enable true enables the picking mode. |
| */ |
| public void setPickingMode(boolean enable) { |
| getTopologyViewer().setPickingModeEnabled(enable); |
| } |
| |
| /* |
| * Shows of hides the renderer statistics. |
| * |
| * @param enable true enables the statistics display. |
| */ |
| public void showStatisticsDisplay(boolean enable) { |
| getTopologyViewer().setShowStatisticsEnabled(enable); |
| } |
| |
| /* |
| * Resets the horizontal view point. |
| */ |
| public void resetLevelViewpoint() { |
| getTopologyViewer().levelViewPoint(); |
| } |
| |
| /* |
| * Takes a screenshot and prompts the user to identify the file type and the |
| * filename. |
| */ |
| public void takeScreenshot() { |
| getTopologyViewer().takeScreenshot(); |
| |
| BufferedImage image = getTopologyViewer().takeScreenshot(); |
| if (image != null) { |
| EImage eImage = ApogyCommonImagesFactory.eINSTANCE.createEImage(); |
| eImage.setImageContent(image); |
| |
| if (eImage != null) { |
| // Open pop-up and save. |
| FileDialog fileChooser = new FileDialog(this.shell, SWT.SAVE); |
| fileChooser.setText("Saves image to file"); |
| fileChooser.setFilterPath(this.currentDirectory); |
| fileChooser.setFilterExtensions(new String[] { "*.png;" }); |
| String filename = fileChooser.open(); |
| if (filename != null) { |
| try { |
| if (filename.endsWith(".png")) { |
| saveImageInJob(eImage, filename, SWT.IMAGE_PNG); |
| } else { |
| String fileExtension = ""; |
| if (filename.lastIndexOf(".") > 0) { |
| fileExtension = filename.substring(filename.lastIndexOf(".")); |
| } |
| String message = "Failed to save the image. The specified file extension <" + fileExtension |
| + "> is not supported."; |
| |
| Logger.error(message); |
| |
| MessageBox messageBox = new MessageBox(this.shell, SWT.ICON_ERROR | SWT.OK); |
| messageBox.setMessage(message); |
| messageBox.open(); |
| } |
| } catch (Exception e) { |
| Logger.error("Unable to save image to file <" + filename + ">."); |
| } |
| this.currentDirectory = fileChooser.getFilterPath(); |
| } |
| } |
| } |
| |
| } |
| |
| /** |
| * Sets the currently active View Point |
| * |
| * @param abstractViewPoint The view point to be displayed. |
| */ |
| public void setActiveViewPoint(AbstractViewPoint abstractViewPoint) { |
| this.abstractViewPoint = abstractViewPoint; |
| |
| if (abstractViewPoint != null) { |
| this.mPart.setLabel(getPartDisplayedName() + " - " + abstractViewPoint.getName()); |
| |
| if (ApogyCoreTopologyFacade.INSTANCE.getApogyTopology() != null) { |
| Node root = ApogyCoreTopologyFacade.INSTANCE.getApogyTopology().getRootNode(); |
| abstractViewPoint.initialize(root); |
| } |
| } else { |
| this.mPart.setLabel(getPartDisplayedName()); |
| } |
| |
| if (getTopologyViewer() != null && abstractViewPoint != null) { |
| getTopologyViewer().setActiveViewPoint(abstractViewPoint); |
| } |
| } |
| |
| /** |
| * Returns the currently active View Point |
| * |
| * @return The view point displayed, can be null. |
| */ |
| public AbstractViewPoint getActiveViewPoint() { |
| return getTopologyViewer().getActiveViewPoint(); |
| } |
| |
| /** |
| * Method that ensures the viewpoint in the list are initialized and the |
| * selected one properly displayed. |
| * |
| * @param root The root of the topology being displayed. |
| */ |
| protected void initialiseViewPoints(Node root) { |
| if (ApogyCoreInvocatorFacade.INSTANCE.getActiveInvocatorSession() != null) { |
| if (ApogyCoreInvocatorFacade.INSTANCE.getActiveInvocatorSession() |
| .getEnvironment() instanceof ApogyEnvironment) { |
| ApogyEnvironment apogyEnvironment = (ApogyEnvironment) ApogyCoreInvocatorFacade.INSTANCE |
| .getActiveInvocatorSession().getEnvironment(); |
| EList<AbstractApogyEnvironmentItem> items = apogyEnvironment.getEnvironmentItems(); |
| ViewPointList viewPointList = null; |
| for (AbstractApogyEnvironmentItem item : items) { |
| if (item instanceof ViewPointList) { |
| viewPointList = (ViewPointList) item; |
| } |
| } |
| |
| // If ViewPointList has been found. |
| if (viewPointList != null) { |
| for (AbstractViewPoint vp : viewPointList.getViewPoints()) { |
| vp.initialize(root); |
| } |
| } |
| } |
| |
| // Forces update of the current viewpoint. |
| setActiveViewPoint(this.abstractViewPoint); |
| |
| } |
| } |
| |
| /* |
| * Saves the current image to a file. This is done in a UIJob to keep the |
| * desktop responsive in case of a long save process. |
| * |
| * @param fileName The destination file where to save the image. |
| * |
| * @param fileType The file type : SWT.IMAGE_JPEG or SWT.IMAGE_BMP or ... |
| */ |
| private void saveImageInJob(final AbstractEImage image, final String fileName, final int fileType) { |
| UIJob job = new UIJob("Save Image to file <" + fileName + ">") { |
| @Override |
| public IStatus runInUIThread(IProgressMonitor monitor) { |
| try { |
| // Save image. |
| if (fileType == SWT.IMAGE_JPEG) { |
| EImagesUtilities.INSTANCE.saveImageAsJPEG(fileName, image); |
| } else if (fileType == SWT.IMAGE_PNG) { |
| EImagesUtilities.INSTANCE.saveImageAsPNG(fileName, image); |
| } |
| |
| Logger.info("Sucessfully saved image to file <" + fileName + ">."); |
| |
| Status status = new Status(IStatus.OK, ApogyCommonOSGiUtilities.INSTANCE.getBundleSymbolicName(this.getClass()), |
| "Sucessfully saved image to file <" + fileName + ">."); |
| return status; |
| } catch (Exception e) { |
| String message = "Failed to save the image to file <" + fileName |
| + ">. The following exception was thrown : \n\n "; |
| |
| Logger.error(message, e); |
| Status status = new Status(IStatus.ERROR, ApogyCommonOSGiUtilities.INSTANCE.getBundleSymbolicName(this.getClass()), message, e); |
| return status; |
| } |
| } |
| }; |
| job.schedule(); |
| } |
| |
| private Adapter getTopologyTopologyViewerRegistryAdapter() { |
| if (this.topologyTopologyViewerRegistryAdapter == null) { |
| this.topologyTopologyViewerRegistryAdapter = new AdapterImpl() { |
| @Override |
| public void notifyChanged(Notification msg) { |
| if (msg.getNotifier() instanceof TopologyViewerRegistry) { |
| int featureId = msg.getFeatureID(TopologyViewerRegistry.class); |
| |
| switch (featureId) { |
| case ApogyCommonTopologyUIViewerPackage.TOPOLOGY_VIEWER_REGISTRY__LATEST_NODE_SELECTION: |
| |
| if (msg.getNewValue() instanceof NodeSelection) { |
| NodeSelection nodeSelection = (NodeSelection) msg.getNewValue(); |
| AbstractApogy3dPart.this.selectionService.setSelection(nodeSelection); |
| } |
| |
| break; |
| |
| default: |
| break; |
| } |
| } |
| } |
| }; |
| } |
| |
| return this.topologyTopologyViewerRegistryAdapter; |
| } |
| |
| private FeaturePathAdapter getVariableInstanciationAdapter() { |
| if (this.variableInstanciationAdapter == null) { |
| this.variableInstanciationAdapter = new FeaturePathAdapterCustomImpl() { |
| @Override |
| public List<? extends EStructuralFeature> getFeatureList() { |
| List<EStructuralFeature> features = new ArrayList<>(); |
| features.add( |
| ApogyCoreInvocatorPackage.Literals.APOGY_CORE_INVOCATOR_FACADE__INIT_VARIABLE_INSTANCES_DATE); |
| return features; |
| } |
| |
| @Override |
| public void notifyChanged(Notification msg) { |
| variablesInstanciated(); |
| } |
| }; |
| |
| } |
| return this.variableInstanciationAdapter; |
| } |
| |
| } |