/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: LayerVisibility.java
*
* Copyright (c) 2009, 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.tool.user.ui;
import com.sun.electric.database.text.PrefPackage;
import com.sun.electric.technology.ArcProto;
import com.sun.electric.technology.Layer;
import com.sun.electric.technology.PrimitiveNode;
import com.sun.electric.technology.TechPool;
import com.sun.electric.technology.Technology;
import com.sun.electric.tool.Job;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.prefs.Preferences;
/**
* Class represents visibility of Layers.
* It is possible to have multiple instances of this class,
* for example for each EditWindow.
*/
public class LayerVisibility extends PrefPackage {
private static final String KEY_VISIBILITY = "VisibilityOf";
private static final String KEY_LAYER_ORDER = "LayerOrderfor";
/**
* The "standard" LayerVisibility
*/
private static LayerVisibility stdLayerVisibility;
private final TechPool techPool;
private HashSet<PrimitiveNode> visibleNodes;
private HashSet<ArcProto> visibleArcs;
private boolean visibilityChanged;
private final TechData[] techData;
private class TechData {
private final Technology tech;
private String layerOrder = "";
private final boolean[] visibleLayers;
private final boolean[] highlightedLayers;
private final float[] layerDrawingOpacity;
TechData(Technology tech) {
this.tech = tech;
assert techPool.getTech(tech.getId()) == tech;
int numLayers = tech.getNumLayers();
visibleLayers = new boolean[numLayers];
Arrays.fill(visibleLayers, true);
highlightedLayers = new boolean[numLayers];
layerDrawingOpacity = new float[numLayers];
setDefaultOpacity();
}
/**
* Method to automatically set the opacity of each layer in a Technology.
* @param tech the Technology to set.
*/
private void setDefaultOpacity() {
for(Iterator<Layer> it = tech.getLayers(); it.hasNext(); ) {
Layer layer = it.next();
int layerIndex = layer.getIndex();
float opacity = getDefaultOpacity(layer);
if (opacity == layerDrawingOpacity[layerIndex]) continue;
visibilityChanged = true;
layerDrawingOpacity[layerIndex] = opacity;
}
}
}
public LayerVisibility(boolean factory) {
this(factory, TechPool.getThreadTechPool());
}
LayerVisibility(boolean factory, TechPool techPool) {
super(factory);
this.techPool = techPool;
int maxTechIndex = -1;
for (Technology tech: techPool.values())
maxTechIndex = Math.max(maxTechIndex, tech.getId().techIndex);
techData = new TechData[maxTechIndex+1];
for (Technology tech: techPool.values())
techData[tech.getId().techIndex] = new TechData(tech);
visibilityChanged = false;
if (factory) return;
Preferences techPrefs = getPrefRoot().node(TECH_NODE);
for (Technology tech: techPool.values()) {
TechData td = getTechData(tech);
String layerOrderKey = getKey(KEY_LAYER_ORDER, tech.getId());
td.layerOrder = techPrefs.get(layerOrderKey, "");
for (Iterator<Layer> it = tech.getLayers(); it.hasNext(); ) {
Layer layer = it.next();
assert !layer.isPseudoLayer();
String visibilityKey = getKey(KEY_VISIBILITY, layer.getId());
td.visibleLayers[layer.getIndex()] = techPrefs.getBoolean(visibilityKey, true);
}
}
}
@Override
public void putPrefs(Preferences prefRoot, boolean removeDefaults) {
super.putPrefs(prefRoot, removeDefaults);
Preferences techPrefs = prefRoot.node(TECH_NODE);
for (Technology tech: techPool.values()) {
TechData td = getTechData(tech);
String layerOrder = td.layerOrder;
String layerOrderKey = getKey(KEY_LAYER_ORDER, tech.getId());
if (removeDefaults && layerOrder.length() == 0)
techPrefs.remove(layerOrderKey);
else
techPrefs.put(layerOrderKey, layerOrder);
for (Iterator<Layer> it = tech.getLayers(); it.hasNext(); ) {
Layer layer = it.next();
boolean visible = isVisible(layer);
String visibilityKey = getKey(KEY_VISIBILITY, layer.getId());
if (removeDefaults && visible)
techPrefs.remove(visibilityKey);
else
techPrefs.putBoolean(visibilityKey, visible);
}
}
}
/**
* Method to set whether this Layer is visible
* @param layer the Layer
* @param visible true if the Layer is to be visible.
*/
void setVisible(Layer layer, boolean visible) {
Map<Layer,Boolean> visibilityChange = Collections.singletonMap(layer, Boolean.valueOf(visible));
setVisible(visibilityChange);
}
/**
* Method to update visibility of some Layers
* @param visibilityChange a map with new values of Layers to update.
*/
void setVisible(Map<Layer,Boolean> visibilityChange) {
boolean changed = false;
for (Map.Entry<Layer,Boolean> e: visibilityChange.entrySet()) {
Layer layer = e.getKey();
TechData td = getTechData(layer.getTechnology());
int layerIndex = layer.getIndex();
boolean visible = e.getValue().booleanValue();
if (visible == td.visibleLayers[layerIndex]) continue;
changed = true;
td.visibleLayers[layerIndex] = visible;
}
if (!changed) return;
visibilityChanged = true;
visibleNodes = null;
visibleArcs = null;
}
/**
* Method to set whether this Layer is highlighted.
* Highlighted layers are drawn brighter.
* @param layer the Layer
* @param highlighted true if the Layer is to be highlighteded.
*/
void setHighlighted(Layer layer, boolean highlighted) {
if (highlighted == isHighlighted(layer)) return;
visibilityChanged = true;
getTechData(layer.getTechnology()).highlightedLayers[layer.getIndex()] = highlighted;
}
/**
* Method to return a list of layers that are saved for specified Technology.
* The saved layers are used in the "Layers" tab (which can be user-rearranged).
* @param tech specified Technology
* @return a list of layers for this Technology in the saved order.
*/
public List<Layer> getSavedLayerOrder(Technology tech)
{
TechData td = getTechData(tech);
String order = td.layerOrder;
if (order == null || order.length() == 0) return null;
int pos = 0;
List<Layer> layers = new ArrayList<Layer>();
while (pos < order.length())
{
// get the next layer name in the string
int end = order.indexOf(',', pos);
if (end < 0) break;
String layerName = order.substring(pos, end);
pos = end + 1;
// find the layer and add it to the list
int colonPos = layerName.indexOf(':');
Technology t = tech;
if (colonPos >= 0)
{
String techName = layerName.substring(0, colonPos);
t = techPool.findTechnology(techName);
if (t == null) continue;
layerName = layerName.substring(colonPos+1);
}
Layer layer = t.findLayer(layerName);
if (layer != null)
layers.add(layer);
}
return layers;
}
/**
* Method to save a list of layers for this Technology in a preferred order.
* This ordering is managed by the "Layers" tab which users can rearrange.
* @param layers a list of layers for this Technology in a preferred order.
*/
public void setSavedLayerOrder(Technology tech, List<Layer> layers)
{
TechData td = getTechData(tech);
if (layers.isEmpty()) {
td.layerOrder = "";
} else {
StringBuffer sb = new StringBuffer();
for(Layer lay : layers)
{
if (lay.getTechnology() != tech) sb.append(lay.getTechnology().getTechName() + ":");
sb.append(lay.getName() + ",");
}
td.layerOrder = sb.toString();
}
visibilityChanged = true;
}
public void setOpacity(Layer layer, double opacity) {
TechData td = getTechData(layer.getTechnology());
float fOpacity = (float)opacity;
int layerIndex = layer.getIndex();
if (fOpacity == td.layerDrawingOpacity[layerIndex]) return;
visibilityChanged = true;
td.layerDrawingOpacity[layerIndex] = fOpacity;
}
private void reset() {
visibilityChanged = true;
for (TechData td: techData) {
if (td == null) continue;
Arrays.fill(td.visibleLayers, true);
Arrays.fill(td.highlightedLayers, false);
td.layerOrder = "";
}
visibleNodes = null;
visibleArcs = null;
}
private void resetOpacity() {
for (TechData td: techData) {
if (td == null) continue;
td.setDefaultOpacity();
}
}
void resetOpacity(Technology tech) {
getTechData(tech).setDefaultOpacity();
}
/**
* Report change status and clear it
* @return true if some Layer has changed visibility or highlight status
*/
boolean clearChanged() {
boolean oldChanged = visibilityChanged;
visibilityChanged = false;
return oldChanged;
}
/**
* Method to tell whether a Layer is visible.
* @param layer specified layer
* @return true if this Layer is visible.
*/
public boolean isVisible(Layer layer) {
TechData td = getTechData(layer.getTechnology());
return td.visibleLayers[layer.getIndex()];
}
/**
* Method to tell whether a PrimitiveNode is visible.
* @param pn specified PrimitiveNode
* @return true if the PrimitiveNode is visible.
*/
public boolean isVisible(PrimitiveNode pn) {
if (visibleNodes == null)
gatherVisiblePrims();
return visibleNodes.contains(pn);
}
/**
* Method to tell whether an ArcProto is visible.
* @param ap specified ArcProto
* @return true if the ArcProto is visible.
*/
public boolean isVisible(ArcProto ap) {
if (visibleArcs == null)
gatherVisiblePrims();
return visibleArcs.contains(ap);
}
/**
* Method to tell whether a Layer is highlighted.
* Highlighted layers are drawn brighter
* @param layer specified layer
* @return true if this Layer is highlighted.
*/
public boolean isHighlighted(Layer layer) {
TechData td = getTechData(layer.getTechnology());
return td.highlightedLayers[layer.getIndex()];
}
public float getOpacity(Layer layer) {
return getTechData(layer.getTechnology()).layerDrawingOpacity[layer.getIndex()];
}
private void gatherVisiblePrims() {
HashSet<PrimitiveNode> visibleNodes = new HashSet<PrimitiveNode>();
HashSet<ArcProto> visibleArcs = new HashSet<ArcProto>();
for (Technology tech: techPool.values()) {
for(Iterator<PrimitiveNode> nIt = tech.getNodes(); nIt.hasNext(); ) {
PrimitiveNode pn = nIt.next();
boolean visible = false;
for (Iterator<Layer> lIt = pn.getLayerIterator(); lIt.hasNext(); ) {
Layer layer = lIt.next();
if (isVisible(layer)) {
visible = true;
break;
}
}
if (visible)
visibleNodes.add(pn);
}
for(Iterator<ArcProto> aIt = tech.getArcs(); aIt.hasNext(); ) {
ArcProto ap = aIt.next();
boolean visible = false;
for(Iterator<Layer> lIt = ap.getLayerIterator(); lIt.hasNext(); ) {
Layer layer = lIt.next();
if (isVisible(layer)) {
visible = true;
break;
}
}
if (visible)
visibleArcs.add(ap);
}
}
this.visibleNodes = visibleNodes;
this.visibleArcs = visibleArcs;
}
/**
* Method to automatically compute the opacity of a layer.
* @param layer the Layer to find opacity.
*/
private float getDefaultOpacity(Layer layer)
{
Layer.Function fun = layer.getFunction();
int extra = layer.getFunctionExtras();
double opacity = 0.4;
if (fun.isMetal())
{
opacity = 0.75 - fun.getLevel() * 0.05;
} else if (fun.isContact())
{
if ((extra&Layer.Function.CONMETAL) != 0) opacity = 0.7; else
opacity = 1;
} else if (fun == Layer.Function.OVERGLASS)
{
opacity = 0.2;
} else if (fun == Layer.Function.POLY1 ||
fun == Layer.Function.POLY2 ||
fun == Layer.Function.POLY3 ||
fun == Layer.Function.GATE ||
fun == Layer.Function.DIFF ||
fun == Layer.Function.DIFFP ||
fun == Layer.Function.DIFFN ||
fun == Layer.Function.WELL ||
fun == Layer.Function.WELLP ||
fun == Layer.Function.WELLN ||
fun == Layer.Function.IMPLANT ||
fun == Layer.Function.IMPLANTN ||
fun == Layer.Function.IMPLANTP) {
// lowest level layers should all be opaque
opacity = 1.0;
} else if (layer == techPool.getGeneric().glyphLay) {
opacity = 0.1; // essential bounds
}
return (float)opacity;
}
private TechData getTechData(Technology tech) {
TechData td = techData[tech.getId().techIndex];
assert td.tech == tech;
return td;
}
/**
* Returns "standard" LayerVisibility
* @return "standard" LayerVisibility
*/
public static LayerVisibility getLayerVisibility() {
assert Job.isClientThread();
return stdLayerVisibility;
}
/**
* Save "standard" LayerVisibility in Preferences
*/
public static void preserveVisibility() {
assert Job.isClientThread();
if (stdLayerVisibility == null) return;
stdLayerVisibility.putPrefs(getPrefRoot(), true);
}
/**
* Load "standard" from Preferences
*/
public static LayerVisibility loadPreferences() {
assert Job.isClientThread();
stdLayerVisibility = new LayerVisibility(false);
return stdLayerVisibility;
}
/**
* Reset "standard" LayerVisibility to factory values.
*/
public static void factoryReset() {
assert Job.isClientThread();
if (stdLayerVisibility == null) return;
stdLayerVisibility.reset();
}
/**
* Reset "standard" LayerVisibility to factory values.
*/
public static void setDefaultOpacity() {
assert Job.isClientThread();
if (stdLayerVisibility == null) return;
stdLayerVisibility.resetOpacity();
}
}