| /******************************************************************************* |
| * 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 - 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.HashMap; |
| |
| import javax.inject.Inject; |
| |
| import org.eclipse.apogy.common.ApogyCommonOSGiUtilities; |
| import org.eclipse.apogy.common.emf.ui.parts.AbstractEObjectSelectionPart; |
| 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.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.JME3Application; |
| 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.ApogySystem; |
| import org.eclipse.apogy.core.topology.ui.ApogyCoreTopologyUIFacade; |
| import org.eclipse.apogy.core.topology.ui.ApogyCoreTopologyUIPackage; |
| import org.eclipse.apogy.core.topology.ui.ApogyCoreTopologyUIRCPConstants; |
| import org.eclipse.apogy.core.topology.ui.DisplayedTopology; |
| import org.eclipse.apogy.core.topology.ui.DisplayedTopologyChoice; |
| 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.basic.MPart; |
| import org.eclipse.e4.ui.workbench.modeling.ISelectionListener; |
| 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.ecore.EObject; |
| 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; |
| |
| import com.jme3.math.ColorRGBA; |
| |
| public class ApogySystem3dPart extends AbstractEObjectSelectionPart implements TopologyViewerProvider { |
| private static final Logger Logger = LoggerFactory.getLogger(ApogySystem3dPart.class); |
| |
| public static ColorRGBA VIEWER_BACKGROUND_COLOR = new ColorRGBA(145.0f / 255.0f, 233.0f / 255.0f, 1f, 1f); |
| |
| protected ApogySystem apogySystem; |
| protected DisplayedTopology displayedTopology = DisplayedTopology.ASSEMBLY; |
| |
| protected TopologyViewer topologyViewer; |
| |
| private Adapter topologyTopologyViewerRegistryAdapter = null; |
| private Adapter editedApogySystemAssemblyRootAdapter = null; |
| |
| private String currentDirectory = System.getProperty("user.dir"); |
| |
| @Inject |
| Shell shell; |
| |
| @Override |
| public TopologyViewer getTopologyViewer() { |
| return this.topologyViewer; |
| } |
| |
| /* |
| * 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(); |
| } |
| } |
| } |
| } |
| |
| @Override |
| protected void setCompositeContents(EObject eObject) { |
| if (eObject instanceof ApogySystem) { |
| setApogySystem((ApogySystem) eObject); |
| } else if (eObject instanceof DisplayedTopologyChoice) { |
| DisplayedTopologyChoice displayedTopologyChoice = (DisplayedTopologyChoice) eObject; |
| setDisplayedTopology(displayedTopologyChoice.getDisplayedTopology()); |
| } |
| } |
| |
| @Override |
| protected HashMap<String, ISelectionListener> getSelectionProvidersIdsToSelectionListeners() { |
| HashMap<String, ISelectionListener> selectionProvidersIdsToSelectionListeners = new HashMap<String, ISelectionListener>(); |
| |
| // Selection from Apogy System File Editor |
| selectionProvidersIdsToSelectionListeners.put(ApogyCoreTopologyUIRCPConstants.PART__APOGY_SYSTEM_FILE_EDITOR_ID, |
| new ISelectionListener() { |
| |
| @Override |
| public void selectionChanged(MPart mPart, Object object) { |
| if (object != null && object instanceof EObject) { |
| setEObject((EObject) object); |
| } |
| } |
| }); |
| |
| selectionProvidersIdsToSelectionListeners |
| .put(ApogyCoreTopologyUIRCPConstants.PART_APOGY_SYSTEM_TOPOLOGY_EDITOR_ID, new ISelectionListener() { |
| |
| @Override |
| public void selectionChanged(MPart mPart, Object object) { |
| if (object != null && object instanceof EObject) { |
| setEObject((EObject) object); |
| } |
| } |
| }); |
| |
| return selectionProvidersIdsToSelectionListeners; |
| } |
| |
| @Override |
| public void userPostConstruct(MPart mPart) { |
| ApogyCoreTopologyUIFacade.INSTANCE.eAdapters().add(getEditedApogySystemAssemblyRootAdapter()); |
| super.userPostConstruct(mPart); |
| } |
| |
| @Override |
| public void userPreDestroy(MPart mPart) { |
| ApogyCoreTopologyUIFacade.INSTANCE.eAdapters().remove(getEditedApogySystemAssemblyRootAdapter()); |
| super.userPreDestroy(mPart); |
| } |
| |
| protected void setApogySystem(ApogySystem newApogySystem) { |
| if (this.apogySystem != newApogySystem) { |
| this.apogySystem = newApogySystem; |
| updateDisplayedTopology(); |
| } |
| } |
| |
| protected void setDisplayedTopology(DisplayedTopology newDisplayedTopology) { |
| if (this.displayedTopology != newDisplayedTopology) { |
| this.displayedTopology = newDisplayedTopology; |
| updateDisplayedTopology(); |
| } |
| } |
| |
| private void updateDisplayedTopology() { |
| Node root = null; |
| switch (this.displayedTopology.getValue()) { |
| case DisplayedTopology.SYSTEM_VALUE: { |
| if (this.apogySystem != null && this.apogySystem.getTopologyRoot() != null) { |
| root = this.apogySystem.getTopologyRoot().getOriginNode(); |
| } |
| } |
| break; |
| |
| case DisplayedTopology.ASSEMBLY_VALUE: { |
| root = ApogyCoreTopologyUIFacade.INSTANCE.getEditedApogySystemAssemblyRoot(); |
| } |
| break; |
| |
| default: |
| break; |
| } |
| |
| if (root == null) { |
| root = ApogyCommonTopologyFactory.eINSTANCE.createAggregateContentNode(); |
| } |
| |
| if (this.topologyViewer != null) { |
| GraphicsContext graphicsContext = ApogyCommonTopologyUIFacade.INSTANCE.createGraphicsContext(root); |
| this.topologyViewer.setInput(graphicsContext); |
| } |
| } |
| |
| @Override |
| protected void createNoContentComposite(Composite parent, int style) { |
| new NoContentComposite(parent, SWT.None) { |
| @Override |
| protected String getMessage() { |
| return "No Apogy System Topology to display."; |
| } |
| }; |
| } |
| |
| @Override |
| protected void createContentComposite(Composite parent, int style) { |
| JME3RenderEngineDelegate jme3RenderEngineDelegate = createJME3RenderEngineDelegate(); |
| this.topologyViewer = new TopologyViewer(parent, jme3RenderEngineDelegate); |
| |
| jme3RenderEngineDelegate.setMaximumFrameRate(this.topologyViewer.getMaximumFrameRate()); |
| jme3RenderEngineDelegate.setAntiAliasing(this.topologyViewer.isAntiAliasingEnabled()); |
| jme3RenderEngineDelegate.setShowStatisticsEnabled(this.topologyViewer.isShowStatisticsEnabled()); |
| jme3RenderEngineDelegate.setAmbientLightEnabled(true); |
| |
| // Listens for selection in the 3D viewer. |
| org.eclipse.apogy.common.topology.ui.viewer.Activator.getTopologyViewerRegistry().eAdapters() |
| .add(getTopologyTopologyViewerRegistryAdapter()); |
| } |
| |
| protected JME3RenderEngineDelegate createJME3RenderEngineDelegate() { |
| JME3RenderEngineDelegate engineDelegate = new JME3RenderEngineDelegate() { |
| @Override |
| protected JME3Application createJME3Application(Composite parent) { |
| |
| JME3Application app = new JME3Application(parent) { |
| @Override |
| public void simpleInitApp() { |
| super.simpleInitApp(); |
| |
| // Sets the far clipping plane to 200m. |
| getCamera().setFrustumFar(100f); |
| |
| getViewPort().setBackgroundColor(VIEWER_BACKGROUND_COLOR); |
| } |
| |
| @Override |
| protected void initLighting() { |
| super.initLighting(); |
| } |
| }; |
| app.setJMERenderEngineDelegate(this); |
| return app; |
| } |
| }; |
| |
| return engineDelegate; |
| } |
| |
| protected Adapter getEditedApogySystemAssemblyRootAdapter() { |
| if (this.editedApogySystemAssemblyRootAdapter == null) { |
| this.editedApogySystemAssemblyRootAdapter = new AdapterImpl() { |
| @Override |
| public void notifyChanged(Notification msg) { |
| if (msg.getNotifier() instanceof ApogyCoreTopologyUIFacade) { |
| int featureId = msg.getFeatureID(ApogyCoreTopologyUIFacade.class); |
| if (featureId == ApogyCoreTopologyUIPackage.APOGY_CORE_TOPOLOGY_UI_FACADE__EDITED_APOGY_SYSTEM_ASSEMBLY_ROOT) { |
| updateDisplayedTopology(); |
| } |
| } |
| } |
| }; |
| } |
| return this.editedApogySystemAssemblyRootAdapter; |
| } |
| |
| 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(); |
| ApogySystem3dPart.this.selectionService.setSelection(nodeSelection); |
| } |
| |
| break; |
| |
| default: |
| break; |
| } |
| } |
| } |
| }; |
| } |
| |
| return this.topologyTopologyViewerRegistryAdapter; |
| } |
| |
| /* |
| * 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(ApogySystem3dPart.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 " + e.getMessage(); |
| |
| Logger.error(message, e); |
| Status status = new Status(IStatus.ERROR, ApogyCommonOSGiUtilities.INSTANCE.getBundleSymbolicName(ApogySystem3dPart.this.getClass()), message, e); |
| return status; |
| } |
| } |
| }; |
| job.schedule(); |
| } |
| } |