freerouting/src/main/java/eu/mihosoft/freerouting/designformats/specctra/Shape.java

832 lines
29 KiB
Java

/*
* Copyright (C) 2014 Alfons Wirtz
* website www.freerouting.net
*
* This program 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.
*
* This program 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 at <http://www.gnu.org/licenses/>
* for more details.
*
* Shape.java
*
* Created on 16. Mai 2004, 11:09
*/
package designformats.specctra;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import geometry.planar.PolylineShape;
import datastructures.IndentFileWriter;
import datastructures.IdentifierType;
/**
* Describes a shape in a Specctra dsn file.
*
* @author alfons
*/
public abstract class Shape
{
/**
* Writes a shape scope to a Specctra dsn file.
*/
public abstract void write_scope(IndentFileWriter p_file, IdentifierType p_identifier) throws java.io.IOException;
/**
* Writes a shape scope to a Specctra session file.
* In a session file all coordinates must be integer.
*/
public abstract void write_scope_int(IndentFileWriter p_file, IdentifierType p_identifier) throws java.io.IOException;
/**
* Reads shape scope from a Specctra dsn file.
* If p_layer_structure == null, only Layer.PCB and Layer.Signal are expected, no induvidual layers.
*/
public static final Shape read_scope(Scanner p_scanner, LayerStructure p_layer_structure)
{
Shape result = null;
try
{
Object next_token = p_scanner.next_token();
if (next_token == Keyword.OPEN_BRACKET)
{
// overread the open bracket
next_token = p_scanner.next_token();
}
if (next_token == Keyword.RECTANGLE)
{
result = Shape.read_rectangle_scope(p_scanner, p_layer_structure);
}
else if (next_token == Keyword.POLYGON)
{
result = Shape.read_polygon_scope(p_scanner, p_layer_structure);
}
else if (next_token == Keyword.CIRCLE)
{
result = Shape.read_circle_scope(p_scanner, p_layer_structure);
}
else if (next_token == Keyword.POLYGON_PATH)
{
result = Shape.read_polygon_path_scope(p_scanner, p_layer_structure);
}
else
{
// not a shape scope, skip it.
ScopeKeyword.skip_scope(p_scanner);
}
}
catch (java.io.IOException e)
{
System.out.println("Shape.read_scope: IO error scanning file");
System.out.println(e);
return result;
}
return result;
}
/**
* Reads an object of type PolylinePath from the dsn-file.
*/
public static PolylinePath read_polyline_path_scope(Scanner p_scanner, LayerStructure p_layer_structure)
{
try
{
Layer layer = null;
Object next_token = p_scanner.next_token();
if (next_token == Keyword.PCB_SCOPE)
{
layer = Layer.PCB;
}
else if (next_token == Keyword.SIGNAL)
{
layer = Layer.SIGNAL;
}
else
{
if (p_layer_structure == null)
{
System.out.println("PolylinePath.read_scope: only layer types pcb or signal expected");
return null;
}
if (!(next_token instanceof String))
{
System.out.println("PolylinePath.read_scope: layer name string expected");
return null;
}
int layer_no = p_layer_structure.get_no((String) next_token);
if (layer_no < 0 || layer_no >= p_layer_structure.arr.length)
{
System.out.print("Shape.read_polyline_path_scope: layer name ");
System.out.print((String) next_token);
System.out.println(" not found in layer structure ");
return null;
}
layer = p_layer_structure.arr[layer_no];
}
Collection<Object> corner_list = new LinkedList<Object>();
// read the width and the corners of the path
for (;;)
{
next_token = p_scanner.next_token();
if (next_token == Keyword.CLOSED_BRACKET)
{
break;
}
corner_list.add(next_token);
}
if (corner_list.size() < 5)
{
System.out.println("PolylinePath.read_scope: to few numbers in scope");
return null;
}
Iterator<Object> it = corner_list.iterator();
double width = 0;
Object next_object = it.next();
if (next_object instanceof Double)
{
width = ((Double) next_object).doubleValue();
}
else if (next_object instanceof Integer)
{
width = ((Integer) next_object).intValue();
}
else
{
System.out.println("PolylinePath.read_scope: number expected");
return null;
}
double[] corner_arr = new double[corner_list.size() - 1];
for (int i = 0; i < corner_arr.length; ++i)
{
next_object = it.next();
if (next_object instanceof Double)
{
corner_arr[i] = ((Double) next_object).doubleValue();
}
else if (next_object instanceof Integer)
{
corner_arr[i] = ((Integer) next_object).intValue();
}
else
{
System.out.println("Shape.read_polygon_path_scope: number expected");
return null;
}
}
return new PolylinePath(layer, width, corner_arr);
}
catch (java.io.IOException e)
{
System.out.println("PolylinePath.read_scope: IO error scanning file");
System.out.println(e);
return null;
}
}
/**
* Reads a shape , which may contain holes from a specctra dsn-file.
* The first shape in the shape_list of the result is the border of the area.
* The other shapes in the shape_list are holes (windows).
*/
public static final ReadAreaScopeResult read_area_scope(Scanner p_scanner,
LayerStructure p_layer_structure, boolean p_skip_window_scopes)
{
Collection<Shape> shape_list = new LinkedList<Shape>();
String clearance_class_name = null;
String area_name = null;
boolean result_ok = true;
Object next_token = null;
try
{
next_token = p_scanner.next_token();
}
catch (java.io.IOException e)
{
System.out.println("Shape.read_area_scope: IO error scanning file");
return null;
}
if (next_token instanceof String)
{
String curr_name = (String) next_token;
if (!curr_name.isEmpty())
{
area_name = curr_name;
}
}
Shape curr_shape = Shape.read_scope(p_scanner, p_layer_structure);
if (curr_shape == null)
{
result_ok = false;
}
shape_list.add(curr_shape);
next_token = null;
for (;;)
{
Object prev_token = next_token;
try
{
next_token = p_scanner.next_token();
}
catch (java.io.IOException e)
{
System.out.println("Shape.read_area_scope: IO error scanning file");
return null;
}
if (next_token == null)
{
System.out.println("Shape.read_area_scope: unexpected end of file");
return null;
}
if (next_token == Keyword.CLOSED_BRACKET)
{
// end of scope
break;
}
if (prev_token == Keyword.OPEN_BRACKET)
{
// a new scope is expected
if (next_token == Keyword.WINDOW && !p_skip_window_scopes)
{
Shape hole_shape = Shape.read_scope(p_scanner, p_layer_structure);
shape_list.add(hole_shape);
// overread closing bracket
try
{
next_token = p_scanner.next_token();
}
catch (java.io.IOException e)
{
System.out.println("Shape.read_area_scope: IO error scanning file");
return null;
}
if (next_token != Keyword.CLOSED_BRACKET)
{
System.out.println("Shape.read_area_scope: closed bracket expected");
return null;
}
}
else if (next_token == Keyword.CLEARANCE_CLASS)
{
clearance_class_name = DsnFile.read_string_scope(p_scanner);
}
else
{
// skip unknown scope
ScopeKeyword.skip_scope(p_scanner);
}
}
}
if (!result_ok)
{
return null;
}
return new ReadAreaScopeResult(area_name, shape_list, clearance_class_name);
}
/**
* Reads a rectangle scope from a Specctra dsn file.
* If p_layer_structure == null, only Layer.PCB and Layer.Signal are expected, no induvidual layers.
*/
public static Rectangle read_rectangle_scope(Scanner p_scanner, LayerStructure p_layer_structure)
{
try
{
Layer rect_layer = null;
double rect_coor[] = new double[4];
Object next_token = p_scanner.next_token();
if (next_token == Keyword.PCB_SCOPE)
{
rect_layer = Layer.PCB;
}
else if (next_token == Keyword.SIGNAL)
{
rect_layer = Layer.SIGNAL;
}
else if (p_layer_structure != null)
{
if (!(next_token instanceof String))
{
System.out.println("Shape.read_rectangle_scope: layer name string expected");
return null;
}
String layer_name = (String) next_token;
int layer_no = p_layer_structure.get_no(layer_name);
if (layer_no < 0 || layer_no >= p_layer_structure.arr.length)
{
System.out.println("Shape.read_rectangle_scope: layer name " + layer_name +
" not found in layer structure ");
}
else
{
rect_layer = p_layer_structure.arr[layer_no];
}
}
else
{
rect_layer = Layer.SIGNAL;
}
// fill the the rectangle
for (int i = 0; i < 4; ++i)
{
next_token = p_scanner.next_token();
if (next_token instanceof Double)
{
rect_coor[i] = ((Double) next_token).doubleValue();
}
else if (next_token instanceof Integer)
{
rect_coor[i] = ((Integer) next_token).intValue();
}
else
{
System.out.println("Shape.read_rectangle_scope: number expected");
return null;
}
}
// overread the closing bracket
next_token = p_scanner.next_token();
if (next_token != Keyword.CLOSED_BRACKET)
{
System.out.println("Shape.read_rectangle_scope ) expected");
return null;
}
if (rect_layer == null)
{
return null;
}
return new Rectangle(rect_layer, rect_coor);
}
catch (java.io.IOException e)
{
System.out.println("Shape.read_rectangle_scope: IO error scanning file");
System.out.println(e);
return null;
}
}
/**
* Reads a closed polygon scope from a Specctra dsn file.
* If p_layer_structure == null, only Layer.PCB and Layer.Signal are expected, no induvidual layers.
*/
public static Polygon read_polygon_scope(Scanner p_scanner, LayerStructure p_layer_structure)
{
try
{
Layer polygon_layer = null;
boolean layer_ok = true;
Object next_token = p_scanner.next_token();
if (next_token == Keyword.PCB_SCOPE)
{
polygon_layer = Layer.PCB;
}
else if (next_token == Keyword.SIGNAL)
{
polygon_layer = Layer.SIGNAL;
}
else
{
if (p_layer_structure == null)
{
System.out.println("Shape.read_polygon_scope: only layer types pcb or signal expected");
return null;
}
if (!(next_token instanceof String))
{
System.out.println("Shape.read_polygon_scope: layer name string expected");
return null;
}
int layer_no = p_layer_structure.get_no((String) next_token);
if (layer_no < 0 || layer_no >= p_layer_structure.arr.length)
{
System.out.print("Shape.read_polygon_scope: layer name ");
System.out.print((String) next_token);
System.out.println(" not found in layer structure ");
layer_ok = false;
}
else
{
polygon_layer = p_layer_structure.arr[layer_no];
}
}
// overread the aperture width
next_token = p_scanner.next_token();
Collection<Object> coor_list = new LinkedList<Object>();
// read the coordinates of the polygon
for (;;)
{
next_token = p_scanner.next_token();
if (next_token == null)
{
System.out.println("Shape.read_polygon_scope: unexpected end of file");
return null;
}
if (next_token == Keyword.OPEN_BRACKET)
{
// unknown scope
ScopeKeyword.skip_scope(p_scanner);
next_token = p_scanner.next_token();
}
if (next_token == Keyword.CLOSED_BRACKET)
{
break;
}
coor_list.add(next_token);
}
if (!layer_ok)
{
return null;
}
double[] coor_arr = new double[coor_list.size()];
Iterator<Object> it = coor_list.iterator();
for (int i = 0; i < coor_arr.length; ++i)
{
Object next_object = it.next();
if (next_object instanceof Double)
{
coor_arr[i] = ((Double) next_object).doubleValue();
}
else if (next_object instanceof Integer)
{
coor_arr[i] = ((Integer) next_object).intValue();
}
else
{
System.out.println("Shape.read_polygon_scope: number expected");
return null;
}
}
return new Polygon(polygon_layer, coor_arr);
}
catch (java.io.IOException e)
{
System.out.println("Rectangle.read_scope: IO error scanning file");
System.out.println(e);
return null;
}
}
/**
* Reads a circle scope from a Specctra dsn file.
*/
public static Circle read_circle_scope(Scanner p_scanner, LayerStructure p_layer_structure)
{
try
{
Layer circle_layer = null;
boolean layer_ok = true;
double circle_coor[] = new double[3];
Object next_token = p_scanner.next_token();
if (next_token == Keyword.PCB_SCOPE)
{
circle_layer = Layer.PCB;
}
else if (next_token == Keyword.SIGNAL)
{
circle_layer = Layer.SIGNAL;
}
else
{
if (p_layer_structure == null)
{
System.out.println("Shape.read_circle_scope: p_layer_structure != null expected");
return null;
}
if (!(next_token instanceof String))
{
System.out.println("Shape.read_circle_scope: string for layer_name expected");
return null;
}
int layer_no = p_layer_structure.get_no((String) next_token);
if (layer_no < 0 || layer_no >= p_layer_structure.arr.length)
{
System.out.print("Shape.read_circle_scope: layer with name ");
System.out.print((String) next_token);
System.out.println(" not found in layer stracture ");
layer_ok = false;
}
else
{
circle_layer = p_layer_structure.arr[layer_no];
}
}
// fill the the the coordinates
int curr_index = 0;
for (;;)
{
next_token = p_scanner.next_token();
if (next_token == Keyword.CLOSED_BRACKET)
{
break;
}
if (curr_index > 2)
{
System.out.println("Shape.read_circle_scope: closed bracket expected");
return null;
}
if (next_token instanceof Double)
{
circle_coor[curr_index] = ((Double) next_token).doubleValue();
}
else if (next_token instanceof Integer)
{
circle_coor[curr_index] = ((Integer) next_token).intValue();
}
else
{
System.out.println("Shape.read_circle_scope: number expected");
return null;
}
++curr_index;
}
if (!layer_ok)
{
return null;
}
return new Circle(circle_layer, circle_coor);
}
catch (java.io.IOException e)
{
System.out.println("Shape.read_rectangle_scope: IO error scanning file");
System.out.println(e);
return null;
}
}
/**
* Reads an object of type Path from the dsn-file.
*/
public static PolygonPath read_polygon_path_scope(Scanner p_scanner, LayerStructure p_layer_structure)
{
try
{
Layer layer = null;
boolean layer_ok = true;
Object next_token = p_scanner.next_token();
if (next_token == Keyword.PCB_SCOPE)
{
layer = Layer.PCB;
}
else if (next_token == Keyword.SIGNAL)
{
layer = Layer.SIGNAL;
}
else
{
if (p_layer_structure == null)
{
System.out.println("Shape.read_polygon_path_scope: only layer types pcb or signal expected");
return null;
}
if (!(next_token instanceof String))
{
System.out.println("Path.read_scope: layer name string expected");
return null;
}
int layer_no = p_layer_structure.get_no((String) next_token);
if (layer_no < 0 || layer_no >= p_layer_structure.arr.length)
{
System.out.print("Shape.read_polygon_path_scope: layer with name ");
System.out.print((String) next_token);
System.out.println(" not found in layer structure ");
layer_ok = false;
}
else
{
layer = p_layer_structure.arr[layer_no];
}
}
Collection<Object> corner_list = new LinkedList<Object>();
// read the width and the corners of the path
for (;;)
{
next_token = p_scanner.next_token();
if (next_token == Keyword.OPEN_BRACKET)
{
// unknown scope
ScopeKeyword.skip_scope(p_scanner);
next_token = p_scanner.next_token();
}
if (next_token == Keyword.CLOSED_BRACKET)
{
break;
}
corner_list.add(next_token);
}
if (corner_list.size() < 5)
{
System.out.println("Shape.read_polygon_path_scope: to few numbers in scope");
return null;
}
if (!layer_ok)
{
return null;
}
Iterator<Object> it = corner_list.iterator();
double width = 0;
Object next_object = it.next();
if (next_object instanceof Double)
{
width = ((Double) next_object).doubleValue();
}
else if (next_object instanceof Integer)
{
width = ((Integer) next_object).intValue();
}
else
{
System.out.println("Shape.read_polygon_path_scope: number expected");
return null;
}
double[] coordinate_arr = new double[corner_list.size() - 1];
for (int i = 0; i < coordinate_arr.length; ++i)
{
next_object = it.next();
if (next_object instanceof Double)
{
coordinate_arr[i] = ((Double) next_object).doubleValue();
}
else if (next_object instanceof Integer)
{
coordinate_arr[i] = ((Integer) next_object).intValue();
}
else
{
System.out.println("Shape.read_polygon_path_scope: number expected");
return null;
}
}
return new PolygonPath(layer, width, coordinate_arr);
}
catch (java.io.IOException e)
{
System.out.println("Shape.read_polygon_path_scope: IO error scanning file");
System.out.println(e);
return null;
}
}
/**
* Transforms a shape with holes to the board coordinate system.
* The first shape in the Collection p_area is the border,
* the other shapes are holes of the area.
*/
public static geometry.planar.Area transform_area_to_board(Collection<Shape> p_area, CoordinateTransform p_coordinate_transform)
{
int hole_count = p_area.size() - 1;
if (hole_count <= -1)
{
System.out.println("Shape.transform_area_to_board: p_area.size() > 0 expected");
return null;
}
Iterator<Shape> it = p_area.iterator();
Shape boundary = it.next();
geometry.planar.Shape boundary_shape = boundary.transform_to_board(p_coordinate_transform);
geometry.planar.Area result;
if (hole_count == 0)
{
result = boundary_shape;
}
else
{
// Area with holes
if (!(boundary_shape instanceof geometry.planar.PolylineShape))
{
System.out.println("Shape.transform_area_to_board: PolylineShape expected");
return null;
}
PolylineShape border = (PolylineShape) boundary_shape;
PolylineShape[] holes = new PolylineShape[hole_count];
for (int i = 0; i < holes.length; ++i)
{
geometry.planar.Shape hole_shape = it.next().transform_to_board(p_coordinate_transform);
if (!(hole_shape instanceof PolylineShape))
{
System.out.println("Shape.transform_area_to_board: PolylineShape expected");
return null;
}
holes[i] = (PolylineShape) hole_shape;
}
result = new geometry.planar.PolylineArea(border, holes);
}
return result;
}
/**
* Transforms the relative coordinates of a shape with holes to the board coordinate system.
* The first shape in the Collection p_area is the border,
* the other shapes are holes of the area.
*/
public static geometry.planar.Area transform_area_to_board_rel(Collection<Shape> p_area, CoordinateTransform p_coordinate_transform)
{
int hole_count = p_area.size() - 1;
if (hole_count <= -1)
{
System.out.println("Shape.transform_area_to_board_rel: p_area.size() > 0 expected");
return null;
}
Iterator<Shape> it = p_area.iterator();
Shape boundary = it.next();
geometry.planar.Shape boundary_shape = boundary.transform_to_board_rel(p_coordinate_transform);
geometry.planar.Area result;
if (hole_count == 0)
{
result = boundary_shape;
}
else
{
// Area with holes
if (!(boundary_shape instanceof geometry.planar.PolylineShape))
{
System.out.println("Shape.transform_area_to_board_rel: PolylineShape expected");
return null;
}
PolylineShape border = (PolylineShape) boundary_shape;
PolylineShape[] holes = new PolylineShape[hole_count];
for (int i = 0; i < holes.length; ++i)
{
geometry.planar.Shape hole_shape = it.next().transform_to_board_rel(p_coordinate_transform);
if (!(hole_shape instanceof PolylineShape))
{
System.out.println("Shape.transform_area_to_board: PolylineShape expected");
return null;
}
holes[i] = (PolylineShape) hole_shape;
}
result = new geometry.planar.PolylineArea(border, holes);
}
return result;
}
public void write_hole_scope(IndentFileWriter p_file, IdentifierType p_identifier_type) throws java.io.IOException
{
p_file.start_scope();
p_file.write("window");
this.write_scope(p_file, p_identifier_type);
p_file.end_scope();
}
/**
* Transforms a specctra dsn shape to a geometry.planar.Shape.
*/
public abstract geometry.planar.Shape transform_to_board(CoordinateTransform p_coordinate_transform);
/**
* Returns the smallest axis parallel rectangle containing this shape.
*/
public abstract Rectangle bounding_box();
/**
* Transforms the relative (vector) coordinates of a specctra dsn shape to a geometry.planar.Shape.
*/
public abstract geometry.planar.Shape transform_to_board_rel(CoordinateTransform p_coordinate_transform);
protected Shape(Layer p_layer)
{
layer = p_layer;
}
public final Layer layer;
/**
* Contains the result of the function read_area_scope.
* area_name or clearance_class_name may be null, which means they are not provided.
*/
static class ReadAreaScopeResult
{
private ReadAreaScopeResult(String p_area_name, Collection<Shape> p_shape_list, String p_clearance_class_name)
{
area_name = p_area_name;
shape_list = p_shape_list;
clearance_class_name = p_clearance_class_name;
}
String area_name; // may be generated later on, if area_name is null.
final Collection<Shape> shape_list;
final String clearance_class_name;
}
}