/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: TechConversionResult.java
* Technology Editor, conversion of technology libraries to technologies
* Written by Steven M. Rubin, Sun Microsystems.
*
* Copyright (c) 2007, 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.tecEdit;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.topology.NodeInst;
import com.sun.electric.database.variable.EditWindow_;
import com.sun.electric.tool.Job;
import com.sun.electric.util.TextUtils;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Class to define error messages that arise during technology creation.
* This class is necessary because errors occur during Jobs, but the display
* of the errors must be done on the client side. Therefore, objects of
* this class are passed from the server to the client for display.
*/
public class TechConversionResult implements Serializable
{
private boolean good;
private String errorMessage;
private NodeInst ni;
private Cell cell;
private List<Example> neList;
private Sample ns;
private double sampleCoord;
private boolean xDir;
public TechConversionResult() { good = true; }
/**
* Method to return the success status.
* @return true if the conversion failed.
*/
public boolean failed() { return !good; }
/**
* Method to mark a general error.
* @param ni the NodeInst that caused the error (may be null).
* @param cell the Cell where the error occurred (may be null).
* @param errorMessage the message to display.
*/
public void markError(NodeInst ni, Cell cell, String errorMessage)
{
good = false;
this.cell = cell;
this.ni = ni;
this.errorMessage = errorMessage;
}
/**
* Method to mark an error in determining stretching rules.
* @param neList the Examples that invoked the error.
* @param ns the Sample on the main Example that failed.
* @param cell the Cell with the error.
* @param sampleCoord the coordinate (in X or Y) where the failure occurred.
* @param xDir true for an X-axis error, false for Y-axis.
*/
public void markStretchProblem(List<Example> neList, Sample ns, Cell cell, double sampleCoord, boolean xDir)
{
good = false;
this.neList = neList;
this.ns = ns;
this.cell = cell;
this.sampleCoord = sampleCoord;
this.xDir = xDir;
}
/**
* Method to return the error message associated with this conversion.
* @return the error message associated with this conversion.
*/
public String getErrorMessage()
{
String fullErrorMsg = errorMessage;
if (cell != null)
{
String prefixErrorMsg = "Cell " + cell.describe(false);
if (ni != null) prefixErrorMsg += ", node " + ni.describe(false);
fullErrorMsg = prefixErrorMsg + ": " + fullErrorMsg;
}
return fullErrorMsg;
}
/**
* Method to highlight the error.
*/
public void showError()
{
// more complex errors have their own way to display
if (errorMessage == null)
{
explainStretchProblem();
return;
}
if (cell != null)
{
Job.getUserInterface().displayCell(cell);
EditWindow_ wnd = Job.getUserInterface().getCurrentEditWindow_();
if (wnd != null && ni != null)
{
wnd.clearHighlighting();
wnd.addElectricObject(ni, cell);
wnd.finishedHighlighting();
}
}
String msg = getErrorMessage();
Job.getUserInterface().showErrorMessage(msg, "Analysis Failure");
System.out.println(msg);
}
/**
* Method to explain a stretching rule problem.
* @param neList the list of Examples
* @param ns the sample in the main Example
* @param np the Cell.
* @param sampleCoord the coordinate of the stretching failure.
* @param xDir true if this is in the X direction, false for Y.
*/
private void explainStretchProblem()
{
Job.getUserInterface().displayCell(cell);
EditWindow_ wnd = Job.getUserInterface().getCurrentEditWindow_();
if (wnd == null) return;
wnd.clearHighlighting();
Map<Sample,double[]> factorList = new HashMap<Sample,double[]>();
double[] factors = showSampleInExample(wnd, ns, neList.get(0), sampleCoord, xDir, true);
factorList.put(ns, factors);
for(int n=1; n<neList.size(); n++)
{
Example e = neList.get(n);
for(Sample s : e.samples)
{
if (s.assoc == ns)
{
factors = showSampleInExample(wnd, s, e, sampleCoord, xDir, false);
factorList.put(s, factors);
}
}
}
// see if the offending sample can be guessed
Set<Sample> allSamples = factorList.keySet();
Map<Sample,Integer> offendingSample = new HashMap<Sample,Integer>();
for(int i=0; i<4; i++)
{
double v1 = 0, v2 = 0;
int v1Count = 0, v2Count = 0;
Sample s1 = null, s2 = null;
for(Sample s : allSamples)
{
factors = factorList.get(s);
if (v1Count == 0)
{
v1Count++;
v1 = factors[i];
s1 = s;
continue;
}
if (v1 == factors[i])
{
v1Count++;
continue;
}
if (v2Count == 0)
{
v2Count++;
v2 = factors[i];
s2 = s;
continue;
}
if (v2 == factors[i])
{
v2Count++;
continue;
}
}
if (v1Count+v2Count != allSamples.size()) continue;
Sample offendingSamp = null;
if (v1Count == 1) offendingSamp = s1;
if (v2Count == 1) offendingSamp = s2;
if (offendingSamp != null)
{
Integer prev = offendingSample.get(offendingSamp);
if (prev == null) prev = new Integer(1); else prev = new Integer(prev.intValue()+1);
offendingSample.put(offendingSamp, prev);
}
}
Sample offendingSamp = null;
int num = 0;
for(Sample s : offendingSample.keySet())
{
int numTimes = offendingSample.get(s).intValue();
if (numTimes > num)
{
num = numTimes;
offendingSamp = s;
}
}
// show the offending sample if found, all if not
String err = "Cannot determine " + (xDir?"X":"Y") + " stretching rule for layer " + Info.getSampleName(ns.layer) +
" in " + cell;
String additional = "\nOne of these stretching rules must be the same for every example:" +
"\nOut (the distance from the center to the point)" +
"\nIn (the distance from the edge to the point)" +
"\nPer (the percentage of the point from the center to the edge)" +
"\nOpp (the distance from the opposite edge to the point)";
if (offendingSamp == null)
{
offendingSamp = ns;
for(Sample s : allSamples)
wnd.addElectricObject(s.node, cell);
} else
{
wnd.addElectricObject(offendingSamp.node, cell);
additional += "\n\nThe error is probably with node " + offendingSamp.node.describe(false);
}
wnd.finishedHighlighting();
Job.getUserInterface().showErrorMessage(err+additional, "Analysis Failure");
System.out.println(err);
}
private double[] showSampleInExample(EditWindow_ wnd, Sample ns, Example e, double sampleCoord,
boolean xDir, boolean mainExample)
{
Cell cell = ns.node.getParent();
Rectangle2D exampleBounds = wnd.getCell().getBounds();
double singleStep = Math.max(exampleBounds.getHeight(), exampleBounds.getWidth()) / 45;
double doubleStep = singleStep * 2;
double halfStep = singleStep / 2;
double exampleHalfSize = (xDir ? (e.hx - e.lx) : (e.hy - e.ly)) / 2;
double exampleCtr = (xDir ? (e.lx + e.hx) : (e.ly + e.hy)) / 2;
// first draw bold bars above/below example at the edges and center
Point2D pt1 = makeDisplayPoint(e, 0, exampleCtr-exampleHalfSize, xDir);
Point2D pt2 = makeDisplayPoint(e, singleStep, exampleCtr-exampleHalfSize, xDir);
wnd.addHighlightLine(pt1, pt2, cell, true, false);
pt1 = makeDisplayPoint(e, 0, exampleCtr+exampleHalfSize, xDir);
pt2 = makeDisplayPoint(e, singleStep, exampleCtr+exampleHalfSize, xDir);
wnd.addHighlightLine(pt1, pt2, cell, true, false);
pt1 = makeDisplayPoint(e, 0, exampleCtr, xDir);
pt2 = makeDisplayPoint(e, doubleStep, exampleCtr, xDir);
wnd.addHighlightLine(pt1, pt2, cell, true, false);
Rectangle2D mainSampleBounds = ns.assoc.node.getBounds();
Rectangle2D sampleBounds = ns.node.getBounds();
boolean adjusted = false;
if (mainExample) adjusted = true; else
{
if (xDir)
{
if (sampleCoord == mainSampleBounds.getMinX())
{ adjusted = true; sampleCoord = sampleBounds.getMinX(); }
if (sampleCoord == mainSampleBounds.getMaxX())
{ adjusted = true; sampleCoord = sampleBounds.getMaxX(); }
} else
{
if (sampleCoord == mainSampleBounds.getMinY())
{ adjusted = true; sampleCoord = sampleBounds.getMinY(); }
if (sampleCoord == mainSampleBounds.getMaxY())
{ adjusted = true; sampleCoord = sampleBounds.getMaxY(); }
}
}
// determine the 4 stretching factors (out, in, %, opp)
double [] factors = new double[4];
double percentOut = Math.abs(sampleCoord-exampleCtr) / exampleHalfSize;
double distOut = Math.abs(sampleCoord-exampleCtr);
factors[0] = distOut;
factors[1] = exampleHalfSize-distOut;
factors[2] = Math.round(percentOut*100);
factors[3] = distOut+exampleHalfSize;
if (adjusted)
{
double arrowCoord = sampleCoord;
double arrowCoordInv = sampleCoord;
double farEnd, closeEnd;
if (sampleCoord > exampleCtr)
{
arrowCoord -= singleStep;
arrowCoordInv += singleStep;
closeEnd = exampleCtr + exampleHalfSize;
farEnd = exampleCtr - exampleHalfSize;
} else
{
arrowCoord += singleStep;
arrowCoordInv -= singleStep;
closeEnd = exampleCtr - exampleHalfSize;
farEnd = exampleCtr + exampleHalfSize;
}
// now draw bar where problem is
pt1 = makeDisplayPoint(e, halfStep, sampleCoord, xDir);
pt2 = makeDisplayPoint(e, doubleStep+singleStep, sampleCoord, xDir);
wnd.addHighlightLine(pt1, pt2, cell, false, false);
// draw arrow from opposite edge to problem location
if (sampleCoord != farEnd)
{
pt1 = makeDisplayPoint(e, singleStep, farEnd, xDir);
pt2 = makeDisplayPoint(e, singleStep, sampleCoord, xDir);
wnd.addHighlightLine(pt1, pt2, cell, false, false);
pt1 = makeDisplayPoint(e, singleStep, sampleCoord, xDir);
pt2 = makeDisplayPoint(e, singleStep-singleStep/3, arrowCoord, xDir);
wnd.addHighlightLine(pt1, pt2, cell, false, false);
pt1 = makeDisplayPoint(e, singleStep, sampleCoord, xDir);
pt2 = makeDisplayPoint(e, singleStep+singleStep/3, arrowCoord, xDir);
wnd.addHighlightLine(pt1, pt2, cell, false, false);
}
// draw arrow from center to problem location
if (sampleCoord != exampleCtr)
{
pt1 = makeDisplayPoint(e, doubleStep, exampleCtr, xDir);
pt2 = makeDisplayPoint(e, doubleStep, sampleCoord, xDir);
wnd.addHighlightLine(pt1, pt2, cell, false, false);
pt1 = makeDisplayPoint(e, doubleStep, sampleCoord, xDir);
pt2 = makeDisplayPoint(e, doubleStep-singleStep/3, arrowCoord, xDir);
wnd.addHighlightLine(pt1, pt2, cell, false, false);
pt1 = makeDisplayPoint(e, doubleStep, sampleCoord, xDir);
pt2 = makeDisplayPoint(e, doubleStep+singleStep/3, arrowCoord, xDir);
wnd.addHighlightLine(pt1, pt2, cell, false, false);
}
// draw arrow from close edge to problem location
if (sampleCoord != closeEnd)
{
pt1 = makeDisplayPoint(e, doubleStep, closeEnd, xDir);
pt2 = makeDisplayPoint(e, doubleStep, sampleCoord, xDir);
wnd.addHighlightLine(pt1, pt2, cell, false, false);
pt1 = makeDisplayPoint(e, doubleStep, sampleCoord, xDir);
pt2 = makeDisplayPoint(e, doubleStep-singleStep/3, arrowCoordInv, xDir);
wnd.addHighlightLine(pt1, pt2, cell, false, false);
pt1 = makeDisplayPoint(e, doubleStep, sampleCoord, xDir);
pt2 = makeDisplayPoint(e, doubleStep+singleStep/3, arrowCoordInv, xDir);
wnd.addHighlightLine(pt1, pt2, cell, false, false);
}
// write factors
pt2 = makeDisplayPoint(e, doubleStep+halfStep, xDir ? exampleCtr : (exampleCtr-halfStep), xDir);
wnd.addHighlightMessage(cell, "Out="+TextUtils.formatDouble(factors[0])+
" Per="+((int)factors[2]), pt2);
pt2 = makeDisplayPoint(e, doubleStep-halfStep, xDir ? closeEnd+halfStep : (closeEnd-halfStep), xDir);
wnd.addHighlightMessage(cell, "In="+TextUtils.formatDouble(factors[1]), pt2);
pt2 = makeDisplayPoint(e, singleStep+halfStep, xDir ? farEnd : (farEnd-halfStep), xDir);
wnd.addHighlightMessage(cell, "Opp="+TextUtils.formatDouble(factors[3]), pt2);
}
return factors;
}
private Point2D makeDisplayPoint(Example e, double offsetOrtho, double sampleCoord, boolean xDir)
{
if (xDir) return new Point2D.Double(sampleCoord, e.hy+offsetOrtho);
return new Point2D.Double(e.hx+offsetOrtho, sampleCoord);
}
}