/* * JaamSim Discrete Event Simulation * Copyright (C) 2012 Ausenco Engineering Canada 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.render; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map.Entry; import com.jaamsim.DisplayModels.DisplayModel; import com.jaamsim.Graphics.DisplayEntity; import com.jaamsim.Graphics.Tag; import com.jaamsim.basicsim.Entity; import com.jaamsim.controllers.RenderManager; import com.jaamsim.input.ColourInput; import com.jaamsim.math.Color4d; import com.jaamsim.math.Mat4d; import com.jaamsim.math.Transform; import com.jaamsim.math.Vec3d; import com.jaamsim.math.Vec4d; /** * Represents the One-to-one mapping of DisplayModels to Entities * Any graphical caching goes in here, while configuration information goes in the DisplayModel * @author matt.chudleigh * */ public abstract class DisplayModelBinding { protected Entity observee; protected DisplayModel dm; private static final Color4d MINT = ColourInput.getColorWithName("mint"); private static final boolean _saveCacheMissData = false; private static final HashMap<String, CacheCounter> cacheMissData = new HashMap<>(); //protected DisplayEntity _dispObservee; private List<Vec4d> handlePoints = null; private List<Vec4d> rotateHandlePoints = null; private final static ArrayList<Vec4d> HANDLE_POINTS; private final static ArrayList<Vec4d> ROTATE_POINTS; private static int cacheHits = 0; private static int cacheMisses = 0; static { // NOTE: the order of the points corresponds to the list of static picking IDs in RenderManager, // both need to be changed together HANDLE_POINTS = new ArrayList<>(8); // Sides HANDLE_POINTS.add(new Vec4d( 0.5, 0, 0, 1.0d)); HANDLE_POINTS.add(new Vec4d(-0.5, 0, 0, 1.0d)); HANDLE_POINTS.add(new Vec4d( 0, 0.5, 0, 1.0d)); HANDLE_POINTS.add(new Vec4d( 0, -0.5, 0, 1.0d)); // Corners HANDLE_POINTS.add(new Vec4d( 0.5, 0.5, 0, 1.0d)); HANDLE_POINTS.add(new Vec4d( 0.5, -0.5, 0, 1.0d)); HANDLE_POINTS.add(new Vec4d(-0.5, 0.5, 0, 1.0d)); HANDLE_POINTS.add(new Vec4d(-0.5, -0.5, 0, 1.0d)); ROTATE_POINTS = new ArrayList<>(2); // Sides ROTATE_POINTS.add(new Vec4d(1.0, 0, 0, 1.0d)); ROTATE_POINTS.add(new Vec4d(0.5, 0, 0, 1.0d)); } public DisplayModelBinding(Entity ent, DisplayModel dm) { this.observee = ent; this.dm = dm; } public abstract void collectProxies(double simTime, ArrayList<RenderProxy> out); public boolean isBoundTo(Entity ent) { return ent == observee; } private void updatePoints(double simTime) { if (!(observee instanceof DisplayEntity)) { return; } DisplayEntity de = (DisplayEntity)observee; // Convert the points to world space Transform trans = de.getGlobalTrans(); Vec3d scale = de.getSize(); scale.mul3(dm.getModelScale()); Mat4d mat = new Mat4d(trans.getMat4dRef()); mat.scaleCols3(scale); handlePoints = RenderUtils.transformPoints(mat, HANDLE_POINTS, 0); rotateHandlePoints = RenderUtils.transformPoints(mat, ROTATE_POINTS, 0); } // Collect the proxies for the selection box public void collectSelectionProxies(double simTime, ArrayList<RenderProxy> out) { collectSelectionBox(simTime, out); } // This is exposed differently than above, because of the weird type heirarchy around // ScreenPointsObservers. This can't just be overloaded, because sometime we want it back.... protected void collectSelectionBox(double simTime, ArrayList<RenderProxy> out) { if (!(observee instanceof DisplayEntity)) { return; } DisplayEntity de = (DisplayEntity)observee; Transform trans = de.getGlobalTrans(); Vec3d scale = de.getSize(); scale.mul3(dm.getModelScale()); PolygonProxy outline = new PolygonProxy(RenderUtils.RECT_POINTS, trans, scale, MINT, true, 1, getVisibilityInfo(), RenderManager.MOVE_PICK_ID); outline.setHoverColour(ColourInput.LIGHT_GREY); out.add(outline); updatePoints(simTime); for (int i = 0; i < 8; ++i) { List<Vec4d> pl = new ArrayList<>(1); pl.add(handlePoints.get(i)); PointProxy point = new PointProxy(pl, ColourInput.GREEN, 8, getVisibilityInfo(), RenderManager.RESIZE_POSX_PICK_ID - i); point.setHoverColour(ColourInput.LIGHT_GREY); point.setCollisionAngle(0.004363); // 0.25 degrees in radians out.add(point); } // Add the rotate handle List<Vec4d> pl = new ArrayList<>(1); pl.add(new Vec4d(rotateHandlePoints.get(0))); PointProxy point = new PointProxy(pl, ColourInput.GREEN, 8, getVisibilityInfo(), RenderManager.ROTATE_PICK_ID); point.setHoverColour(ColourInput.LIGHT_GREY); out.add(point); LineProxy rotateLine = new LineProxy(rotateHandlePoints, MINT, 1, getVisibilityInfo(), RenderManager.ROTATE_PICK_ID); rotateLine.setHoverColour(ColourInput.LIGHT_GREY); out.add(rotateLine); } public static int getCacheHits() { return cacheHits; } public static int getCacheMisses() { return cacheMisses; } public static void clearCacheCounters() { cacheHits = 0; cacheMisses = 0; } public static void clearCacheMissData() { cacheMissData.clear(); } private static final boolean saveCacheMissData() { return _saveCacheMissData; } private static class CacheCounter { int misses = 0; int hits = 0; CacheCounter() {} } public static void registerCacheHit(String type) { cacheHits++; if (!saveCacheMissData()) { return; } CacheCounter cc = cacheMissData.get(type); if (cc == null) { cc = new CacheCounter(); cacheMissData.put(type, cc); } cc.hits++; } public static int getCacheHitCount(String type) { CacheCounter cc = cacheMissData.get(type); if (cc == null) return 0; return cc.hits; } public static void registerCacheMiss(String type) { cacheMisses++; if (!saveCacheMissData()) { return; } CacheCounter cc = cacheMissData.get(type); if (cc == null) { cc = new CacheCounter(); cacheMissData.put(type, cc); } cc.misses++; } public static int getCacheMissCount(String type) { CacheCounter cc = cacheMissData.get(type); if (cc == null) return 0; return cc.misses; } public VisibilityInfo getVisibilityInfo() { return dm.getVisibilityInfo(); } /** * A utility method to compare values while respecting null, used for caching * @param cache * @param val */ protected <T> boolean compare(T cache, T val) { if (cache == val) return true; // We tested above for the both-null case, if only one is null, not equal if (cache == null || val == null) return false; return cache.equals(val); } protected static boolean dirty_tags(HashMap<String, Tag> cache, HashMap<String, Tag> val) { if (cache.size() != val.size()) return true; for (Entry<String, Tag> each : cache.entrySet()) { if (val.get(each.getKey()) != each.getValue()) return true; } return false; } /** * A utility method to compare values while respecting null, used for caching * @param cache * @param val */ protected static boolean dirty_vec3d(Vec3d cache, Vec3d val) { if (cache == val) return false; // We tested above for the both-null case, if only one is null, not equal if (cache == null || val == null) return true; return !cache.equals3(val); } /** * A utility method to compare values while respecting null, used for caching * @param cache * @param val */ protected static boolean dirty_vec4d(Vec4d cache, Vec4d val) { if (cache == val) return false; // We tested above for the both-null case, if only one is null, not equal if (cache == null || val == null) return true; return !cache.equals4(val); } /** * A utility method to compare values while respecting null, used for caching * @param cache * @param val */ protected static boolean dirty_col4d(Color4d cache, Color4d val) { if (cache == val) return false; // We tested above for the both-null case, if only one is null, not equal if (cache == null || val == null) return true; return !cache.equals4(val); } protected <T> boolean compareArray(T[] cache, T[] val) { if (cache == val) return true; // We tested above for the both-null case, if only one is null, not equal if (cache == null || val == null) return false; return Arrays.deepEquals(cache, val); } }