/* * JaamSim Discrete Event Simulation * Copyright (C) 2011 Ausenco Engineering Canada Inc. * Copyright (C) 2016 JaamSim Software Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.jaamsim.ui; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.awt.image.BufferedImage; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.Locale; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JDialog; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.ListSelectionModel; import javax.swing.border.EmptyBorder; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.filechooser.FileNameExtensionFilter; import com.jaamsim.DisplayModels.ColladaModel; import com.jaamsim.DisplayModels.DisplayModel; import com.jaamsim.DisplayModels.ImageModel; import com.jaamsim.DisplayModels.ShapeModel; import com.jaamsim.Graphics.DisplayEntity; import com.jaamsim.basicsim.Entity; import com.jaamsim.controllers.RenderManager; import com.jaamsim.input.Input; import com.jaamsim.input.InputAgent; import com.jaamsim.math.AABB; import com.jaamsim.math.Vec2d; import com.jaamsim.math.Vec3d; import com.jaamsim.render.Future; import com.jaamsim.render.MeshProtoKey; import com.jaamsim.render.RenderUtils; public class GraphicBox extends JDialog { private static GraphicBox myInstance; // only one instance allowed to be open private final JLabel previewLabel; // preview DisplayModel as a picture final ImageIcon previewIcon = new ImageIcon(); private static DisplayEntity currentEntity; private final static JList<DisplayModel> displayModelList; // All defined DisplayModels private File lastDir; // last directory accessed by the file chooser private final JCheckBox useModelSize; private final JCheckBox useModelPosition; static { displayModelList = new JList<>(); } private GraphicBox() { setTitle( "Select DisplayModel" ); setIconImage(GUIFrame.getWindowIcon()); this.setModal(true); // Upon closing do the close method setDefaultCloseOperation( FrameBox.DO_NOTHING_ON_CLOSE ); WindowListener windowListener = new WindowAdapter() { @Override public void windowClosing( WindowEvent e ) { myInstance.close(); } }; this.addWindowListener( windowListener ); // Preview Area previewLabel = new JLabel("", JLabel.CENTER); previewLabel.setBorder(new EmptyBorder(10, 10, 20, 10)); getContentPane().add(previewLabel, "West"); // DisplayModel List JScrollPane scrollPane = new JScrollPane(displayModelList); scrollPane.setBorder(new EmptyBorder(10, 10, 20, 10)); getContentPane().add(scrollPane, "East"); // Update the preview area according to the selected DisplayModel displayModelList.addListSelectionListener( new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { // do this unconditionally to force a repaint and allow for quick outs previewLabel.setIcon(null); // Avoid null pointer exception when the list is being re-populated if(displayModelList.getSelectedIndex() == -1) return; // Selected DisplayModel DisplayModel dm = (DisplayModel)((JList<?>)e.getSource()).getSelectedValue(); if (!RenderManager.isGood()) { return; } Future<BufferedImage> fi = RenderManager.inst().getPreviewForDisplayModel(dm, null); fi.blockUntilDone(); if (fi.failed()) { return; // Something went wrong... } BufferedImage image = RenderUtils.scaleToRes(fi.get(), 180, 180); if(image == null) { return; } previewIcon.setImage(image); previewLabel.setIcon(previewIcon); } } ); // Import button JButton importButton = new JButton("Import"); importButton.addActionListener( new ActionListener() { @Override public void actionPerformed( ActionEvent e ) { if (!RenderManager.isGood()) { return; } // Create a file chooser final JFileChooser chooser = new JFileChooser(lastDir); // Set the file extension filters chooser.setAcceptAllFileFilterUsed(false); FileNameExtensionFilter[] colFilters = ColladaModel.getFileNameExtensionFilters(); FileNameExtensionFilter[] imgFilters = ImageModel.getFileNameExtensionFilters(); chooser.addChoosableFileFilter(colFilters[0]); chooser.addChoosableFileFilter(imgFilters[0]); for (int i = 1; i < colFilters.length; i++) { chooser.addChoosableFileFilter(colFilters[i]); } for (int i = 1; i < imgFilters.length; i++) { chooser.addChoosableFileFilter(imgFilters[i]); } chooser.setFileFilter(colFilters[0]); // Show the file chooser and wait for selection int returnVal = chooser.showDialog(null, "Import"); // Create the selected graphics files if (returnVal == JFileChooser.APPROVE_OPTION) { File f = chooser.getSelectedFile(); lastDir = chooser.getCurrentDirectory(); // Determine the file name and extension String fileName = f.getName(); int i = fileName.lastIndexOf('.'); if (i <= 0 || i >= fileName.length() - 1) { GUIFrame.invokeErrorDialog("Input Error", "File name: %s is invalid.", f.getName()); return; } String extension = fileName.substring(i+1).toLowerCase(); // Set the entity name String entityName = fileName.substring(0, i); entityName = entityName.replaceAll(" ", "_"); // Space is not allowed for Entity Name String modelName = entityName + "-model"; // Create the DisplayModel DisplayModel dm = null; if (ColladaModel.isValidExtension(extension)) { dm = InputAgent.defineEntityWithUniqueName(ColladaModel.class, modelName, "", true); InputAgent.applyArgs(dm, "ColladaFile", f.getPath()); } else if (ImageModel.isValidExtension(extension)) { dm = InputAgent.defineEntityWithUniqueName(ImageModel.class, modelName, "", true); InputAgent.applyArgs(dm, "ImageFile", f.getPath()); } else { GUIFrame.invokeErrorDialog("Input Error", "The extension for file: %s is invalid for both an image and " + "a 3D asset.", fileName); } // Add the new DisplayModel to the List myInstance.refresh(); GUIFrame.updateUI(); // Scroll to the new DisplayModel and ensure it is visible int index = GraphicBox.this.getListIndex(dm); displayModelList.setSelectedIndex(index); displayModelList.ensureIndexIsVisible(index); } } } ); // Accept button JButton acceptButton = new JButton("Accept"); acceptButton.addActionListener( new ActionListener() { @Override public void actionPerformed( ActionEvent e ) { setEnabled(false); // Don't accept any interaction DisplayModel dm = displayModelList.getSelectedValue(); InputAgent.applyArgs(currentEntity, "DisplayModel", dm.getName()); if (!RenderManager.isGood()) { myInstance.close(); } Locale loc = null; AABB modelBounds = new AABB(); if (dm instanceof ColladaModel) { ColladaModel dmc = (ColladaModel)dm; MeshProtoKey key = RenderUtils.FileNameToMeshProtoKey(dmc.getColladaFile()); modelBounds = RenderManager.inst().getMeshBounds(key, true); Vec3d modelSize = new Vec3d(modelBounds.radius); modelSize.scale3(2); Vec3d entitySize = currentEntity.getSize(); double longestSide = modelSize.x; //double ratio = dm.getConversionFactorToMeters(); double ratio = 1; if(! useModelSize.isSelected()) { ratio = entitySize.x/modelSize.x; if(modelSize.y > longestSide) { ratio = entitySize.y/modelSize.y; longestSide = modelSize.y; } if(modelSize.z > longestSide) { ratio = entitySize.z/modelSize.z; } } entitySize = new Vec3d(modelSize); entitySize.scale3(ratio); InputAgent.applyArgs(currentEntity, "Size", String.format(loc, "%.6f", entitySize.x), String.format(loc, "%.6f", entitySize.y), String.format(loc, "%.6f", entitySize.z), "m"); } if (dm instanceof ImageModel) { ImageModel im = (ImageModel)dm; Vec2d imageDims = RenderManager.inst().getImageDims(im.getImageFile()); if (imageDims != null && useModelSize.isSelected()) { // Keep the y size the same, but use the image's proportions. We can't really use the model size, as it is in pixels double scale = currentEntity.getSize().y / imageDims.y; InputAgent.applyArgs(currentEntity, "Size", String.format(loc, "%.6f", imageDims.x * scale), String.format(loc, "%.6f", imageDims.y * scale), "1.0", "m"); } } if (useModelPosition.isSelected()) { Vec3d entityPos = modelBounds.center; InputAgent.applyArgs(currentEntity, "Position", String.format(loc, "%.6f", entityPos.x), String.format(loc, "%.6f", entityPos.y), String.format(loc, "%.6f", entityPos.z), "m"); InputAgent.applyArgs(currentEntity, "Alignment", "0", "0", "0"); } GUIFrame.updateUI(); myInstance.close(); } } ); // Cancel button JButton cancelButton = new JButton("Cancel"); cancelButton.addActionListener( new ActionListener() { @Override public void actionPerformed( ActionEvent e ) { myInstance.close(); } } ); // CheckBoxes JPanel checkBoxPanel = new JPanel(new BorderLayout()); useModelSize = new JCheckBox("Use Display Model Size"); useModelSize.setSelected(true); useModelPosition = new JCheckBox("Keep Model Position"); useModelPosition.setSelected(false); checkBoxPanel.add(useModelSize, BorderLayout.PAGE_START); checkBoxPanel.add(useModelPosition, BorderLayout.PAGE_END); // Button Panel JPanel buttonPanel = new JPanel(); buttonPanel.setLayout( new FlowLayout(FlowLayout.CENTER) ); buttonPanel.add(importButton); buttonPanel.add(checkBoxPanel); buttonPanel.add(acceptButton); buttonPanel.add(cancelButton); getContentPane().add(buttonPanel, BorderLayout.SOUTH); this.pack(); } public static GraphicBox getInstance( DisplayEntity ent, int x, int y ) { currentEntity = ent; Point pos = new Point(x, y); // Has the Graphic Box been created? if (myInstance == null) { myInstance = new GraphicBox(); myInstance.setMinimumSize(new Dimension(400, 300)); } // Position of the GraphicBox pos.setLocation(pos.x + myInstance.getSize().width /2 , pos.y + myInstance.getSize().height /2); myInstance.setLocation(pos.x, pos.y); myInstance.refresh(); myInstance.setEnabled(true); return myInstance; } private void close() { currentEntity = null; setVisible(false); } @Override public void dispose() { super.dispose(); myInstance = null; } private void refresh() { // Prepare a sorted array of all the DisplayModels ArrayList<DisplayModel> models = new ArrayList<>(); for (DisplayModel each : Entity.getClonesOfIterator(DisplayModel.class)) { if (each instanceof ImageModel || each instanceof ColladaModel || each instanceof ShapeModel) models.add(each); } Collections.sort(models, Input.uiSortOrder); DisplayModel[] displayModels = new DisplayModel[models.size()]; for (int i = 0; i < models.size(); i++) { displayModels[i] = models.get(i); } displayModelList.setListData(displayModels); displayModelList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); // Select the present DisplayModel int index = this.getListIndex(currentEntity.getDisplayModelList().get(0)); displayModelList.setSelectedIndex(index); displayModelList.ensureIndexIsVisible(index); } private int getListIndex(DisplayModel dm) { for (int i=0; i<displayModelList.getModel().getSize(); i++) { if (displayModelList.getModel().getElementAt(i) == dm) { return i; } } return -1; } }