/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: DXF.java
* Input/output tool: DXF output
* Written by Steven M. Rubin, Sun Microsystems.
*
* Copyright (c) 2004, 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.io.output;
import com.sun.electric.database.geometry.Poly;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.prototype.NodeProto;
import com.sun.electric.database.topology.NodeInst;
import com.sun.electric.database.variable.VarContext;
import com.sun.electric.database.variable.Variable;
import com.sun.electric.technology.Layer;
import com.sun.electric.technology.PrimitiveNode;
import com.sun.electric.technology.Technology;
import com.sun.electric.technology.technologies.Artwork;
import com.sun.electric.tool.io.IOTool;
import com.sun.electric.util.TextUtils;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
* This is the netlister for DXF.
*/
public class DXF extends Output
{
/** key of Variable holding DXF layer name. */ public static final Variable.Key DXF_LAYER_KEY = Variable.newKey("IO_dxf_layer");
/** key of Variable holding DXF header text. */ public static final Variable.Key DXF_HEADER_TEXT_KEY = Variable.newKey("IO_dxf_header_text");
/** key of Variable holding DXF header information. */ public static final Variable.Key DXF_HEADER_ID_KEY = Variable.newKey("IO_dxf_header_ID");
private int dxfEntityHandle;
private Set<Cell> cellsSeen;
private TextUtils.UnitScale dxfDispUnit;
private String defaultDXFLayerName;
private static String [] ignorefromheader = {"$DWGCODEPAGE", "$HANDSEED", "$SAVEIMAGES"};
private DXFPreferences localPrefs;
public static class DXFPreferences extends OutputPreferences
{
// DXF Settings
int dxfScale = IOTool.getDXFScale();
public Technology tech;
public DXFPreferences(boolean factory) {
super(factory);
tech = Technology.getCurrent();
}
@Override
public Output doOutput(Cell cell, VarContext context, String filePath)
{
DXF out = new DXF(this);
if (out.openTextOutputStream(filePath)) return out.finishWrite();
out.writeDXF(cell);
if (out.closeTextOutputStream()) return out.finishWrite();
System.out.println(filePath + " written");
return out.finishWrite();
}
}
/**
* Creates a new instance of the DXF netlister.
*/
DXF(DXFPreferences dp) { localPrefs = dp; }
private void writeDXF(Cell cell)
{
// set the scale
dxfDispUnit = TextUtils.UnitScale.findFromIndex(localPrefs.dxfScale);
// write the header
Variable varheadertext = cell.getLibrary().getVar(DXF_HEADER_TEXT_KEY);
Variable varheaderid = cell.getLibrary().getVar(DXF_HEADER_ID_KEY);
Layer defLay = Artwork.tech().findLayer("Graphics");
defaultDXFLayerName = defLay.getDXFLayer();
if (varheadertext != null && varheaderid != null)
{
printWriter.print(" 0\nSECTION\n");
printWriter.print(" 2\nHEADER\n");
int len = Math.min(varheadertext.getLength(), varheaderid.getLength());
for(int i=0; i<len; i++)
{
// remove entries that confuse the issues
String pt = (String)varheadertext.getObject(i);
int code = ((Integer)varheaderid.getObject(i)).intValue();
if (code == 9 && i <= len-2)
{
boolean found = false;
for(int j=0; j<ignorefromheader.length; j++)
{
if (pt.equals(ignorefromheader[j]) || pt.substring(1).equals(ignorefromheader[j]))
{
found = true;
break;
}
}
if (found)
{
i++;
continue;
}
}
// make sure Autocad version is correct
if (pt.equals("$ACADVER") && i <= len-2)
{
printWriter.print(getThreeDigit(code) + "\n" + pt + "\n");
printWriter.print(" 1\nAC1009\n");
i++;
continue;
}
printWriter.print(getThreeDigit(code) + "\n" + pt + "\n");
}
printWriter.print(" 0\nENDSEC\n");
}
// write any subcells
dxfEntityHandle = 0x100;
printWriter.print(" 0\nSECTION\n");
printWriter.print(" 2\nBLOCKS\n");
cellsSeen = new HashSet<Cell>();
for(Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
if (ni.isCellInstance())
{
NodeProto np = ni.getProto();
if (!cellsSeen.contains(np)) writeDXFCell((Cell)np, true);
}
}
printWriter.print(" 0\nENDSEC\n");
printWriter.print(" 0\nSECTION\n");
printWriter.print(" 2\nENTITIES\n");
writeDXFCell(cell, false);
printWriter.print(" 0\nENDSEC\n");
printWriter.print(" 0\nEOF\n");
}
/**
* Method to write the contents of cell "np". If "subCells" is nonzero, do a recursive
* descent through the subcells in this cell, writing out "block" definitions.
*/
private void writeDXFCell(Cell cell, boolean subCells)
{
if (subCells)
{
cellsSeen.add(cell);
for(Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
NodeProto np = ni.getProto();
if (ni.isCellInstance() && !cellsSeen.contains(np))
writeDXFCell((Cell)np, true);
}
printWriter.print(" 0\nBLOCK\n");
printWriter.print(" 2\n" + getDXFCellName(cell) + "\n");
printWriter.print(" 10\n0\n");
printWriter.print(" 20\n0\n");
printWriter.print(" 30\n0\n");
printWriter.print(" 70\n0\n");
}
for(Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
NodeProto np = ni.getProto();
// handle instances
if (ni.isCellInstance())
{
Cell subCell = (Cell)np;
printWriter.print(" 0\nINSERT\n");
printWriter.print(" 8\nUNKNOWN\n");
printWriter.print(" 5\n" + getThreeDigitHex(dxfEntityHandle++) + "\n");
printWriter.print(" 2\n" + getDXFCellName(subCell) + "\n");
Rectangle2D cellBounds = subCell.getBounds();
double xC = TextUtils.convertDistance(ni.getAnchorCenterX() - cellBounds.getCenterX(), localPrefs.tech, dxfDispUnit);
double yC = TextUtils.convertDistance(ni.getAnchorCenterY() - cellBounds.getCenterY(), localPrefs.tech, dxfDispUnit);
printWriter.print(" 10\n" + TextUtils.formatDouble(xC) + "\n");
printWriter.print(" 20\n" + TextUtils.formatDouble(yC) + "\n");
printWriter.print(" 30\n0\n");
double rot = ni.getAngle() / 10.0;
printWriter.print(" 50\n" + TextUtils.formatDouble(rot) + "\n");
continue;
}
// determine layer name for this node
String layerName = defaultDXFLayerName;
Variable var = ni.getVar(DXF_LAYER_KEY);
if (var != null) layerName = var.getPureValue(-1); else
{
// examine technology for proper layer name
if (!ni.isCellInstance())
{
PrimitiveNode pNp = (PrimitiveNode)ni.getProto();
Technology.NodeLayer [] nodeLayers = pNp.getNodeLayers();
for(int i=0; i<nodeLayers.length; i++)
{
Technology.NodeLayer nl = nodeLayers[i];
Layer layer = nl.getLayer();
String dxfLayerName = layer.getDXFLayer();
if (dxfLayerName != null && dxfLayerName.length() > 0)
{
layerName = dxfLayerName;
break;
}
}
}
}
// get the node center
double xC = ni.getAnchorCenterX();
double yC = ni.getAnchorCenterY();
// handle circles and arcs
if (np == Artwork.tech().circleNode || np == Artwork.tech().thickCircleNode)
{
double [] angles = ni.getArcDegrees();
double startOffset = angles[0];
double endAngle = angles[1];
if (startOffset != 0.0 || endAngle != 0.0) printWriter.print(" 0\nARC\n"); else
printWriter.print(" 0\nCIRCLE\n");
printWriter.print(" 8\n" + layerName + "\n");
printWriter.print(" 5\n" + getThreeDigitHex(dxfEntityHandle++) + "\n");
xC = TextUtils.convertDistance(xC, localPrefs.tech, dxfDispUnit);
yC = TextUtils.convertDistance(yC, localPrefs.tech, dxfDispUnit);
double zC = TextUtils.convertDistance(ni.getYSize() / 2, localPrefs.tech, dxfDispUnit);
printWriter.print(" 10\n" + TextUtils.formatDouble(xC) + "\n");
printWriter.print(" 20\n" + TextUtils.formatDouble(yC) + "\n");
printWriter.print(" 30\n0\n");
printWriter.print(" 40\n" + TextUtils.formatDouble(zC) + "\n");
if (startOffset != 0.0 || endAngle != 0.0)
{
startOffset = startOffset * 180.0 / Math.PI;
endAngle = endAngle * 180.0 / Math.PI;
double startAngle = ni.getAngle() / 10.0 + startOffset;
if (ni.isXMirrored() != ni.isYMirrored())
{
startAngle = 270.0 - startAngle - endAngle;
if (startAngle < 0.0) startAngle += 360.0;
}
endAngle += startAngle;
if (endAngle >= 360.0) endAngle -= 360.0;
printWriter.print(" 50\n" + TextUtils.formatDouble(startAngle) + "\n");
printWriter.print(" 51\n" + TextUtils.formatDouble(endAngle) + "\n");
}
continue;
}
// handle polygons
if (np == Artwork.tech().openedPolygonNode || np == Artwork.tech().openedDashedPolygonNode ||
np == Artwork.tech().closedPolygonNode)
{
AffineTransform trans = ni.rotateOut();
Point2D [] points = ni.getTrace();
int len = points.length;
if (len == 2 && np != Artwork.tech().closedPolygonNode)
{
// line
printWriter.print(" 0\nLINE\n");
printWriter.print(" 8\n" + layerName + "\n");
printWriter.print(" 5\n" + getThreeDigitHex(dxfEntityHandle++) + "\n");
Point2D pt = new Point2D.Double(points[0].getX() + xC, points[0].getY() + yC);
trans.transform(pt, pt);
double x = TextUtils.convertDistance(pt.getX(), localPrefs.tech, dxfDispUnit);
double y = TextUtils.convertDistance(pt.getY(), localPrefs.tech, dxfDispUnit);
printWriter.print(" 10\n" + TextUtils.formatDouble(x) + "\n");
printWriter.print(" 20\n" + TextUtils.formatDouble(y) + "\n");
printWriter.print(" 30\n0\n");
pt = new Point2D.Double(points[1].getX() + xC, points[1].getY() + yC);
trans.transform(pt, pt);
x = TextUtils.convertDistance(pt.getX(), localPrefs.tech, dxfDispUnit);
y = TextUtils.convertDistance(pt.getY(), localPrefs.tech, dxfDispUnit);
printWriter.print(" 11\n" + TextUtils.formatDouble(x) + "\n");
printWriter.print(" 21\n" + TextUtils.formatDouble(y) + "\n");
printWriter.print(" 31\n0\n");
} else
{
// should write a polyline here
for(int i=0; i<len-1; i++)
{
// line
if (points[i] == null || points[i+1] == null) continue;
printWriter.print(" 0\nLINE\n");
printWriter.print(" 8\n" + layerName + "\n");
printWriter.print(" 5\n" + getThreeDigitHex(dxfEntityHandle++) + "\n");
Point2D pt = new Point2D.Double(points[i].getX() + xC, points[i].getY() + yC);
trans.transform(pt, pt);
double x = TextUtils.convertDistance(pt.getX(), localPrefs.tech, dxfDispUnit);
double y = TextUtils.convertDistance(pt.getY(), localPrefs.tech, dxfDispUnit);
printWriter.print(" 10\n" + TextUtils.formatDouble(x) + "\n");
printWriter.print(" 20\n" + TextUtils.formatDouble(y) + "\n");
printWriter.print(" 30\n0\n");
pt = new Point2D.Double(points[i+1].getX() + xC, points[i+1].getY() + yC);
trans.transform(pt, pt);
x = TextUtils.convertDistance(pt.getX(), localPrefs.tech, dxfDispUnit);
y = TextUtils.convertDistance(pt.getY(), localPrefs.tech, dxfDispUnit);
printWriter.print(" 11\n" + TextUtils.formatDouble(x) + "\n");
printWriter.print(" 21\n" + TextUtils.formatDouble(y) + "\n");
printWriter.print(" 31\n0\n");
}
if (np == Artwork.tech().closedPolygonNode)
{
printWriter.print(" 0\nLINE\n");
printWriter.print(" 8\n" + layerName + "\n");
printWriter.print(" 5\n" + getThreeDigitHex(dxfEntityHandle++) + "\n");
Point2D pt = new Point2D.Double(points[len-1].getX() + xC, points[len-1].getY() + yC);
trans.transform(pt, pt);
double x = TextUtils.convertDistance(pt.getX(), localPrefs.tech, dxfDispUnit);
double y = TextUtils.convertDistance(pt.getY(), localPrefs.tech, dxfDispUnit);
printWriter.print(" 10\n" + TextUtils.formatDouble(x) + "\n");
printWriter.print(" 20\n" + TextUtils.formatDouble(y) + "\n");
printWriter.print(" 30\n0\n");
pt = new Point2D.Double(points[0].getX() + xC, points[0].getY() + yC);
trans.transform(pt, pt);
x = TextUtils.convertDistance(pt.getX(), localPrefs.tech, dxfDispUnit);
y = TextUtils.convertDistance(pt.getY(), localPrefs.tech, dxfDispUnit);
printWriter.print(" 11\n" + TextUtils.formatDouble(x) + "\n");
printWriter.print(" 21\n" + TextUtils.formatDouble(y) + "\n");
printWriter.print(" 31\n0\n");
}
}
continue;
}
// write all other nodes
Poly [] polys = ni.getProto().getTechnology().getShapeOfNode(ni);
AffineTransform trans = ni.rotateOut();
for(int i=0; i<polys.length; i++)
{
Poly poly = polys[i];
poly.transform(trans);
if (poly.getStyle() == Poly.Type.FILLED)
{
printWriter.print(" 0\nSOLID\n");
printWriter.print(" 8\n" + layerName + "\n");
Point2D [] points = poly.getPoints();
for(int j=0; j<points.length; j++)
{
printWriter.print(" 1" + j + "\n" + TextUtils.formatDouble(points[j].getX()) + "\n");
printWriter.print(" 2" + j + "\n" + TextUtils.formatDouble(points[j].getY()) + "\n");
printWriter.print(" 3" + j + "\n0\n");
}
}
}
}
if (subCells)
printWriter.print(" 0\nENDBLK\n");
}
private String getDXFCellName(Cell cell)
{
if (cell.getName().equalsIgnoreCase(cell.getLibrary().getName()))
{
// use another name
String buf = null;
for(int i=1; i<1000; i++)
{
buf = cell.getName() + i;
boolean found = false;
for(Iterator<Cell> it = cell.getLibrary().getCells(); it.hasNext(); )
{
Cell oCell = it.next();
if (oCell.getName().equalsIgnoreCase(buf)) { found = true; break; }
}
if (!found) break;
}
return buf;
}
// just return the cell name
return cell.getName();
}
private String getThreeDigit(int value)
{
String result = Integer.toString(value);
while (result.length() < 3) result = " " + result;
return result;
}
private String getThreeDigitHex(int value)
{
String result = Integer.toHexString(value).toUpperCase();
while (result.length() < 3) result = " " + result;
return result;
}
}