/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: DXF.java
* Input/output tool: DXF input
* Written by Steven M. Rubin, Sun Microsystems.
*
* Copyright (c) 2004 Sun Microsystems and Static Free Software
*
* 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.input;
import com.sun.electric.database.geometry.EPoint;
import com.sun.electric.database.geometry.Orientation;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.EDatabase;
import com.sun.electric.database.hierarchy.Library;
import com.sun.electric.database.hierarchy.View;
import com.sun.electric.database.id.CellId;
import com.sun.electric.database.prototype.NodeProto;
import com.sun.electric.database.text.TextUtils;
import com.sun.electric.database.topology.NodeInst;
import com.sun.electric.database.variable.TextDescriptor;
import com.sun.electric.database.variable.Variable;
import com.sun.electric.technology.Layer;
import com.sun.electric.technology.Technology;
import com.sun.electric.technology.technologies.Artwork;
import com.sun.electric.technology.technologies.Generic;
import com.sun.electric.tool.Job;
import com.sun.electric.tool.io.IOTool;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* This class reads files in DEF files.
*/
public class DXF extends Input
{
private static class DXFLayer
{
private String layerName;
private int layerColor;
private double layerRed, layerGreen, layerBlue;
private DXFLayer next;
}
private static class ForwardRef
{
private String refName;
private Cell parent;
private double x, y;
private int rot;
private double xSca, ySca;
private ForwardRef nextForwardRef;
}
private static class PolyPoint
{
double x, y, z;
double bulge;
}
private int lastGroupID;
private String lastText;
private boolean lastPairValid;
private int ignoredPoints, ignoredAttributeDefs, ignoredAttributes;
private int readPolyLines, readLines, readCircles, readSolids,
read3DFaces, readArcs, readInserts, readTexts;
private DXFLayer firstLayer;
private ForwardRef firstForwardRef;
private Cell mainCell;
private Cell curCell;
private List<String> headerText;
private List<Integer> headerID;
private int inputMode; /* 0: pairs not read, 1: normal pairs, 2: blank lines */
private HashSet<String> validLayerNames;
private HashSet<String> ignoredLayerNames;
private TextUtils.UnitScale dispUnit;
private int groupID;
private String text;
/** 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 DXFPreferences localPrefs;
public static class DXFPreferences extends InputPreferences
{
public boolean flattenHierarchy;
public boolean readAllLayers;
public int scale;
public DXFPreferences(boolean factory) { super(factory); }
public void initFromUserDefaults()
{
flattenHierarchy = IOTool.isDXFInputFlattensHierarchy();
readAllLayers = IOTool.isDXFInputReadsAllLayers();
scale = IOTool.getDXFScale();
}
@Override
public Library doInput(URL fileURL, Library lib, Technology tech, Map<Library,Cell> currentCells, Map<CellId,BitSet> nodesToExpand, Job job)
{
DXF in = new DXF(this);
if (in.openTextInput(fileURL)) return null;
// Librarys before loading
HashSet oldLibs = new HashSet();
for (Iterator<Library> it = Library.getLibraries(); it.hasNext(); )
oldLibs.add(it.next());
oldLibs.remove(lib);
lib = in.importALibrary(lib, tech, currentCells);
in.closeInput();
if (!flattenHierarchy) {
// Expand subCells
EDatabase database = EDatabase.currentDatabase();
for (Iterator<Library> it = Library.getLibraries(); it.hasNext(); ) {
Library l = it.next();
if (oldLibs.contains(l)) continue;
for (Iterator<Cell> cit = l.getCells(); cit.hasNext(); ) {
Cell cell =cit.next();
for (Iterator<NodeInst> nit = cell.getNodes(); nit.hasNext(); ) {
NodeInst ni = nit.next();
if (ni.isCellInstance())
database.addToNodes(nodesToExpand, ni);
}
}
}
}
return lib;
}
}
/**
* Creates a new instance of DXF.
*/
DXF(DXFPreferences ap) { localPrefs = ap; }
/**
* Method to import a library from disk.
* @param lib the library to fill
* @param currentCells this map will be filled with currentCells in Libraries found in library file
* @return the created library (null on error).
*/
@Override
protected Library importALibrary(Library lib, Technology tech, Map<Library,Cell> currentCells)
{
try
{
if (readLibrary(lib, currentCells)) return null;
} catch (IOException e) {}
return lib;
}
/**
* Method to read the DXF file into library "lib". Returns true on error.
*/
private boolean readLibrary(Library lib, Map<Library,Cell> currentCells)
throws IOException
{
// set the scale
setCurUnits();
// examine technology for acceptable DXF layer names
getAcceptableLayers();
// make the only cell in this library
mainCell = Cell.makeInstance(lib, lib.getName());
if (mainCell == null) return true;
currentCells.put(lib, mainCell);
curCell = mainCell;
headerID = new ArrayList<Integer>();
headerText = new ArrayList<String>();
ignoredLayerNames = new HashSet<String>();
// read the file
lastPairValid = false;
boolean err = false;
firstLayer = null;
firstForwardRef = null;
ignoredPoints = ignoredAttributes = ignoredAttributeDefs = 0;
readPolyLines = readLines = readCircles = readSolids = 0;
read3DFaces = readArcs = readInserts = readTexts = 0;
inputMode = 0;
for(;;)
{
if (getNextPair())
{
err = true;
break;
}
// must have section change here
if (groupID != 0)
{
System.out.println("Expected group 0 (start section) at line " + lineReader.getLineNumber());
err = true;
break;
}
if (text.equals("EOF")) break;
if (text.equals("SECTION"))
{
// see what section is coming
if (getNextPair()) break;
if (groupID != 2)
{
System.out.println("Expected group 2 (name) at line " + lineReader.getLineNumber());
err = true;
break;
}
if (text.equals("HEADER"))
{
if (err = readHeaderSection()) break;
continue;
}
if (text.equals("TABLES"))
{
if (err = readTablesSection()) break;
continue;
}
if (text.equals("BLOCKS"))
{
if (err = readEntities(lib)) break;
continue;
}
if (text.equals("ENTITIES"))
{
if (err = readEntities(lib)) break;
continue;
}
if (text.equals("CLASSES"))
{
if (err = ignoreSection()) break;
continue;
}
if (text.equals("OBJECTS"))
{
if (err = ignoreSection()) break;
continue;
}
}
System.out.println("Unknown section name (" + text + ") at line " + lineReader.getLineNumber());
err = true;
break;
}
// insert forward references
for(ForwardRef fr = firstForwardRef; fr != null; fr = fr.nextForwardRef)
{
// have to search by hand because of weird prototype names
Cell found = null;
for(Iterator<Cell> it = lib.getCells(); it.hasNext(); )
{
Cell cell = it.next();
if (cell.getName().equals(fr.refName)) { found = cell; break; }
}
if (found == null)
{
System.out.println("Cannot find block '" + fr.refName + "'");
continue;
}
if (localPrefs.flattenHierarchy)
{
if (extractInsert(found, fr.x, fr.y, fr.xSca, fr.ySca, fr.rot, fr.parent)) return true;
} else
{
if (fr.xSca != 1.0 || fr.ySca != 1.0)
{
found = getScaledCell(found, fr.xSca, fr.ySca);
if (found == null) return true;
}
Rectangle2D bounds = found.getBounds();
Orientation orient = Orientation.fromAngle(fr.rot*10);
NodeInst ni = NodeInst.makeInstance(found, new Point2D.Double(fr.x, fr.y), bounds.getWidth(), bounds.getHeight(), fr.parent, orient, null);
if (ni == null) return true;
// ni.setExpanded(true);
}
}
// save header with library
if (headerID.size() > 0)
{
int len = headerID.size();
Integer [] headerIDs = new Integer[len];
for(int i=0; i<len; i++) headerIDs[i] = headerID.get(i);
lib.newVar(DXF_HEADER_ID_KEY, headerIDs);
}
if (headerText.size() > 0)
{
int len = headerText.size();
String [] headerTexts = new String[len];
for(int i=0; i<len; i++) headerTexts[i] = headerText.get(i);
lib.newVar(DXF_HEADER_TEXT_KEY, headerTexts);
}
if (readPolyLines > 0 || readLines > 0 || readCircles > 0 ||
readSolids > 0 || read3DFaces > 0 || readArcs > 0 ||
readTexts > 0 || readInserts > 0)
{
String warning = "Read";
boolean first = true;
if (readPolyLines > 0)
{
if (first) warning += ","; first = false;
warning += " " + readPolyLines + " polylines";
}
if (readLines > 0)
{
if (first) warning += ","; first = false;
warning += " " + readLines + " lines";
}
if (readCircles > 0)
{
if (first) warning += ","; first = false;
warning += " " + readCircles + " circles";
}
if (readSolids > 0)
{
if (first) warning += ","; first = false;
warning += " " + readSolids + " solids";
}
if (read3DFaces > 0)
{
if (first) warning += ","; first = false;
warning += " " + read3DFaces + " 3d faces";
}
if (readArcs > 0)
{
if (first) warning += ","; first = false;
warning += " " + readArcs + " arcs";
}
if (readTexts > 0)
{
if (first) warning += ","; first = false;
warning += " " + readTexts + " texts";
}
if (readInserts > 0)
{
if (first) warning += ","; first = false;
warning += " " + readInserts + " inserts";
}
System.out.println(warning);
}
if (ignoredPoints > 0 || ignoredAttributes > 0 || ignoredAttributeDefs > 0)
{
String warning = "Ignored";
boolean first = true;
if (ignoredPoints > 0)
{
if (first) warning += ","; first = false;
warning += " " + ignoredPoints + " points";
}
if (ignoredAttributes > 0)
{
if (first) warning += ","; first = false;
warning += " " + ignoredAttributes + " attributes";
}
if (ignoredAttributeDefs > 0)
{
if (first) warning += ","; first = false;
warning += " " + ignoredAttributeDefs + " attribute definitions";
}
System.out.println(warning);
}
// say which layers were ignored
if (ignoredLayerNames.size() > 0)
{
String warning = "Ignored layers ";
boolean first = true;
for(String name : ignoredLayerNames)
{
if (!first) warning += ", ";
first = false;
warning += "'" + name + "'";
}
System.out.println(warning);
}
return err;
}
/**
* Method to read the next group ID and content pair from the file.
* Returns true on end-of-file.
*/
private boolean getNextPair()
throws IOException
{
if (lastPairValid)
{
text = lastText;
groupID = lastGroupID;
lastPairValid = false;
return false;
}
for(;;)
{
// read a line and get the group ID
lastText = getNextLine(false);
if (lastText == null)
{
System.out.println("Unexpected end-of-file at line " + lineReader.getLineNumber());
return true;
}
String groupLine = lastText.trim();
if (!TextUtils.isANumber(groupLine))
{
System.out.println("Invalid group ID on line " + lineReader.getLineNumber() + " (" + lastText + ")");
return true;
}
groupID = TextUtils.atoi(groupLine);
// ignore blank line if file is double-spaced
if (inputMode == 2) getNextLine(true);
// read a line and get the text
lastText = getNextLine(true);
if (lastText == null)
{
System.out.println("Unexpected end-of-file at line " + lineReader.getLineNumber());
return true;
}
text = lastText.trim();
// ignore blank line if file is double-spaced
if (inputMode == 2) getNextLine(true);
if (inputMode == 0)
{
// see if file is single or double spaced
if (lastText.length() != 0) inputMode = 1; else
{
inputMode = 2;
lastText = getNextLine(true);
if (lastText == null)
{
System.out.println("Unexpected end-of-file at line " + lineReader.getLineNumber());
return true;
}
text = lastText;
getNextLine(true);
}
}
// continue reading if a comment, otherwise quit
if (groupID != 999) break;
}
return false;
}
private String getNextLine(boolean canBeBlank)
throws IOException
{
for(;;)
{
String text = lineReader.readLine();
if (canBeBlank || text.length() != 0) return text;
}
}
/****************************************** READING SECTIONS ******************************************/
private boolean readHeaderSection()
throws IOException
{
// just save everything until the end-of-section
for(int line=0; ; line++)
{
if (getNextPair()) return true;
if (groupID == 0 && text.equals("ENDSEC")) break;
// save it
headerID.add(new Integer(groupID));
headerText.add(text);
}
return false;
}
private boolean readTablesSection()
throws IOException
{
// just ignore everything until the end-of-section
for(;;)
{
if (getNextPair()) return true;
// quit now if at the end of the table section
if (groupID == 0 && text.equals("ENDSEC")) break;
// must be a 'TABLE' declaration
if (groupID != 0 || !text.equals("TABLE")) continue;
// a table: see what kind it is
if (getNextPair()) return true;
if (groupID != 2 || !text.equals("LAYER")) continue;
// a layer table: ignore the size information
if (getNextPair()) return true;
if (groupID != 70) continue;
// read the layers
DXFLayer layer = null;
for(;;)
{
if (getNextPair()) return true;
if (groupID == 0 && text.equals("ENDTAB")) break;
if (groupID == 0 && text.equals("LAYER"))
{
// make a new layer
layer = new DXFLayer();
layer.layerName = null;
layer.layerColor = -1;
layer.layerRed = 1.0;
layer.layerGreen = 1.0;
layer.layerBlue = 1.0;
layer.next = firstLayer;
firstLayer = layer;
}
if (groupID == 2 && layer != null)
{
layer.layerName = text;
}
if (groupID == 62 && layer != null)
{
layer.layerColor = TextUtils.atoi(text);
DXFLayer found = null;
for(DXFLayer l = firstLayer; l != null; l = l.next)
{
if (l == layer) continue;
if (l.layerColor == layer.layerColor) { found = l; break; }
}
if (found != null)
{
layer.layerRed = found.layerRed;
layer.layerGreen = found.layerGreen;
layer.layerBlue = found.layerBlue;
} else
{
switch (layer.layerColor)
{
case 1: // red
layer.layerRed = 1.0; layer.layerGreen = 0.0; layer.layerBlue = 0.0;
break;
case 2: // yellow
layer.layerRed = 1.0; layer.layerGreen = 1.0; layer.layerBlue = 0.0;
break;
case 3: // green
layer.layerRed = 0.0; layer.layerGreen = 1.0; layer.layerBlue = 0.0;
break;
case 4: // cyan
layer.layerRed = 0.0; layer.layerGreen = 1.0; layer.layerBlue = 1.0;
break;
case 5: // blue
layer.layerRed = 0.0; layer.layerGreen = 0.0; layer.layerBlue = 1.0;
break;
case 6: // magenta
layer.layerRed = 1.0; layer.layerGreen = 0.0; layer.layerBlue = 1.0;
break;
case 7: // white (well, gray)
layer.layerRed = 0.75; layer.layerGreen = 0.75; layer.layerBlue = 0.75;
break;
default: // unknown layer
layer.layerRed = Math.random();
layer.layerGreen = Math.random();
layer.layerBlue = Math.random();
break;
}
}
}
}
}
return false;
}
private boolean ignoreSection()
throws IOException
{
// just ignore everything until the end-of-section
for(;;)
{
if (getNextPair()) return true;
if (groupID == 0 && text.equals("ENDSEC")) break;
}
return false;
}
/****************************************** READING ENTITIES ******************************************/
private boolean readEntities(Library lib)
throws IOException
{
// read the blocks/entities section
for(;;)
{
if (getNextPair()) return true;
if (groupID != 0)
{
System.out.println("Unknown group code (" + groupID + ") at line " + lineReader.getLineNumber());
return true;
}
if (text.equals("ARC"))
{
if (readArcEntity()) return true;
continue;
}
if (text.equals("ATTDEF"))
{
ignoreEntity();
ignoredAttributeDefs++;
continue;
}
if (text.equals("ATTRIB"))
{
ignoreEntity();
ignoredAttributes++;
continue;
}
if (text.equals("BLOCK"))
{
String msg = readBlock();
if (msg == null) return true;
curCell = Cell.makeInstance(lib, makeBlockName(msg));
if (curCell == null) return true;
continue;
}
if (text.equals("CIRCLE"))
{
if (readCircleEntity()) return true;
continue;
}
if (text.equals("ENDBLK"))
{
ignoreEntity();
curCell = mainCell;
continue;
}
if (text.equals("ENDSEC"))
{
break;
}
if (text.equals("INSERT"))
{
if (readInsertEntity(lib)) return true;
continue;
}
if (text.equals("LINE"))
{
if (readLineEntity()) return true;
continue;
}
if (text.equals("POINT"))
{
ignoreEntity();
ignoredPoints++;
continue;
}
if (text.equals("POLYLINE"))
{
if (readPolyLineEntity()) return true;
continue;
}
if (text.equals("SEQEND"))
{
ignoreEntity();
continue;
}
if (text.equals("SOLID"))
{
if (readSolidEntity()) return true;
continue;
}
if (text.equals("TEXT"))
{
if (readTextEntity()) return true;
continue;
}
if (text.equals("VIEWPORT"))
{
ignoreEntity();
continue;
}
if (text.equals("3DFACE"))
{
if (read3DFaceEntity()) return true;
continue;
}
System.out.println("Unknown entity type (" + text + ") at line " + lineReader.getLineNumber());
return true;
}
return false;
}
private double scaleString(String text)
{
double v = TextUtils.atof(text);
return TextUtils.convertFromDistance(v,Artwork.tech(), dispUnit);
}
private boolean readArcEntity()
throws IOException
{
DXFLayer layer = null;
double x = 0, y = 0;
double rad = 0;
double sAngle = 0, eAngle = 0;
for(;;)
{
if (getNextPair()) return true;
switch (groupID)
{
case 8: layer = getLayer(text); break;
case 10: x = scaleString(text); break;
case 20: y = scaleString(text); break;
case 30: /* ignore Z */ break;
case 40: rad = scaleString(text); break;
case 50: sAngle = TextUtils.atof(text); break;
case 51: eAngle = TextUtils.atof(text); break;
}
if (groupID == 0)
{
pushPair(groupID, text);
break;
}
}
if (!isAcceptableLayer(layer)) return false;
if (sAngle >= 360.0) sAngle -= 360.0;
int iAngle = (int)(sAngle * 10.0);
Orientation orient = Orientation.fromAngle(iAngle);
NodeInst ni = NodeInst.makeInstance(Artwork.tech().circleNode, new Point2D.Double(x, y), rad*2, rad*2, curCell, orient, null);
if (ni == null) return true;
if (sAngle > eAngle) eAngle += 360.0;
double startOffset = sAngle;
startOffset -= iAngle / 10.0;
ni.setArcDegrees(startOffset * Math.PI / 1800.0, (eAngle-sAngle) * Math.PI / 180.0);
ni.newVar(DXF_LAYER_KEY, layer.layerName);
readArcs++;
return false;
}
private String readBlock()
throws IOException
{
String saveMsg = null;
for(;;)
{
if (getNextPair()) return null;
if (groupID == 2) saveMsg = text; else
if (groupID == 0)
{
pushPair(groupID, text);
break;
}
}
return saveMsg;
}
private boolean readCircleEntity()
throws IOException
{
DXFLayer layer = null;
double x = 0, y = 0;
double rad = 0;
for(;;)
{
if (getNextPair()) return true;
switch (groupID)
{
case 8: layer = getLayer(text); break;
case 10: x = scaleString(text); break;
case 20: y = scaleString(text); break;
case 30: /* ignore Z */ break;
case 40: rad = scaleString(text); break;
}
if (groupID == 0)
{
pushPair(groupID, text);
break;
}
}
if (!isAcceptableLayer(layer)) return false;
NodeInst ni = NodeInst.makeInstance(Artwork.tech().circleNode, new Point2D.Double(x, y), rad*2, rad*2, curCell);
if (ni == null) return true;
ni.newVar(DXF_LAYER_KEY, layer.layerName);
readCircles++;
return false;
}
private boolean readInsertEntity(Library lib)
throws IOException
{
int rot = 0;
String name = null;
int xRep = 1, yRep = 1;
double x = 0, y = 0;
double xSca = 1, ySca = 1;
for(;;)
{
if (getNextPair()) return true;
switch (groupID)
{
case 8: /* ignore layer */ break;
case 10: x = scaleString(text); break;
case 20: y = scaleString(text); break;
case 30: /* ignore Z */ break;
case 50: rot = TextUtils.atoi(text); break;
case 41: xSca = TextUtils.atof(text); break;
case 42: ySca = TextUtils.atof(text); break;
case 70: xRep = TextUtils.atoi(text); break;
case 71: yRep = TextUtils.atoi(text); break;
case 44: /* ignore X spacing */ break;
case 45: /* ignore Y spacing */ break;
case 2: name = text; break;
}
if (groupID == 0)
{
pushPair(groupID, text);
break;
}
}
String pt = makeBlockName(name);
if (pt != null)
{
if (xRep != 1 || yRep != 1)
{
System.out.println("Cannot insert block '" + pt + "' repeated " + xRep + "x" + yRep + " times");
return false;
}
// have to search by hand because of weird prototype names
Cell found = null;
for(Iterator<Cell> it = lib.getCells(); it.hasNext(); )
{
Cell np = it.next();
if (np.getName().equals(pt)) { found = np; break; }
}
if (found == null)
{
ForwardRef fr = new ForwardRef();
fr.refName = pt;
fr.parent = curCell;
fr.x = x; fr.y = y;
fr.rot = rot;
fr.xSca = xSca; fr.ySca = ySca;
fr.nextForwardRef = firstForwardRef;
firstForwardRef = fr;
return false;
}
if (localPrefs.flattenHierarchy)
{
if (extractInsert(found, x, y, xSca, ySca, rot, curCell)) return true;
} else
{
if (xSca != 1.0 || ySca != 1.0)
{
found = getScaledCell(found, xSca, ySca);
if (found == null) return true;
}
double sX = found.getDefWidth();
double sY = found.getDefHeight();
Orientation orient = Orientation.fromAngle(rot*10);
NodeInst ni = NodeInst.makeInstance(found, new Point2D.Double(x, y), sX, sY, curCell, orient, null);
if (ni == null) return true;
// ni.setExpanded(true);
}
}
readInserts++;
return false;
}
private boolean readLineEntity()
throws IOException
{
DXFLayer layer = null;
int lineType = 0;
double x1 = 0, y1 = 0;
double x2 = 0, y2 = 0;
for(;;)
{
if (getNextPair()) return true;
switch (groupID)
{
case 8: layer = getLayer(text); break;
case 10: x1 = scaleString(text); break;
case 20: y1 = scaleString(text); break;
case 30: /* ignore Z */ break;
case 11: x2 = scaleString(text); break;
case 21: y2 = scaleString(text); break;
case 31: /* ignore Z */ break;
}
if (groupID == 0)
{
pushPair(groupID, text);
break;
}
}
if (!isAcceptableLayer(layer)) return false;
double cX = (x1 + x2) / 2;
double cY = (y1 + y2) / 2;
double sX = Math.abs(x1 - x2);
double sY = Math.abs(y1 - y2);
NodeProto np = Artwork.tech().openedDashedPolygonNode;
if (lineType == 0) np = Artwork.tech().openedPolygonNode;
NodeInst ni = NodeInst.makeInstance(np, new Point2D.Double(cX, cY), sX, sY, curCell);
if (ni == null) return true;
EPoint [] points = new EPoint[2];
points[0] = new EPoint(x1, y1);
points[1] = new EPoint(x2, y2);
ni.setTrace(points);
ni.newVar(DXF_LAYER_KEY, layer.layerName);
readLines++;
return false;
}
private boolean readPolyLineEntity()
throws IOException
{
boolean closed = false;
DXFLayer layer = null;
int lineType = 0;
boolean inEnd = false;
List<PolyPoint> polyPoints = new ArrayList<PolyPoint>();
PolyPoint curPP = null;
boolean hasBulgeInfo = false;
for(;;)
{
if (getNextPair()) return true;
if (groupID == 8)
{
layer = getLayer(text);
continue;
}
if (groupID == 10)
{
if (curPP != null) curPP.x = scaleString(text);
continue;
}
if (groupID == 20)
{
if (curPP != null) curPP.y = scaleString(text);
continue;
}
if (groupID == 30)
{
if (curPP != null) curPP.z = scaleString(text);
continue;
}
if (groupID == 42)
{
if (curPP != null)
{
curPP.bulge = TextUtils.atof(text);
if (curPP.bulge != 0) hasBulgeInfo = true;
}
continue;
}
if (groupID == 70)
{
int i = TextUtils.atoi(text);
if ((i&1) != 0) closed = true;
continue;
}
if (groupID == 0)
{
if (inEnd)
{
pushPair(groupID, text);
break;
}
if (text.equals("SEQEND"))
{
inEnd = true;
continue;
}
if (text.equals("VERTEX"))
{
curPP = new PolyPoint();
curPP.bulge = 0;
polyPoints.add(curPP);
}
continue;
}
}
int count = polyPoints.size();
if (isAcceptableLayer(layer) && count >= 3)
{
// see if there is bulge information
if (hasBulgeInfo)
{
// handle bulges
int start = 1;
if (closed) start = 0;
for(int i=start; i<count; i++)
{
int last = i - 1;
if (i == 0) last = count-1;
PolyPoint pp = polyPoints.get(i);
PolyPoint lastPp = polyPoints.get(last);
double x1 = lastPp.x; double y1 = lastPp.y;
double x2 = pp.x; double y2 = pp.y;
if (lastPp.bulge != 0.0)
{
// special case the semicircle bulges
if (Math.abs(lastPp.bulge) == 1.0)
{
double cX = (x1 + x2) / 2;
double cY = (y1 + y2) / 2;
if ((y1 == cY && x1 == cX) || (y2 == cY && x2 == cX))
{
System.out.println("Domain error in polyline bulge computation");
continue;
}
double sA = Math.atan2(y1-cY, x1-cX);
double eA = Math.atan2(y2-cY, x2-cX);
if (lastPp.bulge < 0.0)
{
double r2 = sA; sA = eA; eA = r2;
}
if (sA < 0.0) sA += 2.0 * Math.PI;
sA = sA * 1800.0 / Math.PI;
int iAngle = (int)sA;
double rad = new Point2D.Double(cX, cY).distance(new Point2D.Double(x1, y1));
Orientation orient = Orientation.fromAngle(iAngle);
NodeInst ni = NodeInst.makeInstance(Artwork.tech().circleNode, new Point2D.Double(cX, cY), rad*2, rad*2, curCell, orient, null);
if (ni == null) return true;
double startOffset = sA;
startOffset -= iAngle;
ni.setArcDegrees(startOffset * Math.PI / 1800.0, Math.PI);
ni.newVar(DXF_LAYER_KEY, layer.layerName);
continue;
}
// compute half distance between the points
double x01 = x1; double y01 = y1;
double x02 = x2; double y02 = y2;
double dx = x02 - x01; double dy = y02 - y01;
double dist = Math.hypot(dx, dy);
// compute radius of arc (bulge is tangent of 1/4 of included arc angle)
double incAngle = Math.atan(lastPp.bulge) * 4.0;
double arcRad = Math.abs((dist / 2.0) / Math.sin(incAngle / 2.0));
double rad = arcRad;
// prepare to compute the two circle centers
double r2 = arcRad*arcRad;
double delta_1 = -dist / 2.0;
double delta_12 = delta_1 * delta_1;
double delta_2 = Math.sqrt(r2 - delta_12);
// pick one center, according to bulge sign
double bulgeSign = lastPp.bulge;
if (Math.abs(bulgeSign) > 1.0) bulgeSign = -bulgeSign;
double xcf = 0, ycf = 0;
if (bulgeSign > 0.0)
{
xcf = x02 + ((delta_1 * (x02-x01)) + (delta_2 * (y01-y02))) / dist;
ycf = y02 + ((delta_1 * (y02-y01)) + (delta_2 * (x02-x01))) / dist;
} else
{
xcf = x02 + ((delta_1 * (x02-x01)) + (delta_2 * (y02-y01))) / dist;
ycf = y02 + ((delta_1 * (y02-y01)) + (delta_2 * (x01-x02))) / dist;
}
x1 = xcf; y1 = ycf;
// compute angles to the arc endpoints
if ((y01 == ycf && x01 == xcf) || (y02 == ycf && x02 == xcf))
{
System.out.println("Domain error in polyline computation");
continue;
}
double sA = Math.atan2(y01-ycf, x01-xcf);
double eA = Math.atan2(y02-ycf, x02-xcf);
if (lastPp.bulge < 0.0)
{
r2 = sA; sA = eA; eA = r2;
}
if (sA < 0.0) sA += 2.0 * Math.PI;
if (eA < 0.0) eA += 2.0 * Math.PI;
sA = sA * 1800.0 / Math.PI;
eA = eA * 1800.0 / Math.PI;
// create the arc node
int iAngle = (int)sA;
Orientation orient = Orientation.fromAngle(iAngle);
NodeInst ni = NodeInst.makeInstance(Artwork.tech().circleNode, new Point2D.Double(x1, y1), rad*2, rad*2, curCell, orient, null);
if (ni == null) return true;
if (sA > eA) eA += 3600.0;
double startOffset = sA;
startOffset -= iAngle;
ni.setArcDegrees(startOffset * Math.PI / 1800.0, (eA-sA) * Math.PI / 1800.0);
ni.newVar(DXF_LAYER_KEY, layer.layerName);
continue;
}
// this segment has no bulge
double cX = (x1 + x2) / 2;
double cY = (y1 + y2) / 2;
NodeProto np = Artwork.tech().openedDashedPolygonNode;
if (lineType == 0) np = Artwork.tech().openedPolygonNode;
NodeInst ni = NodeInst.makeInstance(np, new Point2D.Double(cX, cY), Math.abs(x1 - x2), Math.abs(y1 - y2), curCell);
if (ni == null) return true;
Point2D [] points = new Point2D[2];
points[0] = new Point2D.Double(x1, y1);
points[1] = new Point2D.Double(x2, y2);
ni.setTrace(points);
ni.newVar(DXF_LAYER_KEY, layer.layerName);
}
} else
{
// no bulges: do simple polygon
double lX = 0, hX = 0;
double lY = 0, hY = 0;
for(int i=0; i<count; i++)
{
PolyPoint pp = polyPoints.get(i);
if (i == 0)
{
lX = hX = pp.x;
lY = hY = pp.y;
} else
{
if (pp.x < lX) lX = pp.x;
if (pp.x > hX) hX = pp.x;
if (pp.y < lY) lY = pp.y;
if (pp.y > hY) hY = pp.y;
}
}
double cX = (lX + hX) / 2;
double cY = (lY + hY) / 2;
NodeProto np = Artwork.tech().closedPolygonNode;
if (!closed)
{
if (lineType == 0) np = Artwork.tech().openedPolygonNode; else
np = Artwork.tech().openedDashedPolygonNode;
}
NodeInst ni = NodeInst.makeInstance(np, new Point2D.Double(cX, cY), hX-lX, hY-lY, curCell);
if (ni == null) return true;
Point2D [] points = new Point2D[count];
for(int i=0; i<count; i++)
{
PolyPoint pp = polyPoints.get(i);
points[i] = new Point2D.Double(pp.x, pp.y);
}
ni.setTrace(points);
ni.newVar(DXF_LAYER_KEY, layer.layerName);
}
}
readPolyLines++;
return false;
}
private boolean readSolidEntity()
throws IOException
{
DXFLayer layer = null;
double factor = 1.0;
double x1 = 0, y1 = 0;
double x2 = 0, y2 = 0;
double x3 = 0, y3 = 0;
double x4 = 0, y4 = 0;
for(;;)
{
if (getNextPair()) return true;
switch (groupID)
{
case 8: layer = getLayer(text); break;
case 10: x1 = scaleString(text); break;
case 20: y1 = scaleString(text); break;
case 30: /* ignore Z */ break;
case 11: x2 = scaleString(text); break;
case 21: y2 = scaleString(text); break;
case 31: /* ignore Z */ break;
case 12: x3 = scaleString(text); break;
case 22: y3 = scaleString(text); break;
case 32: /* ignore Z */ break;
case 13: x4 = scaleString(text); break;
case 23: y4 = scaleString(text); break;
case 33: /* ignore Z */ break;
case 230:
factor = TextUtils.atof(text);
break;
}
if (groupID == 0)
{
pushPair(groupID, text);
break;
}
}
x1 = x1 * factor;
x2 = x2 * factor;
x3 = x3 * factor;
x4 = x4 * factor;
if (!isAcceptableLayer(layer)) return false;
double lX = Math.min(Math.min(x1, x2), Math.min(x3, x4));
double hX = Math.max(Math.max(x1, x2), Math.max(x3, x4));
double lY = Math.min(Math.min(y1, y2), Math.min(y3, y4));
double hY = Math.max(Math.max(y1, y2), Math.max(y3, y4));
double cX = (lX + hX) / 2;
double cY = (lY + hY) / 2;
NodeInst ni = NodeInst.makeInstance(Artwork.tech().filledPolygonNode, new Point2D.Double(cX, cY), hX-lX, hY-lY, curCell);
if (ni == null) return true;
Point2D [] points = new Point2D[4];
points[0] = new Point2D.Double(x1, y1);
points[1] = new Point2D.Double(x2, y2);
points[2] = new Point2D.Double(x3, y3);
points[3] = new Point2D.Double(x4, y4);
ni.setTrace(points);
ni.newVar(DXF_LAYER_KEY, layer.layerName);
readSolids++;
return false;
}
private boolean readTextEntity()
throws IOException
{
DXFLayer layer = null;
String msg = null;
double x = 0, y = 0;
double height = 0, xAlign = 0;
boolean gotXA = false;
for(;;)
{
if (getNextPair()) return true;
switch (groupID)
{
case 8: layer = getLayer(text); break;
case 10: x = scaleString(text); break;
case 20: y = scaleString(text); break;
case 40: height = scaleString(text); break;
case 11: xAlign = scaleString(text); gotXA = true; break;
case 1: msg = text; break;
}
if (groupID == 0)
{
pushPair(groupID, text);
break;
}
}
double lX = x, hX = x;
double lY = y, hY = y;
if (gotXA)
{
lX = Math.min(x, xAlign);
hX = lX + Math.abs(xAlign-x) * 2;
lY = y;
hY = y + height;
} else
{
if (msg != null)
{
double h = msg.length();
lX = x; hX = x + height * h;
lY = y; hY = y + height;
}
}
if (!isAcceptableLayer(layer)) return false;
if (msg != null)
{
NodeInst ni = NodeInst.makeInstance(Generic.tech().invisiblePinNode, new Point2D.Double((lX+hX)/2, (lY+hY)/2), hX-lX, hY-lY, curCell);
if (ni == null) return true;
TextDescriptor td = TextDescriptor.getNodeTextDescriptor().withPos(TextDescriptor.Position.BOXED).withAbsSize(TextDescriptor.Size.TXTMAXPOINTS);
ni.newVar(Artwork.ART_MESSAGE, msg, td);
ni.newVar(DXF_LAYER_KEY, layer.layerName);
readTexts++;
}
return false;
}
private boolean read3DFaceEntity()
throws IOException
{
DXFLayer layer = null;
double x1 = 0, y1 = 0;
double x2 = 0, y2 = 0;
double x3 = 0, y3 = 0;
double x4 = 0, y4 = 0;
for(;;)
{
if (getNextPair()) return true;
switch (groupID)
{
case 8: layer = getLayer(text); break;
case 10: x1 = scaleString(text); break;
case 20: y1 = scaleString(text); break;
case 30: /* ignore Z */ break;
case 11: x2 = scaleString(text); break;
case 21: y2 = scaleString(text); break;
case 31: /* ignore Z */ break;
case 12: x3 = scaleString(text); break;
case 22: y3 = scaleString(text); break;
case 32: /* ignore Z */ break;
case 13: x4 = scaleString(text); break;
case 23: y4 = scaleString(text); break;
case 33: /* ignore Z */ break;
}
if (groupID == 0)
{
pushPair(groupID, text);
break;
}
}
if (!isAcceptableLayer(layer)) return false;
double lX = Math.min(Math.min(x1, x2), Math.min(x3, x4));
double hX = Math.max(Math.max(x1, x2), Math.max(x3, x4));
double lY = Math.min(Math.min(y1, y2), Math.min(y3, y4));
double hY = Math.max(Math.max(y1, y2), Math.max(y3, y4));
double cX = (lX + hX) / 2;
double cY = (lY + hY) / 2;
NodeInst ni = NodeInst.makeInstance(Artwork.tech().closedPolygonNode, new Point2D.Double(cX, cY), hX-lX, hY-lY, curCell);
if (ni == null) return true;
Point2D [] points = new Point2D[4];
points[0] = new Point2D.Double(x1, y1);
points[1] = new Point2D.Double(x2, y2);
points[2] = new Point2D.Double(x3, y3);
points[3] = new Point2D.Double(x4, y4);
ni.setTrace(points);
ni.newVar(DXF_LAYER_KEY, layer.layerName);
read3DFaces++;
return false;
}
private void ignoreEntity()
throws IOException
{
for(;;)
{
if (getNextPair()) break;
if (groupID == 0) break;
}
pushPair(groupID, text);
}
/****************************************** READING SUPPORT ******************************************/
private boolean isAcceptableLayer(DXFLayer layer)
{
if (layer == null) return false;
if (localPrefs.readAllLayers) return true;
if (validLayerNames.contains(layer.layerName)) return true;
// add this to the list of layer names that were ignored
ignoredLayerNames.add(layer.layerName);
return false;
}
private boolean extractInsert(Cell onp, double x, double y, double xSca, double ySca, int rot, Cell np)
{
// rotate "rot*10" about point [(onp->lowx+onp->highx)/2+x, (onp->lowy+onp->highy)/2+y]
Orientation orient = Orientation.fromAngle(rot*10);
AffineTransform trans = orient.pureRotate();
double m00 = trans.getScaleX();
double m01 = trans.getShearX();
double m11 = trans.getScaleY();
double m10 = trans.getShearY();
Rectangle2D bounds = onp.getBounds();
double m02 = bounds.getCenterX() + x;
double m12 = bounds.getCenterY() + y;
trans.setTransform(m00, m10, m01, m11, m02, m12);
Point2D pt = new Point2D.Double(-m02, -m12);
trans.transform(pt, pt);
trans.setTransform(m00, m10, m01, m11, pt.getX(), pt.getY());
for(Iterator<NodeInst> it = onp.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
if (ni.isCellInstance())
{
System.out.println("Cannot insert block '" + onp + "'...it has inserts in it");
return true;
}
if (ni.getProto() == Generic.tech().cellCenterNode) continue;
double sX = ni.getXSize() * xSca;
double sY = ni.getYSize() * ySca;
double cX = x + ni.getAnchorCenterX() * xSca;
double cY = y + ni.getAnchorCenterY() * ySca;
Point2D tPt = new Point2D.Double(cX, cY);
trans.transform(tPt, tPt);
NodeInst nNi = NodeInst.makeInstance(ni.getProto(), tPt, sX, sY, np, orient.concatenate(ni.getOrient()), null);
if (nNi == null) return true;
if (ni.getProto() == Artwork.tech().closedPolygonNode || ni.getProto() == Artwork.tech().filledPolygonNode ||
ni.getProto() == Artwork.tech().openedPolygonNode || ni.getProto() == Artwork.tech().openedDashedPolygonNode)
{
// copy trace information
Point2D [] oldTrace = ni.getTrace();
if (oldTrace != null)
{
int len = oldTrace.length;
Point2D [] newTrace = new Point2D[len];
for(int i=0; i<len; i++)
{
if (oldTrace[i] != null)
newTrace[i] = new Point2D.Double(cX + oldTrace[i].getX() * xSca, cY + oldTrace[i].getY() * ySca);
}
nNi.setTrace(newTrace);
}
} else if (ni.getProto() == Generic.tech().invisiblePinNode)
{
// copy text information
Variable var = ni.getVar(Artwork.ART_MESSAGE);
if (var != null)
{
nNi.newVar(Artwork.ART_MESSAGE, var.getObject(), var.getTextDescriptor());
}
} else if (ni.getProto() == Artwork.tech().circleNode || ni.getProto() == Artwork.tech().thickCircleNode)
{
// copy arc information
double [] curvature = ni.getArcDegrees();
nNi.setArcDegrees(curvature[0], curvature[1]);
}
// copy other information
Variable var = ni.getVar(DXF_LAYER_KEY);
if (var != null) nNi.newVar(DXF_LAYER_KEY, var.getObject());
var = ni.getVar(Artwork.ART_COLOR);
if (var != null) nNi.newVar(Artwork.ART_COLOR, var.getObject());
}
return false;
}
private Cell getScaledCell(Cell onp, double xSca, double ySca)
{
String fViewName = "scaled" + xSca + "x" + ySca;
String sViewName = "s" + xSca + "x" + ySca;
View view = View.findView(fViewName);
if (view == null)
{
view = View.newInstance(fViewName, sViewName);
if (view == null) return null;
}
// find the view of this cell
Cell rightView = onp.otherView(view);
if (rightView != null) return rightView;
// not found: create it
String cellName = onp.getName() + "{" + sViewName + "}";
Cell np = Cell.makeInstance(onp.getLibrary(), cellName);
if (np == null) return null;
for(Iterator<NodeInst> it = onp.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
if (ni.isCellInstance())
{
System.out.println("Cannot insert block '" + onp + "'...it has inserts in it");
return null;
}
NodeInst nNi = NodeInst.makeInstance(ni.getProto(), ni.getAnchorCenter(),
ni.getXSize()*xSca, ni.getYSize()*ySca, np, ni.getOrient(), null);
if (nNi == null) return null;
if (ni.getProto() == Artwork.tech().closedPolygonNode || ni.getProto() == Artwork.tech().filledPolygonNode ||
ni.getProto() == Artwork.tech().openedPolygonNode || ni.getProto() == Artwork.tech().openedDashedPolygonNode)
{
// copy trace information
Point2D [] oldTrace = ni.getTrace();
if (oldTrace != null)
{
int len = oldTrace.length;
Point2D [] newTrace = new Point2D[len];
for(int i=0; i<len; i++)
{
if (oldTrace[i] != null)
newTrace[i] = new Point2D.Double(oldTrace[i].getX() * xSca, oldTrace[i].getY() * ySca);
}
nNi.setTrace(newTrace);
}
} else if (ni.getProto() == Generic.tech().invisiblePinNode)
{
// copy text information
Variable var = ni.getVar(Artwork.ART_MESSAGE);
if (var != null)
{
nNi.newVar(Artwork.ART_MESSAGE, var.getObject(), var.getTextDescriptor());
}
} else if (ni.getProto() == Artwork.tech().circleNode || ni.getProto() == Artwork.tech().thickCircleNode)
{
// copy arc information
double [] curvature = ni.getArcDegrees();
nNi.setArcDegrees(curvature[0], curvature[1]);
}
// copy layer information
Variable var = ni.getVar(DXF_LAYER_KEY);
if (var != null) nNi.newVar(DXF_LAYER_KEY, var.getObject());
}
return np;
}
private DXFLayer getLayer(String name)
{
for(DXFLayer layer = firstLayer; layer != null; layer = layer.next)
if (name.equals(layer.layerName)) return layer;
// create a new one
DXFLayer layer = new DXFLayer();
layer.layerName = name;
layer.layerColor = -1;
layer.layerRed = 1.0;
layer.layerGreen = 1.0;
layer.layerBlue = 1.0;
layer.next = firstLayer;
firstLayer = layer;
return layer;
}
private void pushPair(int groupID, String text)
{
lastGroupID = groupID;
lastText = text;
lastPairValid = true;
}
/**
* Method to examine the layer names on the artwork technology and obtain
* a list of acceptable layer names and numbers.
*/
private void getAcceptableLayers()
{
validLayerNames = new HashSet<String>();
for(Iterator<Layer> it = Artwork.tech().getLayers(); it.hasNext(); )
{
Layer lay = it.next();
String layNames = lay.getDXFLayer();
if (layNames == null) continue;
while (layNames.length() > 0)
{
int commaPos = layNames.indexOf(',');
if (commaPos < 0) commaPos = layNames.length();
String oneName = layNames.substring(0, commaPos);
validLayerNames.add(oneName);
layNames = layNames.substring(oneName.length());
if (layNames.startsWith(",")) layNames = layNames.substring(1);
}
}
}
/**
* Method to convert a block name "name" into a valid Electric cell name (converts
* bad characters).
*/
private String makeBlockName(String name)
{
StringBuffer infstr = new StringBuffer();
for(int i=0; i<name.length(); i++)
{
char chr = name.charAt(i);
if (chr == '$' || chr == '{' || chr == '}' || chr == ':') chr = '_';
infstr.append(chr);
}
return infstr.toString();
}
/**
* Method to set the conversion units between DXF files and real distance.
* The value is stored in the global "dispUnit".
*/
private void setCurUnits()
{
switch (localPrefs.scale)
{
case -3: dispUnit = TextUtils.UnitScale.GIGA; break;
case -2: dispUnit = TextUtils.UnitScale.MEGA; break;
case -1: dispUnit = TextUtils.UnitScale.KILO; break;
case 0: dispUnit = TextUtils.UnitScale.NONE; break;
case 1: dispUnit = TextUtils.UnitScale.MILLI; break;
case 2: dispUnit = TextUtils.UnitScale.MICRO; break;
case 3: dispUnit = TextUtils.UnitScale.NANO; break;
case 4: dispUnit = TextUtils.UnitScale.PICO; break;
case 5: dispUnit = TextUtils.UnitScale.FEMTO; break;
}
}
}