/* -*- tab-width: 4 -*- * * Electric(tm) VLSI Design System * * File: View3DWindow.java * Written by Gilda Garreton, Sun Microsystems. * * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. * * Electric(tm) is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * Electric(tm) is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Electric(tm); see the file COPYING. If not, write to * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, Mass 02111-1307, USA. */ package com.sun.electric.plugins.j3d; import com.sun.electric.Main; import com.sun.electric.database.EditingPreferences; import com.sun.electric.database.Environment; import com.sun.electric.database.change.DatabaseChangeEvent; import com.sun.electric.database.change.DatabaseChangeListener; import com.sun.electric.database.geometry.Poly; import com.sun.electric.database.hierarchy.Cell; import com.sun.electric.database.hierarchy.HierarchyEnumerator; import com.sun.electric.database.hierarchy.Nodable; import com.sun.electric.database.prototype.NodeProto; import com.sun.electric.database.text.Setting; import com.sun.electric.database.topology.ArcInst; import com.sun.electric.database.topology.Geometric; import com.sun.electric.database.topology.NodeInst; import com.sun.electric.database.variable.CodeExpression; import com.sun.electric.database.variable.ElectricObject; import com.sun.electric.database.variable.TextDescriptor; import com.sun.electric.database.variable.VarContext; import com.sun.electric.database.variable.Variable; import com.sun.electric.plugins.j3d.utils.J3DAppearance; import com.sun.electric.plugins.j3d.utils.J3DAxis; import com.sun.electric.plugins.j3d.utils.J3DCanvas3D; import com.sun.electric.plugins.j3d.utils.J3DUtils; import com.sun.electric.technology.ArcProto; import com.sun.electric.technology.Layer; import com.sun.electric.technology.Technology; import com.sun.electric.technology.technologies.Artwork; import com.sun.electric.tool.Job; import com.sun.electric.tool.JobException; import com.sun.electric.tool.user.Highlight; import com.sun.electric.tool.user.Highlighter; import com.sun.electric.tool.user.User; import com.sun.electric.tool.user.UserInterfaceMain; import com.sun.electric.tool.user.ui.*; import com.sun.electric.util.TextUtils; import com.sun.electric.util.math.DBMath; import com.sun.j3d.utils.behaviors.interpolators.KBKeyFrame; import com.sun.j3d.utils.behaviors.interpolators.RotPosScaleTCBSplinePathInterpolator; import com.sun.j3d.utils.behaviors.interpolators.TCBKeyFrame; import com.sun.j3d.utils.behaviors.vp.OrbitBehavior; import com.sun.j3d.utils.picking.PickCanvas; import com.sun.j3d.utils.picking.PickIntersection; import com.sun.j3d.utils.picking.PickResult; import com.sun.j3d.utils.universe.PlatformGeometry; import com.sun.j3d.utils.universe.SimpleUniverse; import com.sun.j3d.utils.universe.ViewingPlatform; import com.sun.j3d.utils.geometry.Primitive; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelListener; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.awt.image.ImageObserver; import java.awt.print.PageFormat; import java.io.File; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Observable; import java.util.Observer; import java.util.Set; import javax.media.j3d.*; import javax.swing.JFrame; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.tree.MutableTreeNode; import javax.vecmath.Matrix4d; import javax.vecmath.Point2d; import javax.vecmath.Point3d; import javax.vecmath.Quat4f; import javax.vecmath.Vector3d; import javax.vecmath.Vector3f; /** * This class deals with 3D View using Java3D * @author Gilda Garreton * @version 0.1 */ public class View3DWindow extends JPanel implements WindowContent, MouseMotionListener, MouseListener, MouseWheelListener, KeyListener, ActionListener, Observer, DatabaseChangeListener { private SimpleUniverse u; private J3DCanvas3D canvas; protected TransformGroup objTrans; private BranchGroup scene; private OrbitBehavior orbit; // For demo cases private Map<TransformGroup,Interpolator> interpolatorMap = new HashMap<TransformGroup,Interpolator>(); private J3DKeyCollision keyBehavior; /** the window frame containing this editwindow */ private WindowFrame wf; /** reference to 2D view of the cell */ private EditWindow view2D; /** layer visibility */ private LayerVisibility lv; /** appearances of used Layers */ private HashMap<Layer,J3DAppearance> layerAppearance = new HashMap<Layer,J3DAppearance>(); /** the cell that is in the window */ protected Cell cell; /** scale3D factor in Z axis */ private double scale3D = J3DUtils.get3DFactor(); /** Highlighter for this window */ private Highlighter highlighter; private PickCanvas pickCanvas; /** List with all Shape3D drawn per ElectricObject */ private Map<ElectricObject,List<Node>> electricObjectMap = new HashMap<ElectricObject,List<Node>>(); private boolean oneTransformPerNode = false; /** Map with object transformation for individual moves */ private Map<Node,TransformGroup> transformGroupMap = new HashMap<Node,TransformGroup>(); /** To detect max number of nodes */ private boolean reachLimit = false; /** To ask question only once */ private boolean alreadyChecked = false; /** Job reference */ private Job job; /** Reference to limit to consider scene graph big */ private int maxNumNodes; /** To consider a locos shape if field and gate polys are not alignedg */ private boolean locosShape; /** Inner class to create 3D view in a job. This should be safer in terms of the number of nodes * and be able to stop it */ private static class View3DWindowJob extends Job { private Cell cell; private transient WindowFrame windowFrame; private transient WindowContent view2D; private boolean transPerNode; public View3DWindowJob(Cell cell, WindowFrame wf, WindowContent view2D, boolean transPerNode) { super("3D View Job", null, Job.Type.CLIENT_EXAMINE, null, null, Job.Priority.USER); this.cell = cell; this.windowFrame = wf; this.view2D = view2D; this.transPerNode = transPerNode; startJob(); } public boolean doIt() throws JobException { return true; } public void terminateOK() { View3DWindow window = new View3DWindow(cell, windowFrame, view2D, transPerNode, this); windowFrame.finishWindowFrameInformation(window, cell); for (Component comp : (Main.getToolBar().getComponents()) comp.setVisible(false); } } public static void create3DWindow(Cell cell, WindowFrame wf, WindowContent view2D, boolean transPerNode) { new View3DWindowJob(cell, wf, view2D, transPerNode); } public void getObjTransform(Transform3D trans) { objTrans.getTransform(trans); } public void setObjTransform(Transform3D trans) { objTrans.setTransform(trans); } /** * Method to return if size limit has been reached * @param number * @return true if number of nodes is still under maximum value */ private boolean isSizeLimitOK(int number) { if (reachLimit || number > maxNumNodes) { // Only ask once if (!alreadyChecked) { String[] possibleValues = { "Full", "Limit", "Cancel" }; int response = Job.getUserInterface().askForChoice("Number of nodes in graph scene reached limit of " + maxNumNodes + " (loaded " + number + " nodes so far).\nClick 'Full' to include all nodes in " +cell + ", 'Limit' to show " + number + " nodes or 'Cancel' to abort process.\nUnexpand cells to reduce the number).", "Warning", possibleValues, possibleValues[2]); alreadyChecked = true; if (response > 0) // Cancel or limit { if (response == 2) job.abort(); reachLimit = true; } } if (reachLimit) return false; } return true; } // constructor View3DWindow(Cell cell, WindowFrame wf, WindowContent view2D, boolean transPerNode, Job job) { this.cell = cell; this.wf = wf; this.view2D = (EditWindow)view2D; lv = this.view2D.getLayerVisibility(); // Adding observer this.view2D.getWindowFrame().addObserver(this); this.oneTransformPerNode = transPerNode; this.job = job; this.maxNumNodes = J3DUtils.get3DMaxNumNodes(); highlighter = new Highlighter(Highlighter.SELECT_HIGHLIGHTER, wf); setLayout(new BorderLayout()); GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration(); if (config == null) { GraphicsConfigTemplate3D gc3D = new GraphicsConfigTemplate3D( ); gc3D.setSceneAntialiasing( GraphicsConfigTemplate.PREFERRED ); GraphicsDevice gd[] = GraphicsEnvironment.getLocalGraphicsEnvironment( ).getScreenDevices( ); config = gd[0].getBestConfiguration( gc3D ); } canvas = new J3DCanvas3D(config); add("Center", canvas); canvas.addMouseListener(this); // Set global appearances before create the elements J3DAppearance.setCellAppearanceValues(this); J3DAppearance.setHighlightedAppearanceValues(this); J3DAppearance.setAxisAppearanceValues(this); // Set global alpha value J3DUtils.setAlpha(J3DUtils.get3DAlpha()); // Create a simple scene and attach it to the virtual universe scene = createSceneGraph(cell); if (scene == null) return; // Have Java 3D perform optimizations on this scene graph. scene.compile(); u = new SimpleUniverse(canvas); // viewingPlatform, viewer); // lights on ViewPlatform geometry group PlatformGeometry pg = new PlatformGeometry(); J3DUtils.createLights(pg); ViewingPlatform viewingPlatform = u.getViewingPlatform(); // This will move the ViewPlatform back a bit so the // objects in the scene can be viewed. viewingPlatform.setNominalViewingTransform(); orbit = new OrbitBehavior(canvas, OrbitBehavior.REVERSE_ALL); orbit.setSchedulingBounds(J3DUtils.infiniteBounds); orbit.setCapability(OrbitBehavior.ALLOW_LOCAL_TO_VWORLD_READ); /** Setting rotation center */ Point3d center = new Point3d(0, 0, 0); BoundingSphere sceneBnd = (BoundingSphere)scene.getBounds(); sceneBnd.getCenter(center); orbit.setRotationCenter(center); orbit.setMinRadius(0); // orbit.setZoomFactor(10); // orbit.setTransFactors(10, 10); orbit.setProportionalZoom(true); viewingPlatform.setNominalViewingTransform(); viewingPlatform.setViewPlatformBehavior(orbit); double radius = sceneBnd.getRadius(); View view = u.getViewer().getView(); // Too expensive at this point if (canvas.getSceneAntialiasingAvailable() && J3DUtils.is3DAntialiasing()) view.setSceneAntialiasingEnable(true); // Setting the projection policy view.setProjectionPolicy(J3DUtils.is3DPerspective()? View.PERSPECTIVE_PROJECTION : View.PARALLEL_PROJECTION); if (!J3DUtils.is3DPerspective()) view.setCompatibilityModeEnable(true); // Setting transparency sorting view.setTransparencySortingPolicy(View.TRANSPARENCY_SORT_GEOMETRY); view.setDepthBufferFreezeTransparent(false); // set to true only for transparent layers // Setting a good viewpoint for the camera Vector3d vCenter = new Vector3d(center); double vDist = 1.4 * radius / Math.tan(view.getFieldOfView()/2.0); vCenter.z += vDist; Transform3D vTrans = new Transform3D(); //translateB.setView(cellBnd.getWidth(), 0); //double[] rotVals = User.transformIntoValues(User.get3DRotation()); //rotateB.setRotation(rotVals[0], rotVals[1], rotVals[2]); //zoomB.setZoom(User.get3DOrigZoom()); vTrans.setTranslation(vCenter); view.setBackClipDistance((vDist+radius)*200.0); view.setFrontClipDistance((vDist+radius)/200.0); // view.setBackClipPolicy(View.VIRTUAL_EYE); // view.setFrontClipPolicy(View.VIRTUAL_EYE); if (J3DUtils.is3DPerspective()) { //keyBehavior.setHomeRotation(User.transformIntoValues(J3DUtils.get3DRotation())); viewingPlatform.getViewPlatformBehavior().setHomeTransform(vTrans); viewingPlatform.getViewPlatformBehavior().goHome(); //viewingPlatform.getViewPlatformTransform().setTransform(vTrans); } else { Transform3D proj = new Transform3D(); Rectangle2D cellBnd = cell.getBounds(); proj.ortho(cellBnd.getMinX(), cellBnd.getMinX(), cellBnd.getMinY(), cellBnd.getMaxY(), (vDist+radius)/200.0, (vDist+radius)*2.0); view.setVpcToEc(proj); //viewingPlatform.getViewPlatformTransform().setTransform(lookAt); } u.addBranchGraph(scene); // Create axis with associated behavior BranchGroup axisRoot = new BranchGroup(); // Position the axis Transform3D t = new Transform3D(); // t.set(new Vector3d(-0.9, -0.5, -2.5)); // set on Linux t.set(new Vector3d(-radius/10, -radius/16, -radius/3.5)); TransformGroup axisTranslation = new TransformGroup(t); axisRoot.addChild(axisTranslation); // Create transform group to orient the axis and make it // readable & writable (this will be the target of the axis // behavior) final TransformGroup axisTG = new TransformGroup(); axisTG.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); axisTG.setCapability(TransformGroup.ALLOW_TRANSFORM_READ); axisTranslation.addChild(axisTG); // Create the axis geometry J3DAxis axis = new J3DAxis(radius/10, J3DAppearance.axisApps[0], J3DAppearance.axisApps[1], J3DAppearance.axisApps[2], User.getDefaultFont()); axisTG.addChild(axis); // Add axis into BG pg.addChild(axisRoot); // Create the axis behavior final TransformGroup viewPlatformTG = viewingPlatform.getViewPlatformTransform(); pg.addChild(new Behavior() { @Override public void initialize() { setSchedulingInterval(Behavior.getNumSchedulingIntervals() - 1); // the last scheduling interval setSchedulingBounds(J3DUtils.infiniteBounds); // everywhere wakeupOn(new WakeupOnElapsedFrames(0, true)); } @Override public void processStimulus(Enumeration criteria) { Transform3D t = new Transform3D(); viewPlatformTG.getTransform(t); // Axis transform is the inverse of view platform transform t.setTranslation(new Vector3d()); t.invert(); axisTG.setTransform(t); wakeupOn(new WakeupOnElapsedFrames(0, true)); } }); viewingPlatform.setPlatformGeometry(pg) ; setWindowTitle(); UserInterfaceMain.addDatabaseChangeListener(this); } /** * Method to create main transformation group * @param cell * @return BrachGroup representing the scene graph */ protected BranchGroup createSceneGraph(Cell cell) { // Create the root of the branch graph BranchGroup objRoot = new BranchGroup(); objRoot.setCapability(BranchGroup.ALLOW_BOUNDS_READ); objRoot.setCapability(BranchGroup.ENABLE_PICK_REPORTING); objRoot.setCapability(BranchGroup.ALLOW_BOUNDS_WRITE); // Create a Transformgroup to scale all objects so they // appear in the scene. TransformGroup objScale = new TransformGroup(); Transform3D t3d = new Transform3D(); t3d.setScale(0.7); objScale.setTransform(t3d); objRoot.addChild(objScale); // Create the TransformGroup node and initialize it to the // identity. Enable the TRANSFORM_WRITE capability so that // our behavior code can modify it at run time. Add it to // the root of the subgraph. objTrans = new TransformGroup(); objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ); objTrans.setCapability(TransformGroup.ENABLE_PICK_REPORTING); objTrans.setCapability(TransformGroup.ALLOW_CHILDREN_EXTEND); objTrans.setCapability(TransformGroup.ALLOW_CHILDREN_READ); objTrans.setCapability(TransformGroup.ALLOW_CHILDREN_WRITE); objTrans.setCapability(TransformGroup.ALLOW_BOUNDS_READ); //objRoot.addChild(objTrans); objScale.addChild(objTrans); // Background J3DUtils.createBackground(objRoot); View3DEnumerator view3D = new View3DEnumerator(cell); HierarchyEnumerator.enumerateCell(cell, VarContext.globalContext, view3D); // HierarchyEnumerator.enumerateCell(cell, VarContext.globalContext, null, view3D); if (electricObjectMap.isEmpty()) System.out.println("No 3D elements added. Check 3D values."); if (job.checkAbort()) return null; // Job cancel // Picking tools pickCanvas = new PickCanvas(canvas, objRoot); pickCanvas.setMode(PickCanvas.GEOMETRY_INTERSECT_INFO); pickCanvas.setTolerance(4.0f); setInterpolator(); // create the KeyBehavior and attach main transformation group keyBehavior = new J3DKeyCollision(objTrans, this); keyBehavior.setSchedulingBounds(J3DUtils.infiniteBounds); objTrans.addChild(keyBehavior); return objRoot; } /** * Method to set the window title. */ public void setWindowTitle() { if (wf == null) return; wf.setTitle(wf.composeTitle(cell, "3D View: ", 0)); } /** * Method to return the cell that is shown in this window. * @return the cell that is shown in this window. */ public Cell getCell() { return cell; } /** * Method to get rid of this EditWindow. Called by WindowFrame when * that windowFrame gets closed. */ public void finished() { // remove myself from listener list removeKeyListener(this); removeMouseListener(this); removeMouseMotionListener(this); removeMouseWheelListener(this); UserInterfaceMain.removeDatabaseChangeListener(this); this.view2D.getWindowFrame().deleteObserver(this); } public void bottomScrollChanged(int e) {} public void rightScrollChanged(int e) {} /** Dummy functios due to text-oriented functions */ public void fullRepaint() { J3DUtils.setBackgroundColor(null); J3DAppearance.setCellAppearanceValues(null); J3DAppearance.setHighlightedAppearanceValues(null); J3DUtils.setAmbientalColor(null); J3DAppearance.setAxisAppearanceValues(null); J3DUtils.setDirectionalColor(null); for (Map.Entry<Layer,J3DAppearance> e: layerAppearance.entrySet()) { Layer layer = e.getKey(); J3DAppearance app = e.getValue(); app.setGraphics(layer.getGraphics()); } repaint(); } public boolean findNextText(boolean reverse) { return false; } public void replaceText(String replace) {} public JPanel getPanel() { return this; } public void initTextSearch(String search, boolean caseSensitive, boolean regExp, Set<TextUtils.WhatToSearch> whatToSearch, CodeExpression.Code codeRestr, TextDescriptor.Unit unitRestr, boolean highlightedOnly) {} /** * Method to pan along X according to fixed amount of ticks * @param direction * @param panningAmounts * @param ticks */ public void panXOrY(int direction, double[] panningAmounts, int ticks) { Cell cell = getCell(); if (cell == null) return; double panningAmount = panningAmounts[User.getPanningDistance()]; int mult = (int)((double)10 * panningAmount); if (mult == 0) mult = 1; keyBehavior.moveAlongAxis(direction, mult*ticks); } /** * Method to shift the panels so that the current cursor location becomes the center. */ public void centerCursor() { } // public void setViewAndZoom(double x, double y, double zoom) // { //// translateB.setView(x, y); // } /** * Method to zoom out by a factor of 2 plus mouse pre-defined factor */ public void zoomOutContents() { keyBehavior.zoomInOut(false); // zoomB.zoomInOut(false); } /** * Method to zoom in by a factor of 2 plus mouse pre-defined factor */ public void zoomInContents() { keyBehavior.zoomInOut(true); //zoomB.zoomInOut(true); } /** * Method to reset zoom/rotation/extraTrans to original place (Fill Window operation) */ public void fillScreen() { objTrans.setTransform(new Transform3D()); u.getViewingPlatform().getViewPlatformBehavior().goHome(); } public void setCell(Cell cell, VarContext context, WindowFrame.DisplayAttributes da) {} public void focusOnHighlighted() {} public void replaceAllText(String replace) {} public Highlighter getHighlighter() { return highlighter; } /** * */ public List<MutableTreeNode> loadExplorerTrees() { return wf.loadDefaultExplorerTree(); } public void loadTechnologies() { lv = LayerVisibility.getLayerVisibility(); } /** * Adds given Arc to scene graph * @param ai * @param objTrans */ public void addArc(ArcInst ai, AffineTransform transform, TransformGroup objTrans) { // add the arc ArcProto ap = ai.getProto(); Technology tech = ap.getTechnology(); List<Node> list = addPolys(tech.getShapeOfArc(ai), transform, objTrans); if (list.isEmpty()) System.out.println("No layer with non-zero thickness found in arc '" + ai.getName() + "'"); else electricObjectMap.put(ai, list); } /** * Adds given Node to scene graph * @param no * @param objTrans */ private void addNode(NodeInst no, AffineTransform transform, TransformGroup objTrans) { // add the node NodeProto nProto = no.getProto(); Technology tech = nProto.getTechnology(); int gate = -1; int count = 0; int poly = -1; // Skipping Special nodes if (NodeInst.isSpecialNode(no)) return; List<Node> list = null; if (no.isCellInstance()) { // Cell Cell cell = (Cell)nProto; Rectangle2D rect = no.getBounds(); double [] values = new double[2]; values[0] = Double.MAX_VALUE; values[1] = Double.MIN_VALUE; cell.getZValues(values); values[0] *= scale3D; values[1] *= scale3D; Poly pol = new Poly(rect); list = new ArrayList<Node>(1); if (transform.getType() != AffineTransform.TYPE_IDENTITY) pol.transform(transform); rect = pol.getBounds2D(); list.add(J3DUtils.addPolyhedron(rect, values[0], values[1] - values[0], J3DAppearance.cellApp, objTrans)); } else { Poly[] polys = tech.getShapeOfNode(no, true, true, null); List<Shape3D> boxList = null; // Special case for transistors if (nProto.getFunction().isTransistor()) { int[] active = new int[2]; boolean isSerpentine = no.isSerpentineTransistor(); boolean isCBTransistor = no.getFunction().isCNTransistor(); boxList = new ArrayList<Shape3D>(4); // Merge active regions for (int i = 0; i < polys.length; i++) { Layer.Function fun = polys[i].getLayer().getFunction(); // count the # of active regions first. Merge only if 2 are founded (assumption of simple transistor) if (!isSerpentine && !isCBTransistor && fun.isDiff()) { // The 3D code will merge the active only for simple transistors. // Only 2 active regions are allowed for non-serpentine if (count > 1) System.out.println("More than 2 active regions detected in Transistor '" + no.getName() + "'. Ignoring this layer"); else active[count++] = i; } else if (fun.isGatePoly()) gate = i; else if (fun.isPoly()) poly = i; } if (count == 2) { Rectangle2D rect1 = polys[active[0]].getBounds2D(); Rectangle2D rect2 = polys[active[1]].getBounds2D(); double minX = Math.min(rect1.getMinX(), rect2.getMinX()); double minY = Math.min(rect1.getMinY(), rect2.getMinY()); double maxX = Math.max(rect1.getMaxX(), rect2.getMaxX()); double maxY = Math.max(rect1.getMaxY(), rect2.getMaxY()); Rectangle2D newRect = new Rectangle2D.Double(minX, minY, (maxX-minX), (maxY-minY)); Poly tmp = new Poly(newRect); tmp.setLayer(polys[active[0]].getLayer()); polys[active[0]] = tmp; // new active with whole area beneath poly gate int last = polys.length - 1; if (active[1] != last) polys[active[1]] = polys[last]; polys[last] = null; } } list = addPolys(polys, transform, objTrans); // Adding extra layers after polygons are rotated. if (locosShape && nProto.getFunction().isTransistor() && gate != -1 && poly != -1) { Point3d [] pts = new Point3d[8]; Point2D[] points = polys[gate].getPoints(); Point2D p0 = points[0]; Point2D p1 = points[1]; Point2D p2 = points[points.length-1]; double dist1 = p0.distance(p1); double dist2 = p0.distance(p2); Layer layer = polys[gate].getLayer(); double dist = (layer.getDistance() + layer.getThickness()) * scale3D; double distPoly = (polys[poly].getLayer().getDistance()) * scale3D; Point2D pointDist, pointClose; List<Point3d> topList = new ArrayList<Point3d>(); List<Point3d> bottomList = new ArrayList<Point3d>(); int center, right; if (dist1 > dist2) { pointDist = p1; pointClose = p2; center = 1; right = 2; } else { pointDist = p2; pointClose = p1; center = 2; right = points.length-1; } Point2d pDelta = new Point2d(pointDist.getX()-points[0].getX(), pointDist.getY()-points[0].getY()); pDelta.scale(0.1); double[] values = new double[2]; pDelta.get(values); // First extra polyhedron topList.add(new Point3d(p0.getX()+values[0], p0.getY()+values[1], dist)); topList.add(new Point3d(p0.getX(), p0.getY(), distPoly)); topList.add(new Point3d(p0.getX()-values[0], p0.getY()-values[1], distPoly)); topList.add(new Point3d(p0.getX(), p0.getY(), dist)); bottomList.add(new Point3d(pointClose.getX()+values[0], pointClose.getY()+values[1], dist)); bottomList.add(new Point3d(pointClose.getX(), pointClose.getY(), distPoly)); bottomList.add(new Point3d(pointClose.getX()-values[0], pointClose.getY()-values[1], distPoly)); bottomList.add(new Point3d(pointClose.getX(), pointClose.getY(), dist)); J3DUtils.correctNormals(topList, bottomList); System.arraycopy(topList.toArray(), 0, pts, 0, 4); System.arraycopy(bottomList.toArray(), 0, pts, 4, 4); boxList.add(J3DUtils.addShape3D(pts, 4, getAppearance(layer), objTrans)); // Second polyhedron topList.clear(); bottomList.clear(); topList.add(new Point3d(points[center].getX()-values[0], points[center].getY()-values[1], dist)); topList.add(new Point3d(points[center].getX(), points[center].getY(), distPoly)); topList.add(new Point3d(points[center].getX()+values[0], points[center].getY()+values[1], distPoly)); topList.add(new Point3d(points[center].getX(), points[center].getY(), dist)); bottomList.add(new Point3d(points[right].getX()-values[0], points[right].getY()-values[1], dist)); bottomList.add(new Point3d(points[right].getX(), points[right].getY(), distPoly)); bottomList.add(new Point3d(points[right].getX()+values[0], points[right].getY()+values[1], distPoly)); bottomList.add(new Point3d(points[right].getX(), points[right].getY(), dist)); J3DUtils.correctNormals(topList, bottomList); System.arraycopy(topList.toArray(), 0, pts, 0, 4); System.arraycopy(bottomList.toArray(), 0, pts, 4, 4); boxList.add(J3DUtils.addShape3D(pts, 4, getAppearance(layer), objTrans)); } if (boxList != null) list.addAll(boxList); } if (list.isEmpty()) System.out.println("No layer with non-zero thickness found in node '" + no.getName() + "'"); else { electricObjectMap.put(no, list); for (int i = 0; i < list.size(); i++) { transformGroupMap.put(list.get(i), objTrans); } } } /** * Adds given list of Polys representing a PrimitiveNode to the transformation group * @param polys * @param transform * @param objTrans */ private List<Node> addPolys(Poly [] polys, AffineTransform transform, TransformGroup objTrans) { if (polys == null) return (null); List<Node> list = new ArrayList<Node>(); for(int i = 0; i < polys.length; i++) { Poly poly = polys[i]; if (poly == null) continue; // Case for transistors and active regions. Layer layer = poly.getLayer(); if (layer == null || layer.getTechnology() == null) continue; // Non-layout technology. E.g Artwork if (!lv.isVisible(layer)) continue; // Doesn't generate the graph double thickness = layer.getThickness() * scale3D; double distance = layer.getDistance() * scale3D; if (thickness == 0) continue; // Skip zero-thickness layers if (transform != null) poly.transform(transform); // Setting appearance J3DAppearance ap = getAppearance(layer); Poly.Type type = poly.getStyle(); switch (type) { case FILLED: case CLOSED: // polygon cases if (poly.getBox() == null) // non-manhattan shape { list.add(J3DUtils.addPolyhedron(poly.getPathIterator(null), distance, thickness, ap, objTrans)); } else { Rectangle2D bounds = poly.getBounds2D(); list.add(J3DUtils.addPolyhedron(bounds, distance, thickness, ap, objTrans)); } break; case CIRCLE: case DISC: list.add(J3DUtils.addCylinder(poly.getPoints(), distance, thickness, ap, objTrans)); break; default: System.out.println("Case not implemented in View3DDWindow.addPolys"); } } return (list); } /******************************************************************************************************** * Model-View paradigm to control refresh from 2D ********************************************************************************************************/ /** * Internal method to hightlight objects * @param toSelect true if element must be highlighted * @param do2D true if 2D highlighter should be called */ private void selectObject(boolean toSelect, boolean do2D) { Highlighter highlighter2D = null; // Clean previous selection if (view2D != null && do2D) { highlighter2D = view2D.getHighlighter(); highlighter2D.clear(); } for (Highlight h : highlighter.getHighlights()) { HighlightShape3D hObj = (HighlightShape3D)(h.getObject()); // Node obj = hObj.shape; if (toSelect) // highlight cell, set transparency { //J3DAppearance app = (J3DAppearance)obj.getAppearance(); hObj.setAppearance(J3DAppearance.highlightApp); //app.getRenderingAttributes().setVisible(false); //J3DAppearance.highlightApp.setGraphics(app.getGraphics()); if (view2D != null && do2D) { //Geometry geo = obj.getGeometry(); BoundingBox bb = (BoundingBox)hObj.shape.getBounds(); Point3d lowerP = new Point3d(), upperP = new Point3d(); bb.getUpper(upperP); bb.getLower(lowerP); double[] lowerValues = new double[3]; double[] upperValues = new double[3]; lowerP.get(lowerValues); upperP.get(upperValues); Rectangle2D area = new Rectangle2D.Double(lowerValues[0], lowerValues[1], (upperValues[0]-lowerValues[0]), (upperValues[1]-lowerValues[1])); highlighter2D.addArea(area, cell); } } else // back to normal { //EGraphics graphics = J3DAppearance.highlightApp.getGraphics(); if (hObj.origApp != null) //if (graphics != null) { //J3DAppearance origAp = (J3DAppearance)graphics.get3DAppearance(); hObj.setAppearance(hObj.origApp); } else // its a cell hObj.setAppearance(J3DAppearance.cellApp); } } if (!toSelect) highlighter.clear(); if (do2D) view2D.fullRepaint(); } /** * Observer method to highlight 3D nodes by clicking 2D objects * @param o * @param arg */ public void update(Observable o, Object arg) { if (arg instanceof LayerVisibility) { lv = (LayerVisibility)arg; for (Map.Entry<Layer,J3DAppearance> e: layerAppearance.entrySet()) { Layer layer = e.getKey(); J3DAppearance app = e.getValue(); app.set3DVisibility(lv.isVisible(layer)); } repaint(); return; } if (o == view2D.getWindowFrame()) { // Undo previous highlight selectObject(false, false); Highlighter highlighter2D = view2D.getHighlighter(); List<Geometric> geomList = highlighter2D.getHighlightedEObjs(true, true); for (Geometric geom : geomList) { ElectricObject eobj = geom; List<Node> list = electricObjectMap.get(eobj); if (list == null || list.size() == 0) continue; for (Node shape : list) { highlighter.addObject(new HighlightShape3D(shape), cell); } } selectObject(true, false); return; // done } } /** This class will help to remember original appearance of the node * */ private static class HighlightShape3D { Node shape; Appearance origApp; HighlightShape3D(Node n) { this.shape = n; this.origApp = getAppearance(n); } static Appearance getAppearance(Node n) { if (n instanceof Shape3D) return ((Shape3D)n).getAppearance(); else if (n instanceof Primitive) return ((Primitive)n).getAppearance(); else assert(false); // it should not happen return null; } void setAppearance(Appearance a) { if (shape instanceof Shape3D) ((Shape3D)shape).setAppearance(a); else if (shape instanceof Primitive) ((Primitive)shape).setAppearance(a); else assert(false); // it should not happen } } /** * Method to change Z values in elements * @param value */ public static void setScaleFactor(double value) { Transform3D vTrans = new Transform3D(); Vector3d vCenter = new Vector3d(1, 1, value); for(Iterator<WindowFrame> it = WindowFrame.getWindows(); it.hasNext(); ) { WindowFrame wf = it.next(); WindowContent content = wf.getContent(); if (!(content instanceof View3DWindow)) continue; View3DWindow wnd = (View3DWindow)content; wnd.objTrans.getTransform(vTrans); vTrans.setScale(vCenter); wnd.objTrans.setTransform(vTrans); } } /** * Method to turn on/off antialiasing * @param value true if antialiasing is set to true */ public static void setAntialiasing(boolean value) { for(Iterator<WindowFrame> it = WindowFrame.getWindows(); it.hasNext(); ) { WindowFrame wf = it.next(); WindowContent content = wf.getContent(); if (!(content instanceof View3DWindow)) continue; View3DWindow wnd = (View3DWindow)content; View view = wnd.u.getViewer().getView(); view.setSceneAntialiasingEnable(value); } } /** * Method to change geometry of all nodes using this particular layer * This could be an expensive function!. * @param layer * @param distance * @param thickness */ public void setZValues(Layer layer, Double origDist, Double origThick, Double distance, Double thickness) { // for(Iterator<WindowFrame> it = WindowFrame.getWindows(); it.hasNext(); ) // { // WindowFrame wf = it.next(); // WindowContent content = wf.getContent(); // if (!(content instanceof View3DWindow)) continue; // View3DWindow wnd = (View3DWindow)content; for (int i = 0; i < /*wnd.*/objTrans.numChildren(); i++) { Node node = /*wnd.*/objTrans.getChild(i); if (node instanceof Shape3D) { Shape3D shape = (Shape3D)node; J3DAppearance app = (J3DAppearance)shape.getAppearance(); if (app.getLayer() == layer) J3DUtils.updateZValues(shape, origDist.floatValue(), (float)(origDist.floatValue()+origThick.floatValue()), distance.floatValue(), (float)(distance.floatValue()+thickness.floatValue())); } } // } } /** * Method to get 3D appearance for a Layer. It will create * object if doesn't exist. * @param layer * @return */ private J3DAppearance getAppearance(Layer layer) { J3DAppearance app = layerAppearance.get(layer); if (app == null) { app = new J3DAppearance(layer, lv.isVisible(layer)); layerAppearance.put(layer, app); } return app; } /** * Method to export directly PNG file * @param ep * @param filePath */ public void writeImage(ElectricPrinter ep, String filePath) { canvas.filePath = filePath; saveImage(false); } public void saveImage(boolean movieMode) { canvas.movieMode = movieMode; canvas.writePNG_ = true; canvas.repaint(); } /** * Method to intialize for printing. * @param ep the ElectricPrinter object. * @param pageFormat information about the print job. * @return false for now. */ public boolean initializePrinting(ElectricPrinter ep, PageFormat pageFormat) { return false;} /** * Method to print window using offscreen canvas. * @param ep printable object. * @return the image to print (null on error). */ public BufferedImage getPrintImage(ElectricPrinter ep) { BufferedImage bImage = ep.getBufferedImage(); //int OFF_SCREEN_SCALE = 3; // might have problems if visibility of some layers is switched off if (bImage == null) { //Forcint the repaint canvas.writePNG_ = true; canvas.repaint(); bImage = canvas.img; // // Create the off-screen Canvas3D object // if (offScreenCanvas3D == null) // { // offScreenCanvas3D = new J3DUtils.OffScreenCanvas3D(SimpleUniverse.getPreferredConfiguration(), true); // // attach the offscreen canvas to the view // u.getViewer().getView().addCanvas3D(offScreenCanvas3D); // // Set the off-screen size based on a scale3D factor times the // // on-screen size // Screen3D sOn = canvas.getScreen3D(); // Screen3D sOff = offScreenCanvas3D.getScreen3D(); // Dimension dim = sOn.getSize(); // dim.width *= OFF_SCREEN_SCALE; // dim.height *= OFF_SCREEN_SCALE; // sOff.setSize(dim); // sOff.setPhysicalScreenWidth(sOn.getPhysicalScreenWidth() * OFF_SCREEN_SCALE); // sOff.setPhysicalScreenHeight(sOn.getPhysicalScreenHeight() * OFF_SCREEN_SCALE); // bImage = new BufferedImage(dim.width, dim.height, BufferedImage.TYPE_INT_ARGB); // ImageComponent2D buffer = new ImageComponent2D(ImageComponent.FORMAT_RGBA, bImage); // // offScreenCanvas3D.setOffScreenBuffer(buffer); // } // offScreenCanvas3D.renderOffScreenBuffer(); // offScreenCanvas3D.waitForOffScreenRendering(); // bImage = offScreenCanvas3D.getOffScreenBuffer().getImage(); ep.setBufferedImage(bImage); //Need to remove offscreen after that //u.getViewer().getView().removeCanvas3D(offScreenCanvas3D); } Graphics2D g2d = (Graphics2D)ep.getGraphics(); // In case of printing if (g2d != null) { AffineTransform t2d = new AffineTransform(); t2d.translate(ep.getPageFormat().getImageableX(), ep.getPageFormat().getImageableY()); double xscale = ep.getPageFormat().getImageableWidth() / (double)bImage.getWidth(); double yscale = ep.getPageFormat().getImageableHeight() / (double)bImage.getHeight(); double scale = Math.min(xscale, yscale); t2d.scale(scale, scale); try { ImageObserver obj = ep; g2d.drawImage(bImage, t2d, obj); } catch (Exception ex) { ex.printStackTrace(); return null; } } return bImage; } // ************************************* EVENT LISTENERS ************************************* //private int lastXPosition, lastYPosition; /** * Respond to an action performed, in this case change the current cell * when the user clicks on an entry in the upHierarchy popup menu. */ public void actionPerformed(ActionEvent e) { JMenuItem source = (JMenuItem)e.getSource(); // extract library and cell from string Cell cell = (Cell)Cell.findNodeProto(source.getText()); if (cell == null) return; setCell(cell, VarContext.globalContext, null); } // the MouseListener events public void mousePressed(MouseEvent evt) { //lastXPosition = evt.getX(); lastYPosition = evt.getY(); /* View3DWindow wnd = (View3DWindow)evt.getSource(); WindowFrame.setCurrentWindowFrame(wnd.wf); WindowFrame.curMouseListener.mousePressed(evt); */ } public void mouseReleased(MouseEvent evt) { //lastXPosition = evt.getX(); lastYPosition = evt.getY(); //WindowFrame.curMouseListener.mouseReleased(evt); } /** * Method to rotate individual groups * @param values array of values */ public J3DUtils.ThreeDDemoKnot moveAndRotate(double[] values) { Vector3f newPos = new Vector3f((float)values[0], (float)values[1], (float)values[2]); double factor = 10; Quat4f quaf = J3DUtils.createQuaternionFromEuler(factor*values[3], factor*values[4], factor*values[5]); Transform3D currXform = new Transform3D(); for (Iterator<NodeInst> it = cell.getNodes(); it.hasNext();) { NodeInst ni = it.next(); Variable var = ni.getVar("3D_NODE_DEMO"); if (var == null) continue; List<Node> list = electricObjectMap.get(ni); for (int i = 0; i < list.size(); i++) { Node obj = list.get(i); TransformGroup grp = transformGroupMap.get(obj); grp.getTransform(currXform); currXform.setTranslation(newPos); // tmpVec.set(newPos); // boolean invert = true; // if (invert) { // currXform.mul(currXform, tmpVec); // } else { // currXform.mul(tmpVec, currXform); // } grp.setTransform(currXform); grp.getTransform(currXform); Matrix4d mat = new Matrix4d(); // Remember old matrix currXform.get(mat); //tmpVec.setEuler(rotation); // Translate to rotation point currXform.setTranslation(new Vector3d(values[6], values[7], values[8])); currXform.setRotation(quaf); // if (invert) { // currXform.mul(currXform, tmpVec); // } else { // currXform.mul(tmpVec, currXform); // } // Set old translation back Vector3d translation = new Vector3d(mat.m03, mat.m13, mat.m23); currXform.setTranslation(translation); // Update xform grp.setTransform(currXform); } } return(new J3DUtils.ThreeDDemoKnot(1, newPos, quaf, null)); } public void mouseClicked(MouseEvent evt) { //lastXPosition = evt.getX(); lastYPosition = evt.getY(); //WindowFrame.curMouseListener.mouseClicked(evt); pickCanvas.setShapeLocation(evt); // Transform3D t = new Transform3D(); // Transform3D t1 = new Transform3D(); // canvas.getImagePlateToVworld(t); // canvas.getVworldToImagePlate(t1); PickResult result = pickCanvas.pickClosest(); // Clean previous selection selectObject(false, true); if (result != null) { Shape3D s = (Shape3D)result.getNode(PickResult.SHAPE3D); if (s != null) { highlighter.addObject(new HighlightShape3D(s), cell); selectObject(true, true); } } WindowFrame.getMouseListener().mouseClicked(evt); } public void mouseEntered(MouseEvent evt) { //lastXPosition = evt.getX(); lastYPosition = evt.getY(); //showCoordinates(evt); //WindowFrame.curMouseListener.mouseEntered(evt); } public void mouseExited(MouseEvent evt) { //lastXPosition = evt.getX(); lastYPosition = evt.getY(); //WindowFrame.curMouseListener.mouseExited(evt); } // the MouseMotionListener events public void mouseMoved(MouseEvent evt) { //lastXPosition = evt.getX(); lastYPosition = evt.getY(); //showCoordinates(evt); //WindowFrame.curMouseMotionListener.mouseMoved(evt); } public void mouseDragged(MouseEvent evt) { //lastXPosition = evt.getX(); lastYPosition = evt.getY(); //showCoordinates(evt); //WindowFrame.curMouseMotionListener.mouseDragged(evt); } public void showCoordinates(MouseEvent evt) { View3DWindow wnd = (View3DWindow)evt.getSource(); if (wnd.getCell() == null) StatusBar.setCoordinates(null, wnd.wf); else { Point2D pt = wnd.screenToDatabase(evt.getX(), evt.getY()); EditingPreferences ep = EditingPreferences.getThreadEditingPreferences(); DBMath.gridAlign(pt, ep.getAlignmentToGrid()); StatusBar.setCoordinates("(" + TextUtils.formatDouble(pt.getX(), 2) + ", " + TextUtils.formatDouble(pt.getY(), 2) + ")", wnd.wf); } } // the MouseWheelListener events public void mouseWheelMoved(MouseWheelEvent evt) { WindowFrame.getMouseWheelListenerListener().mouseWheelMoved(evt); } // the KeyListener events public void keyPressed(KeyEvent evt) { System.out.println("Here keyPressed");WindowFrame.getKeyListenerListener().keyPressed(evt); } public void keyReleased(KeyEvent evt) { System.out.println("Here keyReleased");WindowFrame.getKeyListenerListener().keyReleased(evt); } public void keyTyped(KeyEvent evt) { System.out.println("Here keyTyped");WindowFrame.getKeyListenerListener().keyTyped(evt); } public Point getLastMousePosition() { //return new Point(lastXPosition, lastYPosition); return new Point(0,0); } public void databaseChanged(DatabaseChangeEvent e) { Environment oldEnv = e.oldSnapshot.environment; Environment newEnv = e.newSnapshot.environment; if (newEnv == oldEnv) return; if (newEnv.techPool != oldEnv.techPool) { Job.getUserInterface().showInformationMessage("3D Window becomes invalid after technology parameters change", "Closing 3D Window"); wf.finished(); return; } for (Technology tech: newEnv.techPool.values()) { for (Iterator<Layer> it = tech.getLayers(); it.hasNext(); ) { Layer layer = it.next(); Setting distanceSetting = layer.getDistanceSetting(); Setting thicknessSetting = layer.getThicknessSetting(); Double oldDistance = (Double)oldEnv.getValue(distanceSetting); Double oldThickness = (Double)oldEnv.getValue(thicknessSetting); Double newDistance = (Double)newEnv.getValue(distanceSetting); Double newThickness = (Double)newEnv.getValue(thicknessSetting); if (newDistance.equals(oldDistance) && newThickness.equals(oldThickness)) continue; setZValues(layer, oldDistance, oldThickness, newDistance, newThickness); } } } // ************************************* COORDINATES ************************************* /** * Method to convert a screen coordinate to database coordinates. * @param screenX the X coordinate (on the screen in this EditWindow). * @param screenY the Y coordinate (on the screen in this EditWindow). * @return the coordinate of that point in database units. */ public Point2D screenToDatabase(int screenX, int screenY) { double dbX = 0, dbY = 0; /* = (screenX - sz.width/2) / scale3D + offx; double dbY = (sz.height/2 - screenY) / scale3D + offy; */ return new Point2D.Double(dbX, dbY); } /***************************************************************************** * To navigate in tree and create 3D objects * *****************************************************************************/ private class View3DEnumerator extends HierarchyEnumerator.Visitor { public View3DEnumerator(Cell cell) { // Checking if field poly and gate poly are not aligned. // If not, the extra polyhedra are generated to build like a LoCos shape Technology tech = cell.getTechnology(); Layer poly = tech.findLayerFromFunction(Layer.Function.POLY1, -1); Layer gate = tech.findLayerFromFunction(Layer.Function.GATE, -1); if (poly != null && gate != null && !DBMath.areEquals(poly.getDistance(), gate.getDistance())) { locosShape = true; } } /** */ public boolean enterCell(HierarchyEnumerator.CellInfo info) { if (job != null && job.checkAbort()) return false; if (!isSizeLimitOK(info.getCell().getNumArcs() + objTrans.numChildren())) return false; // limit reached AffineTransform rTrans = info.getTransformToRoot(); for(Iterator<ArcInst> it = info.getCell().getArcs(); it.hasNext(); ) { addArc(it.next(), rTrans, objTrans); } return true; } /** */ public void exitCell(HierarchyEnumerator.CellInfo info) {} /** */ public boolean visitNodeInst(Nodable no, HierarchyEnumerator.CellInfo info) { if (reachLimit) return false; if (job != null && job.checkAbort()) return false; NodeInst ni = no.getNodeInst(); AffineTransform trans = ni.rotateOutAboutTrueCenter(); AffineTransform root = info.getTransformToRoot(); if (root.getType() != AffineTransform.TYPE_IDENTITY) trans.preConcatenate(root); TransformGroup grp = objTrans; if (oneTransformPerNode) { grp = new TransformGroup(); grp.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); grp.setCapability(TransformGroup.ALLOW_TRANSFORM_READ); grp.setCapability(TransformGroup.ALLOW_CHILDREN_EXTEND); grp.setCapability(TransformGroup.ALLOW_CHILDREN_READ); grp.setCapability(TransformGroup.ALLOW_CHILDREN_WRITE); objTrans.addChild(grp); } addNode(ni, trans, grp); if (!isSizeLimitOK(grp.numChildren())) return false; // limit reached // For cells, it should go into the hierarchy return ni.isExpanded(); } } /***************************************************************************** * Demo Stuff * *****************************************************************************/ /** * Method to create spline interpolator for demo mode */ private void setInterpolator() { Transform3D yAxis = new Transform3D(); List<J3DUtils.ThreeDDemoKnot> polys = new ArrayList<J3DUtils.ThreeDDemoKnot>(); double [] zValues = new double[2]; cell.getZValues(zValues); double zCenter = (zValues[0] + zValues[1])/2; Rectangle2D bounding = cell.getBounds(); Vector3d translation = new Vector3d (bounding.getCenterX(), bounding.getCenterY(), zCenter); yAxis.setTranslation(translation); for (Iterator<NodeInst> it = cell.getNodes(); it.hasNext();) { NodeInst ni = it.next(); if (ni.getProto() == Artwork.tech().pinNode) { Rectangle2D rect = ni.getBounds(); Variable var = ni.getVar("3D_Z_VALUE"); double zValue = (var == null) ? zCenter : TextUtils.atof(var.getObject().toString()); var = ni.getVar("3D_SCALE_VALUE"); double scale = (var == null) ? 1 : TextUtils.atof(var.getObject().toString()); var = ni.getVar("3D_HEADING_VALUE"); double heading = (var == null) ? 0 : TextUtils.atof(var.getObject().toString()); var = ni.getVar("3D_PITCH_VALUE"); double pitch = (var == null) ? 0 : TextUtils.atof(var.getObject().toString()); var = ni.getVar("3D_BANK_VALUE"); double bank = (var == null) ? 0 : TextUtils.atof(var.getObject().toString()); var = ni.getVar("3D_ROTX_VALUE"); double rotX = (var == null) ? 0 : TextUtils.atof(var.getObject().toString()); var = ni.getVar("3D_ROTY_VALUE"); double rotY = (var == null) ? 0 : TextUtils.atof(var.getObject().toString()); var = ni.getVar("3D_ROTZ_VALUE"); double rotZ = (var == null) ? 0 : TextUtils.atof(var.getObject().toString()); J3DUtils.ThreeDDemoKnot knot = new J3DUtils.ThreeDDemoKnot(rect.getCenterX(), rect.getCenterY(), zValue, scale, heading, pitch, bank, rotX, rotY, rotZ); polys.add(knot); } } if (polys.size() == 0) return; // nothing to create KBKeyFrame[] splineKeyFrames = new KBKeyFrame[polys.size()]; TCBKeyFrame[] keyFrames = new TCBKeyFrame[polys.size()]; for (int i = 0; i < polys.size(); i++) { J3DUtils.ThreeDDemoKnot knot = polys.get(i); splineKeyFrames[i] = J3DUtils.getNextKBKeyFrame((float)((float)i/(polys.size()-1)), knot); keyFrames[i] = J3DUtils.getNextTCBKeyFrame((float)((float)i/(polys.size()-1)), knot); } Interpolator tcbSplineInter = new RotPosScaleTCBSplinePathInterpolator(J3DUtils.jAlpha, objTrans, yAxis, keyFrames); tcbSplineInter.setSchedulingBounds(J3DUtils.infiniteBounds); tcbSplineInter.setEnable(false); interpolatorMap.put(objTrans, tcbSplineInter); objTrans.addChild(tcbSplineInter); } /** * Method to create a path interpolator using knots * defined in input list * @param knotList list with knot data. If null, search for data attached to nodes */ public Map<TransformGroup,BranchGroup> addInterpolator(List<J3DUtils.ThreeDDemoKnot> knotList) { if (knotList != null && knotList.size() < 2) { System.out.println("Needs at least 2 frams for the interpolator"); return null; } Map<TransformGroup,BranchGroup> interMap = new HashMap<TransformGroup,BranchGroup>(1); for (Iterator<NodeInst> it = cell.getNodes(); it.hasNext();) { NodeInst ni = it.next(); Variable var = ni.getVar("3D_NODE_DEMO"); if (var == null) continue; List<J3DUtils.ThreeDDemoKnot> tmpList = knotList; if (tmpList == null) { tmpList = J3DUtils.readDemoDataFromFile(this); if (tmpList == null) continue; // nothing load } List<Node> list = electricObjectMap.get(ni); for (int j = 0; j < list.size(); j++) { Node obj = list.get(j); TransformGroup grp = transformGroupMap.get(obj); interMap = addInterpolatorPerGroup(tmpList, grp, interMap, false); // BranchGroup behaviorBranch = new BranchGroup(); // behaviorBranch.setCapability(BranchGroup.ALLOW_DETACH); // to detach this branch from parent group // TCBKeyFrame[] keyFrames = new TCBKeyFrame[tmpList.size()]; // for (int i = 0; i < tmpList.size(); i++) // { // J3DUtils.ThreeDDemoKnot knot = tmpList.get(i); // keyFrames[i] = J3DUtils.getNextTCBKeyFrame((float)((float)i/(tmpList.size()-1)), knot); // } // Transform3D yAxis = new Transform3D(); // Interpolator tcbSplineInter = new RotPosScaleTCBSplinePathInterpolator(J3DUtils.jAlpha, grp, // yAxis, keyFrames); // tcbSplineInter.setSchedulingBounds(new BoundingSphere(new Point3d(), Double.MAX_VALUE)); // behaviorBranch.addChild(tcbSplineInter); // interMap.put(grp, behaviorBranch); // grp.addChild(behaviorBranch); // interpolatorMap.put(grp, tcbSplineInter); } } return interMap; } /** * Method to add interpolator per group * @param knotList * @param grp * @param interMap * @return Map with interpolation groups */ public Map<TransformGroup,BranchGroup> addInterpolatorPerGroup(List<J3DUtils.ThreeDDemoKnot> knotList, TransformGroup grp, Map<TransformGroup,BranchGroup> interMap, boolean useView) { if (knotList == null || knotList.size() < 2) { System.out.println("Needs at least 2 frams for the interpolator"); return null; } if (interMap == null) interMap = new HashMap<TransformGroup,BranchGroup>(1); if (grp == null) { if (!useView) grp = objTrans; else grp = u.getViewingPlatform().getViewPlatformTransform(); } BranchGroup behaviorBranch = new BranchGroup(); behaviorBranch.setCapability(BranchGroup.ALLOW_DETACH); // to detach this branch from parent group TCBKeyFrame[] keyFrames = new TCBKeyFrame[knotList.size()]; for (int i = 0; i < knotList.size(); i++) { J3DUtils.ThreeDDemoKnot knot = knotList.get(i); keyFrames[i] = J3DUtils.getNextTCBKeyFrame((float)((float)i/(knotList.size()-1)), knot); } Transform3D yAxis = new Transform3D(); Interpolator tcbSplineInter = new J3DRotPosScaleTCBSplinePathInterpolator(J3DUtils.jAlpha, grp, yAxis, keyFrames, knotList); tcbSplineInter.setSchedulingBounds(new BoundingSphere(new Point3d(), Double.MAX_VALUE)); behaviorBranch.addChild(tcbSplineInter); interMap.put(grp, behaviorBranch); grp.addChild(behaviorBranch); interpolatorMap.put(grp, tcbSplineInter); return interMap; } /** * Method to remove certain interpolators from scene graph * @param interMap */ public void removeInterpolator(Map<TransformGroup,BranchGroup> interMap) { canvas.resetMoveFrames(); for (TransformGroup grp : interMap.keySet()) { Node node = interMap.get(grp); grp.removeChild(node); } } ///////////////////// KEY BEHAVIOR FUNCTION /////////////////////////////// private static Vector3d tmpVec = new Vector3d(); private static Vector3d mapSize = null; protected double getScale( ) { return 0.05; } Vector3d getMapSize( ) { if (mapSize == null) mapSize = new Vector3d(2, 0, 2); return mapSize; } Point2d convertToMapCoordinate( Vector3d worldCoord ) { Point2d point2d = new Point2d( ); Vector3d squareSize = getMapSize(); point2d.x = (worldCoord.x + getPanel().getWidth())/ squareSize.x; point2d.y = (worldCoord.z + getPanel().getHeight())/ squareSize.z; return point2d; } public boolean isCollision(Transform3D t3d) { // get the translation t3d.get(tmpVec); // we need to scale up by the scale that was // applied to the root TG on the view side of the scenegraph tmpVec.scale( 1.0 / getScale( ) ); // Vector3d mapSquareSize = getMapSize( ); // first check that we are still inside the "world" // if (tmpVec.x < -getPanel().getWidth() + mapSquareSize.x || // tmpVec.x > getPanel().getWidth() - mapSquareSize.x || // tmpVec.y < -getPanel().getHeight() + mapSquareSize.y || // tmpVec.y > getPanel().getHeight() - mapSquareSize.y ) // return true; // then do a pixel based look up using the map return isCollision(tmpVec); } /** * Method to detect if give x, y location in the world collides with geometry * @param worldCoord * @return true if vector collides */ protected boolean isCollision( Vector3d worldCoord ) { Point2d point = convertToMapCoordinate( worldCoord ); // PickTool pickTool = new PickTool(scene); // pickTool.setMode( PickTool.BOUNDS ); // // BoundingSphere bounds = (BoundingSphere) objTrans.getBounds( ); // PickBounds pickBounds = new PickBounds( new BoundingSphere( new Point3d(keyBehavior.positionVector.x, // keyBehavior.positionVector.y, keyBehavior.positionVector.z), bounds.getRadius( ) ) ); // pickTool.setShape( pickBounds, new Point3d(0, 0, 0)); // PickResult[] resultArray = pickTool.pickAll( ); // // System.out.println( "Wold Point " + worldCoord + " local " + keyBehavior.positionVector); // // if (resultArray != null) // { // for( int n = 0; n < resultArray.length; n++ ) // { // Object userData = resultArray[n].getObject( ).getUserData( ); // // if ( userData != null && userData instanceof String ) // { // System.out.println( "Collision between: " + objTrans.getUserData( ) + " and: " + userData ); // // check that we are not colliding with ourselves... // if ( ((String) userData).equals( (String) objTrans.getUserData( ) ) == false ) // { // System.out.println( "Collision between: " + objTrans.getUserData( ) + " and: " + userData ); // return true; // } // } // } // } pickCanvas.setShapeLocation((int)point.x, (int)point.y); PickResult result = pickCanvas.pickClosest(); if (result != null && result.getNode(PickResult.SHAPE3D) != null) { // Shape3D shape = (Shape3D)result.getNode(PickResult.SHAPE3D); //shape.setAppearance(J3DAppearance.highlightApp); for (int i = 0; i < result.numIntersections(); i++) { PickIntersection inter = result.getIntersection(i); // System.out.println("Collision " + inter.getDistance() + " " + inter.getPointCoordinates() + " normal " + inter.getPointNormal()); // System.out.println("Point " + point + " world " + worldCoord); // GeometryArray geo = inter.getGeometryArray(); if (inter.getDistance() < 6) return (true); // collision } } return (false); } public J3DUtils.ThreeDDemoKnot addFrame(boolean useView) { Transform3D tmpTrans = new Transform3D(); if (!useView) objTrans.getTransform(tmpTrans); else u.getViewingPlatform().getViewPlatformTransform().getTransform(tmpTrans); tmpTrans.get(tmpVec); Quat4f rot = new Quat4f(); tmpTrans.get(rot); Shape3D shape = null; // for (Highlight h : highlighter.getHighlights()) // { // shape = (Shape3D)h.getObject(); // break; // } // repaint(); return(new J3DUtils.ThreeDDemoKnot(1, new Vector3f(tmpVec), rot, shape)); } public void saveMovie(File file) { if (file != null) canvas.saveMovie(file); } private static class J3DRotPosScaleTCBSplinePathInterpolator extends com.sun.j3d.utils.behaviors.interpolators.RotPosScaleTCBSplinePathInterpolator { List knotList; int previousUpper = -1, previousLower = -1; public J3DRotPosScaleTCBSplinePathInterpolator(Alpha alpha, TransformGroup target, Transform3D axisOfTransform, TCBKeyFrame[] keys, List list) { super(alpha, target, axisOfTransform, keys); knotList = list; } public void processStimulus(java.util.Enumeration criteria) { super.processStimulus(criteria); // // if (upperKnot == previousUpper && lowerKnot == previousLower) return; // previousUpper = upperKnot; // previousLower = lowerKnot; // J3DUtils.ThreeDDemoKnot knot = knotList.get(upperKnot-1); // if (knot != null && knot.shape != null) // target.addChild(knot.shape); //// knot.shape.getAppearance().getRenderingAttributes().setVisible(true); // knot = knotList.get(lowerKnot-1); // if (knot != null && knot.shape != null) //// target.removeChild(knot.shape); // knot.shape.getAppearance().getRenderingAttributes().setVisible(false); //// System.out.println("Criteria " + upperKnot + " " + lowerKnot); } } }