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

1420 lines
52 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.
*
* Structure.java
*
* Created on 13. Mai 2004, 09:57
*/
package designformats.specctra;
import geometry.planar.IntBox;
import geometry.planar.PolylineShape;
import geometry.planar.TileShape;
import geometry.planar.Point;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import datastructures.UndoableObjects;
import rules.BoardRules;
import rules.DefaultItemClearanceClasses.ItemClass;
import datastructures.IndentFileWriter;
import datastructures.IdentifierType;
import datastructures.UndoableObjects.Storable;
import board.FixedState;
import board.TestLevel;
/**
* Class for reading and writing structure scopes from dsn-files.
*
* @author Alfons Wirtz
*/
class Structure extends ScopeKeyword
{
/** Creates a new instance of Structure */
public Structure()
{
super("structure");
}
public boolean read_scope(ReadScopeParameter p_par)
{
BoardConstructionInfo board_construction_info = new BoardConstructionInfo();
// If true, components on the back side are rotated before mirroring
// The correct location is the scope PlaceControl, but Electra writes it here.
boolean flip_style_rotate_first = false;
Collection<Shape.ReadAreaScopeResult> keepout_list = new LinkedList<Shape.ReadAreaScopeResult>();
Collection<Shape.ReadAreaScopeResult> via_keepout_list = new LinkedList<Shape.ReadAreaScopeResult>();
Collection<Shape.ReadAreaScopeResult> place_keepout_list = new LinkedList<Shape.ReadAreaScopeResult>();
Object next_token = null;
for (;;)
{
Object prev_token = next_token;
try
{
next_token = p_par.scanner.next_token();
}
catch (java.io.IOException e)
{
System.out.println("Structure.read_scope: IO error scanning file");
System.out.println(e);
return false;
}
if (next_token == null)
{
System.out.println("Structure.read_scope: unexpected end of file");
return false;
}
if (next_token == CLOSED_BRACKET)
{
// end of scope
break;
}
boolean read_ok = true;
if (prev_token == OPEN_BRACKET)
{
if (next_token == Keyword.BOUNDARY)
{
read_boundary_scope(p_par.scanner, board_construction_info);
}
else if (next_token == Keyword.LAYER)
{
read_ok = read_layer_scope(p_par.scanner, board_construction_info, p_par.string_quote);
if (p_par.layer_structure != null)
{
// correct the layer_structure because another layer isr read
p_par.layer_structure = new LayerStructure(board_construction_info.layer_info);
}
}
else if (next_token == Keyword.VIA)
{
p_par.via_padstack_names = read_via_padstacks(p_par.scanner);
}
else if (next_token == Keyword.RULE)
{
board_construction_info.default_rules.addAll(Rule.read_scope(p_par.scanner));
}
else if (next_token == Keyword.KEEPOUT)
{
if (p_par.layer_structure == null)
{
p_par.layer_structure = new LayerStructure(board_construction_info.layer_info);
}
keepout_list.add(Shape.read_area_scope(p_par.scanner, p_par.layer_structure, false));
}
else if (next_token == Keyword.VIA_KEEPOUT)
{
if (p_par.layer_structure == null)
{
p_par.layer_structure = new LayerStructure(board_construction_info.layer_info);
}
via_keepout_list.add(Shape.read_area_scope(p_par.scanner, p_par.layer_structure, false));
}
else if (next_token == Keyword.PLACE_KEEPOUT)
{
if (p_par.layer_structure == null)
{
p_par.layer_structure = new LayerStructure(board_construction_info.layer_info);
}
place_keepout_list.add(Shape.read_area_scope(p_par.scanner, p_par.layer_structure, false));
}
else if (next_token == Keyword.PLANE_SCOPE)
{
if (p_par.layer_structure == null)
{
p_par.layer_structure = new LayerStructure(board_construction_info.layer_info);
}
Keyword.PLANE_SCOPE.read_scope(p_par);
}
else if (next_token == Keyword.AUTOROUTE_SETTINGS)
{
if (p_par.layer_structure == null)
{
p_par.layer_structure = new LayerStructure(board_construction_info.layer_info);
p_par.autoroute_settings = AutorouteSettings.read_scope(p_par.scanner, p_par.layer_structure);
}
}
else if (next_token == Keyword.CONTROL)
{
read_ok = read_control_scope(p_par);
}
else if (next_token == Keyword.FLIP_STYLE)
{
flip_style_rotate_first = PlaceControl.read_flip_style_rotate_first(p_par.scanner);
}
else if (next_token == Keyword.SNAP_ANGLE)
{
board.AngleRestriction snap_angle = read_snap_angle(p_par.scanner);
if (snap_angle != null)
{
p_par.snap_angle = snap_angle;
}
}
else
{
skip_scope(p_par.scanner);
}
}
if (!read_ok)
{
return false;
}
}
boolean result = true;
if (p_par.board_handling.get_routing_board() == null)
{
result = create_board(p_par, board_construction_info);
}
board.RoutingBoard board = p_par.board_handling.get_routing_board();
if (board == null)
{
return false;
}
if (flip_style_rotate_first)
{
board.components.set_flip_style_rotate_first(true);
}
FixedState fixed_state;
if (board.get_test_level() == TestLevel.RELEASE_VERSION)
{
fixed_state = FixedState.SYSTEM_FIXED;
}
else
{
fixed_state = FixedState.USER_FIXED;
}
// insert the keepouts
for (Shape.ReadAreaScopeResult curr_area : keepout_list)
{
if (!insert_keepout(curr_area, p_par, KeepoutType.keepout, fixed_state))
{
return false;
}
}
for (Shape.ReadAreaScopeResult curr_area : via_keepout_list)
{
if (!insert_keepout(curr_area, p_par, KeepoutType.via_keepout, FixedState.SYSTEM_FIXED))
{
return false;
}
}
for (Shape.ReadAreaScopeResult curr_area : place_keepout_list)
{
if (!insert_keepout(curr_area, p_par, KeepoutType.place_keepout, FixedState.SYSTEM_FIXED))
{
return false;
}
}
// insert the planes.
Iterator<ReadScopeParameter.PlaneInfo> it = p_par.plane_list.iterator();
while (it.hasNext())
{
ReadScopeParameter.PlaneInfo plane_info = it.next();
Net.Id net_id = new Net.Id(plane_info.net_name, 1);
if (!p_par.netlist.contains(net_id))
{
Net new_net = p_par.netlist.add_net(net_id);
if (new_net != null)
{
board.rules.nets.add(new_net.id.name, new_net.id.subnet_number, true);
}
}
rules.Net curr_net = board.rules.nets.get(plane_info.net_name, 1);
if (curr_net == null)
{
System.out.println("Plane.read_scope: net not found");
continue;
}
geometry.planar.Area plane_area =
Shape.transform_area_to_board(plane_info.area.shape_list, p_par.coordinate_transform);
Layer curr_layer = (plane_info.area.shape_list.iterator().next()).layer;
if (curr_layer.no >= 0)
{
int clearance_class_no;
if (plane_info.area.clearance_class_name != null)
{
clearance_class_no = board.rules.clearance_matrix.get_no(plane_info.area.clearance_class_name);
if (clearance_class_no < 0)
{
System.out.println("Structure.read_scope: clearance class not found");
clearance_class_no = BoardRules.clearance_class_none();
}
}
else
{
clearance_class_no = curr_net.get_class().default_item_clearance_classes.get(rules.DefaultItemClearanceClasses.ItemClass.AREA);
}
int[] net_numbers = new int[1];
net_numbers[0] = curr_net.net_number;
board.insert_conduction_area(plane_area, curr_layer.no, net_numbers, clearance_class_no,
false, FixedState.SYSTEM_FIXED);
}
else
{
System.out.println("Plane.read_scope: unexpected layer name");
return false;
}
}
insert_missing_power_planes(board_construction_info.layer_info, p_par.netlist, board);
p_par.board_handling.initialize_manual_trace_half_widths();
if (p_par.autoroute_settings != null)
{
p_par.board_handling.get_settings().autoroute_settings = p_par.autoroute_settings;
}
return result;
}
public static void write_scope(WriteScopeParameter p_par) throws java.io.IOException
{
p_par.file.start_scope();
p_par.file.write("structure");
// write the bounding box
p_par.file.start_scope();
p_par.file.write("boundary");
IntBox bounds = p_par.board.get_bounding_box();
double[] rect_coor = p_par.coordinate_transform.board_to_dsn(bounds);
Rectangle bounding_rectangle = new Rectangle(Layer.PCB, rect_coor);
bounding_rectangle.write_scope(p_par.file, p_par.identifier_type);
p_par.file.end_scope();
// lookup the outline in the board
Storable curr_ob = null;
Iterator<UndoableObjects.UndoableObjectNode> it = p_par.board.item_list.start_read_object();
for (;;)
{
curr_ob = p_par.board.item_list.read_object(it);
if (curr_ob == null)
{
break;
}
if (curr_ob instanceof board.BoardOutline)
{
break;
}
}
if (curr_ob == null)
{
System.out.println("Structure.write_scope; outline not found");
return;
}
board.BoardOutline outline = (board.BoardOutline) curr_ob;
// write the outline
for (int i = 0; i < outline.shape_count(); ++i)
{
Shape outline_shape = p_par.coordinate_transform.board_to_dsn(outline.get_shape(i), Layer.SIGNAL);
p_par.file.start_scope();
p_par.file.write("boundary");
outline_shape.write_scope(p_par.file, p_par.identifier_type);
p_par.file.end_scope();
}
write_snap_angle(p_par.file, p_par.board.rules.get_trace_angle_restriction());
// write the routing vias
write_via_padstacks(p_par.board.library, p_par.file, p_par.identifier_type);
// write the control scope
write_control_scope(p_par.board.rules, p_par.file);
write_default_rules(p_par);
// write the autoroute settings
AutorouteSettings.write_scope(p_par.file, p_par.autoroute_settings,
p_par.board.layer_structure, p_par.identifier_type);
// write the keepouts
it = p_par.board.item_list.start_read_object();
for (;;)
{
curr_ob = p_par.board.item_list.read_object(it);
if (curr_ob == null)
{
break;
}
if (!(curr_ob instanceof board.ObstacleArea))
{
continue;
}
board.ObstacleArea curr_keepout = (board.ObstacleArea) curr_ob;
if (curr_keepout.get_component_no() != 0)
{
// keepouts belonging to a component are not written individually.
continue;
}
if (curr_keepout instanceof board.ConductionArea)
{
// conduction area will be written later.
continue;
}
write_keepout_scope(p_par, curr_keepout);
}
// write the conduction areas
it = p_par.board.item_list.start_read_object();
for (;;)
{
curr_ob = p_par.board.item_list.read_object(it);
if (curr_ob == null)
{
break;
}
if (!(curr_ob instanceof board.ConductionArea))
{
continue;
}
board.ConductionArea curr_area = (board.ConductionArea) curr_ob;
if (p_par.board.layer_structure.arr[curr_area.get_layer()].is_signal)
{
// These conduction areas are written in the wiring scope.
continue;
}
Plane.write_scope(p_par, (board.ConductionArea) curr_ob);
}
p_par.file.end_scope();
}
static void write_default_rules(WriteScopeParameter p_par) throws java.io.IOException
{
// write the default rule using 0 as default layer.
Rule.write_default_rule(p_par, 0);
// write the layer structure
for (int i = 0; i < p_par.board.layer_structure.arr.length; ++i)
{
boolean write_layer_rule =
p_par.board.rules.get_default_net_class().get_trace_half_width(i) != p_par.board.rules.get_default_net_class().get_trace_half_width(0) || !clearance_equals(p_par.board.rules.clearance_matrix, i, 0);
Layer.write_scope(p_par, i, write_layer_rule);
}
}
private static void write_via_padstacks(library.BoardLibrary p_library, IndentFileWriter p_file,
IdentifierType p_identifier_type) throws java.io.IOException
{
p_file.new_line();
p_file.write("(via");
for (int i = 0; i < p_library.via_padstack_count(); ++i)
{
library.Padstack curr_padstack = p_library.get_via_padstack(i);
if (curr_padstack != null)
{
p_file.write(" ");
p_identifier_type.write(curr_padstack.name, p_file);
}
else
{
System.out.println("Structure.write_via_padstacks: padstack is null");
}
}
p_file.write(")");
}
private static void write_control_scope(rules.BoardRules p_rules, IndentFileWriter p_file) throws java.io.IOException
{
p_file.start_scope();
p_file.write("control");
p_file.new_line();
p_file.write("(via_at_smd ");
boolean via_at_smd_allowed = false;
for (int i = 0; i < p_rules.via_infos.count(); ++i)
{
if (p_rules.via_infos.get(i).attach_smd_allowed())
{
via_at_smd_allowed = true;
break;
}
}
if (via_at_smd_allowed)
{
p_file.write("on)");
}
else
{
p_file.write("off)");
}
p_file.end_scope();
}
private static void write_keepout_scope(WriteScopeParameter p_par, board.ObstacleArea p_keepout) throws java.io.IOException
{
geometry.planar.Area keepout_area = p_keepout.get_area();
int layer_no = p_keepout.get_layer();
board.Layer board_layer = p_par.board.layer_structure.arr[layer_no];
Layer keepout_layer = new Layer(board_layer.name, layer_no, board_layer.is_signal);
geometry.planar.Shape boundary_shape;
geometry.planar.Shape[] holes;
if (keepout_area instanceof geometry.planar.Shape)
{
boundary_shape = (geometry.planar.Shape) keepout_area;
holes = new geometry.planar.Shape[0];
}
else
{
boundary_shape = keepout_area.get_border();
holes = keepout_area.get_holes();
}
p_par.file.start_scope();
if (p_keepout instanceof board.ViaObstacleArea)
{
p_par.file.write("via_keepout");
}
else
{
p_par.file.write("keepout");
}
Shape dsn_shape = p_par.coordinate_transform.board_to_dsn(boundary_shape, keepout_layer);
if (dsn_shape != null)
{
dsn_shape.write_scope(p_par.file, p_par.identifier_type);
}
for (int i = 0; i < holes.length; ++i)
{
Shape dsn_hole = p_par.coordinate_transform.board_to_dsn(holes[i], keepout_layer);
dsn_hole.write_hole_scope(p_par.file, p_par.identifier_type);
}
if (p_keepout.clearance_class_no() > 0)
{
Rule.write_item_clearance_class(p_par.board.rules.clearance_matrix.get_name(p_keepout.clearance_class_no()),
p_par.file, p_par.identifier_type);
}
p_par.file.end_scope();
}
private static boolean read_boundary_scope(Scanner p_scanner, BoardConstructionInfo p_board_construction_info)
{
Shape curr_shape = Shape.read_scope(p_scanner, null);
// overread the closing bracket.
try
{
Object prev_token = null;
for (;;)
{
Object next_token = p_scanner.next_token();
if (next_token == Keyword.CLOSED_BRACKET)
{
break;
}
if (prev_token == Keyword.OPEN_BRACKET)
{
if (next_token == Keyword.CLEARANCE_CLASS)
{
p_board_construction_info.outline_clearance_class_name = DsnFile.read_string_scope(p_scanner);
}
}
prev_token = next_token;
}
}
catch (java.io.IOException e)
{
System.out.println("Structure.read_boundary_scope: IO error scanning file");
return false;
}
if (curr_shape == null)
{
System.out.println("Structure.read_boundary_scope: shape is null");
return true;
}
if (curr_shape.layer == Layer.PCB)
{
if (p_board_construction_info.bounding_shape == null)
{
p_board_construction_info.bounding_shape = curr_shape;
}
else
{
System.out.println("Structure.read_boundary_scope: exact 1 bounding_shape expected");
}
}
else if (curr_shape.layer == Layer.SIGNAL)
{
p_board_construction_info.outline_shapes.add(curr_shape);
}
else
{
System.out.println("Structure.read_boundary_scope: unexpected layer");
}
return true;
}
static boolean read_layer_scope(Scanner p_scanner, BoardConstructionInfo p_board_construction_info, String p_string_quote)
{
try
{
boolean layer_ok = true;
boolean is_signal = true;
Object next_token = p_scanner.next_token();
if (!(next_token instanceof String))
{
System.out.println("Structure.read_layer_scope: String expected");
return false;
}
Collection<String> net_names = new LinkedList<String>();
String layer_string = (String) next_token;
next_token = p_scanner.next_token();
while (next_token != Keyword.CLOSED_BRACKET)
{
if (next_token != Keyword.OPEN_BRACKET)
{
System.out.println("Structure.read_layer_scope: ( expected");
return false;
}
next_token = p_scanner.next_token();
if (next_token == Keyword.TYPE)
{
next_token = p_scanner.next_token();
if (next_token == Keyword.POWER)
{
is_signal = false;
}
else if (next_token != Keyword.SIGNAL)
{
System.out.print("Structure.read_layer_scope: unknown layer type ");
if (next_token instanceof String)
{
System.out.print((String) next_token);
}
System.out.println();
layer_ok = false;
}
next_token = p_scanner.next_token();
if (next_token != Keyword.CLOSED_BRACKET)
{
System.out.println("Structure.read_layer_scope: ) expected");
return false;
}
}
else if (next_token == Keyword.RULE)
{
Collection<Rule> curr_rules = Rule.read_scope(p_scanner);
p_board_construction_info.layer_dependent_rules.add(new LayerRule(layer_string, curr_rules));
}
else if (next_token == Keyword.USE_NET)
{
for (;;)
{
p_scanner.yybegin(SpecctraFileScanner.NAME);
next_token = p_scanner.next_token();
if (next_token == Keyword.CLOSED_BRACKET)
{
break;
}
if (next_token instanceof String)
{
net_names.add((String) next_token);
}
else
{
System.out.println("Structure.read_layer_scope: string expected");
}
}
}
else
{
skip_scope(p_scanner);
}
next_token = p_scanner.next_token();
}
if (layer_ok)
{
Layer curr_layer = new Layer(layer_string, p_board_construction_info.found_layer_count, is_signal, net_names);
p_board_construction_info.layer_info.add(curr_layer);
++p_board_construction_info.found_layer_count;
}
}
catch (java.io.IOException e)
{
System.out.println("Layer.read_scope: IO error scanning file");
System.out.println(e);
return false;
}
return true;
}
static Collection<String> read_via_padstacks(Scanner p_scanner)
{
try
{
Collection<String> normal_vias = new LinkedList<String>();
Collection<String> spare_vias = new LinkedList<String>();
for (;;)
{
Object next_token = p_scanner.next_token();
if (next_token == Keyword.CLOSED_BRACKET)
{
break;
}
if (next_token == Keyword.OPEN_BRACKET)
{
next_token = p_scanner.next_token();
if (next_token == Keyword.SPARE)
{
spare_vias = read_via_padstacks(p_scanner);
}
else
{
skip_scope(p_scanner);
}
}
else if (next_token instanceof String)
{
normal_vias.add((String) next_token);
}
else
{
System.out.println("Structure.read_via_padstack: String expected");
return null;
}
}
// add the spare vias to the end of the list
normal_vias.addAll(spare_vias);
return normal_vias;
}
catch (java.io.IOException e)
{
System.out.println("Structure.read_via_padstack: IO error scanning file");
return null;
}
}
private static boolean read_control_scope(ReadScopeParameter p_par)
{
Object next_token = null;
for (;;)
{
Object prev_token = next_token;
try
{
next_token = p_par.scanner.next_token();
}
catch (java.io.IOException e)
{
System.out.println("Structure.read_control_scope: IO error scanning file");
return false;
}
if (next_token == null)
{
System.out.println("Structure.read_control_scope: unexpected end of file");
return false;
}
if (next_token == CLOSED_BRACKET)
{
// end of scope
break;
}
if (prev_token == OPEN_BRACKET)
{
if (next_token == Keyword.VIA_AT_SMD)
{
p_par.via_at_smd_allowed = DsnFile.read_on_off_scope(p_par.scanner);
}
else
{
skip_scope(p_par.scanner);
}
}
}
return true;
}
static board.AngleRestriction read_snap_angle(Scanner p_scanner)
{
try
{
Object next_token = p_scanner.next_token();
board.AngleRestriction snap_angle;
if (next_token == Keyword.NINETY_DEGREE)
{
snap_angle = board.AngleRestriction.NINETY_DEGREE;
}
else if (next_token == Keyword.FORTYFIVE_DEGREE)
{
snap_angle = board.AngleRestriction.FORTYFIVE_DEGREE;
}
else if (next_token == Keyword.NONE)
{
snap_angle = board.AngleRestriction.NONE;
}
else
{
System.out.println("Structure.read_snap_angle_scope: unexpected token");
return null;
}
next_token = p_scanner.next_token();
if (next_token != Keyword.CLOSED_BRACKET)
{
System.out.println("Structure.read_selection_layer_scop: closing bracket expected");
return null;
}
return snap_angle;
}
catch (java.io.IOException e)
{
System.out.println("Structure.read_snap_angl: IO error scanning file");
return null;
}
}
static void write_snap_angle(IndentFileWriter p_file, board.AngleRestriction p_angle_restriction) throws java.io.IOException
{
p_file.start_scope();
p_file.write("snap_angle ");
p_file.new_line();
if (p_angle_restriction == board.AngleRestriction.NINETY_DEGREE)
{
p_file.write("ninety_degree");
}
else if (p_angle_restriction == board.AngleRestriction.FORTYFIVE_DEGREE)
{
p_file.write("fortyfive_degree");
}
else
{
p_file.write("none");
}
p_file.end_scope();
}
private boolean create_board(ReadScopeParameter p_par, BoardConstructionInfo p_board_construction_info)
{
int layer_count = p_board_construction_info.layer_info.size();
if (layer_count == 0)
{
System.out.println("Structure.create_board: layers missing in structure scope");
return false;
}
if (p_board_construction_info.bounding_shape == null)
{
// happens if the boundary shape with layer pcb is missing
if (p_board_construction_info.outline_shapes.isEmpty())
{
System.out.println("Structure.create_board: outline missing");
p_par.board_outline_ok = false;
return false;
}
Iterator<Shape> it = p_board_construction_info.outline_shapes.iterator();
Rectangle bounding_box = it.next().bounding_box();
while (it.hasNext())
{
bounding_box = bounding_box.union(it.next().bounding_box());
}
p_board_construction_info.bounding_shape = bounding_box;
}
Rectangle bounding_box = p_board_construction_info.bounding_shape.bounding_box();
board.Layer[] board_layer_arr = new board.Layer[layer_count];
Iterator<Layer> it = p_board_construction_info.layer_info.iterator();
for (int i = 0; i < layer_count; ++i)
{
Layer curr_layer = it.next();
if (curr_layer.no < 0 || curr_layer.no >= layer_count)
{
System.out.println("Structure.create_board: illegal layer number");
return false;
}
board_layer_arr[i] = new board.Layer(curr_layer.name, curr_layer.is_signal);
}
board.LayerStructure board_layer_structure = new board.LayerStructure(board_layer_arr);
p_par.layer_structure = new LayerStructure(p_board_construction_info.layer_info);
// Calculate an appropritate scaling between dsn coordinates and board coordinates.
int scale_factor = Math.max(p_par.resolution, 1);
double max_coor = 0;
for (int i = 0; i < 4; ++i)
{
max_coor = Math.max(max_coor, Math.abs(bounding_box.coor[i] * p_par.resolution));
}
if (max_coor == 0)
{
p_par.board_outline_ok = false;
return false;
}
// make scalefactor smaller, if there is a danger of integer overflow.
while (5 * max_coor >= geometry.planar.Limits.CRIT_INT)
{
scale_factor /= 10;
max_coor /= 10;
}
p_par.coordinate_transform = new CoordinateTransform(scale_factor, 0, 0);
IntBox bounds = (IntBox) bounding_box.transform_to_board(p_par.coordinate_transform);
bounds = bounds.offset(1000);
Collection<PolylineShape> board_outline_shapes = new LinkedList<PolylineShape>();
for (Shape curr_shape : p_board_construction_info.outline_shapes)
{
if (curr_shape instanceof PolygonPath)
{
PolygonPath curr_path = (PolygonPath) curr_shape;
if (curr_path.width != 0)
{
// set the width to 0, because the offset function used in transform_to_board is not implemented
// for shapes, which are not convex.
curr_shape = new PolygonPath(curr_path.layer, 0, curr_path.coordinate_arr);
}
}
PolylineShape curr_board_shape = (PolylineShape) curr_shape.transform_to_board(p_par.coordinate_transform);
if (curr_board_shape.dimension() > 0)
{
board_outline_shapes.add(curr_board_shape);
}
}
if (board_outline_shapes.isEmpty())
{
// construct an outline from the bounding_shape, if the outline is missing.
PolylineShape curr_board_shape = (PolylineShape) p_board_construction_info.bounding_shape.transform_to_board(p_par.coordinate_transform);
board_outline_shapes.add(curr_board_shape);
}
Collection<PolylineShape> hole_shapes = separate_holes(board_outline_shapes);
rules.ClearanceMatrix clearance_matrix = rules.ClearanceMatrix.get_default_instance(board_layer_structure, 0);
rules.BoardRules board_rules = new rules.BoardRules(board_layer_structure, clearance_matrix);
board.Communication.SpecctraParserInfo specctra_parser_info =
new board.Communication.SpecctraParserInfo(p_par.string_quote, p_par.host_cad,
p_par.host_version, p_par.constants, p_par.write_resolution, p_par.dsn_file_generated_by_host);
board.Communication board_communication =
new board.Communication(p_par.unit, p_par.resolution, specctra_parser_info,
p_par.coordinate_transform, p_par.item_id_no_generator, p_par.observers);
PolylineShape[] outline_shape_arr = new PolylineShape[board_outline_shapes.size()];
Iterator<PolylineShape> it2 = board_outline_shapes.iterator();
for (int i = 0; i < outline_shape_arr.length; ++i)
{
outline_shape_arr[i] = it2.next();
}
update_board_rules(p_par, p_board_construction_info, board_rules);
board_rules.set_trace_angle_restriction(p_par.snap_angle);
p_par.board_handling.create_board(bounds, board_layer_structure, outline_shape_arr,
p_board_construction_info.outline_clearance_class_name, board_rules,
board_communication, p_par.test_level);
board.BasicBoard board = p_par.board_handling.get_routing_board();
// Insert the holes in the board outline as keepouts.
for (PolylineShape curr_outline_hole : hole_shapes)
{
for (int i = 0; i < board_layer_structure.arr.length; ++i)
{
board.insert_obstacle(curr_outline_hole, i, 0, FixedState.SYSTEM_FIXED);
}
}
return true;
}
// Check, if a conduction area is inserted on each plane,
// and insert evtl. a conduction area
private static void insert_missing_power_planes(Collection<Layer> p_layer_info,
NetList p_netlist, board.BasicBoard p_board)
{
Collection<board.ConductionArea> conduction_areas = p_board.get_conduction_areas();
for (Layer curr_layer : p_layer_info)
{
if (curr_layer.is_signal)
{
continue;
}
boolean conduction_area_found = false;
for (board.ConductionArea curr_conduction_area : conduction_areas)
{
if (curr_conduction_area.get_layer() == curr_layer.no)
{
conduction_area_found = true;
break;
}
}
if (!conduction_area_found && !curr_layer.net_names.isEmpty())
{
String curr_net_name = curr_layer.net_names.iterator().next();
Net.Id curr_net_id = new Net.Id(curr_net_name, 1);
if (!p_netlist.contains(curr_net_id))
{
Net new_net = p_netlist.add_net(curr_net_id);
if (new_net != null)
{
p_board.rules.nets.add(new_net.id.name, new_net.id.subnet_number, true);
}
}
rules.Net curr_net = p_board.rules.nets.get(curr_net_id.name, curr_net_id.subnet_number);
{
if (curr_net == null)
{
System.out.println("Structure.insert_missing_power_planes: net not found");
continue;
}
}
int[] net_numbers = new int[1];
net_numbers[0] = curr_net.net_number;
p_board.insert_conduction_area(p_board.bounding_box, curr_layer.no, net_numbers, BoardRules.clearance_class_none(),
false, FixedState.SYSTEM_FIXED);
}
}
}
/**
* Calculates shapes in p_outline_shapes, which are holes in the outline and returns
* them in the result list.
*/
private static Collection<PolylineShape> separate_holes(Collection<PolylineShape> p_outline_shapes)
{
OutlineShape shape_arr[] = new OutlineShape[p_outline_shapes.size()];
Iterator<PolylineShape> it = p_outline_shapes.iterator();
for (int i = 0; i < shape_arr.length; ++i)
{
shape_arr[i] = new OutlineShape(it.next());
}
for (int i = 0; i < shape_arr.length; ++i)
{
OutlineShape curr_shape = shape_arr[i];
for (int j = 0; j < shape_arr.length; ++j)
{
// check if shape_arr[j] may be contained in shape_arr[i]
OutlineShape other_shape = shape_arr[j];
if (i == j || other_shape.is_hole)
{
continue;
}
if (!other_shape.bounding_box.contains(curr_shape.bounding_box))
{
continue;
}
curr_shape.is_hole = other_shape.contains_all_corners(curr_shape);
}
}
Collection<PolylineShape> hole_list = new LinkedList<PolylineShape>();
for (int i = 0; i < shape_arr.length; ++i)
{
if (shape_arr[i].is_hole)
{
p_outline_shapes.remove(shape_arr[i].shape);
hole_list.add(shape_arr[i].shape);
}
}
return hole_list;
}
/**
* Updates the board rules from the rules read from the dsn file.
*/
private static void update_board_rules(ReadScopeParameter p_par,
BoardConstructionInfo p_board_construction_info, BoardRules p_board_rules)
{
boolean smd_to_turn_gap_found = false;
// update the clearance matrix
Iterator<Rule> it = p_board_construction_info.default_rules.iterator();
while (it.hasNext())
{
Rule curr_ob = it.next();
if (curr_ob instanceof Rule.ClearanceRule)
{
Rule.ClearanceRule curr_rule = (Rule.ClearanceRule) curr_ob;
if (set_clearance_rule(curr_rule, -1, p_par.coordinate_transform, p_board_rules, p_par.string_quote))
{
smd_to_turn_gap_found = true;
}
}
}
// update width rules
it = p_board_construction_info.default_rules.iterator();
while (it.hasNext())
{
Object curr_ob = it.next();
if (curr_ob instanceof Rule.WidthRule)
{
double wire_width = ((Rule.WidthRule) curr_ob).value;
int trace_halfwidth = (int) Math.round(p_par.coordinate_transform.dsn_to_board(wire_width) / 2);
p_board_rules.set_default_trace_half_widths(trace_halfwidth);
}
}
Iterator<LayerRule> it3 = p_board_construction_info.layer_dependent_rules.iterator();
while (it3.hasNext())
{
LayerRule layer_rule = it3.next();
int layer_no = p_par.layer_structure.get_no(layer_rule.layer_name);
if (layer_no < 0)
{
continue;
}
Iterator<Rule> it2 = layer_rule.rule.iterator();
while (it2.hasNext())
{
Rule curr_ob = it2.next();
if (curr_ob instanceof Rule.WidthRule)
{
double wire_width = ((Rule.WidthRule) curr_ob).value;
int trace_halfwidth = (int) Math.round(p_par.coordinate_transform.dsn_to_board(wire_width) / 2);
p_board_rules.set_default_trace_half_width(layer_no, trace_halfwidth);
}
else if (curr_ob instanceof Rule.ClearanceRule)
{
Rule.ClearanceRule curr_rule = (Rule.ClearanceRule) curr_ob;
set_clearance_rule(curr_rule, layer_no, p_par.coordinate_transform, p_board_rules, p_par.string_quote);
}
}
}
if (!smd_to_turn_gap_found)
{
p_board_rules.set_pin_edge_to_turn_dist(p_board_rules.get_min_trace_half_width());
}
}
/**
* Converts a dsn clearance rule into a board clearance rule.
* If p_layer_no < 0, the rule is set on all layers.
* Returns true, if the string smd_to_turn_gap was found.
*/
static boolean set_clearance_rule(Rule.ClearanceRule p_rule, int p_layer_no,
CoordinateTransform p_coordinate_transform, BoardRules p_board_rules, String p_string_quote)
{
boolean result = false;
int curr_clearance = (int) Math.round(p_coordinate_transform.dsn_to_board(p_rule.value));
if (p_rule.clearance_class_pairs.isEmpty())
{
if (p_layer_no < 0)
{
p_board_rules.clearance_matrix.set_default_value(curr_clearance);
}
else
{
p_board_rules.clearance_matrix.set_default_value(p_layer_no, curr_clearance);
}
return result;
}
if (contains_wire_clearance_pair(p_rule.clearance_class_pairs))
{
create_default_clearance_classes(p_board_rules);
}
Iterator<String> it = p_rule.clearance_class_pairs.iterator();
while (it.hasNext())
{
String curr_string = it.next();
if (curr_string.equalsIgnoreCase("smd_to_turn_gap"))
{
p_board_rules.set_pin_edge_to_turn_dist(curr_clearance);
result = true;
continue;
}
String[] curr_pair;
if (curr_string.startsWith(p_string_quote))
{
// split at the second occurance of p_string_quote
curr_string = curr_string.substring(p_string_quote.length());
curr_pair = curr_string.split(p_string_quote, 2);
if (curr_pair.length != 2 || !curr_pair[1].startsWith("_"))
{
System.out.println("Structure.set_clearance_rule: '_' exprcted");
continue;
}
curr_pair[1] = curr_pair[1].substring(1);
}
else
{
curr_pair = curr_string.split("_", 2);
if (curr_pair.length != 2)
{
// pairs with more than 1 underline like smd_via_same_net are not implemented
continue;
}
}
if (curr_pair[1].startsWith(p_string_quote) && curr_pair[1].endsWith(p_string_quote))
{
// remove the quotes
curr_pair[1] = curr_pair[1].substring(1, curr_pair[1].length() - 1);
}
else
{
String[] tmp_pair = curr_pair[1].split("_", 2);
if (tmp_pair.length != 1)
{
// pairs with more than 1 underline like smd_via_same_net are not implemented
continue;
}
}
int first_class_no;
if (curr_pair[0].equals("wire"))
{
first_class_no = 1; // default class
}
else
{
first_class_no = p_board_rules.clearance_matrix.get_no(curr_pair[0]);
}
if (first_class_no < 0)
{
first_class_no = append_clearance_class(p_board_rules, curr_pair[0]);
}
int second_class_no;
if (curr_pair[1].equals("wire"))
{
second_class_no = 1; // default class
}
else
{
second_class_no = p_board_rules.clearance_matrix.get_no(curr_pair[1]);
}
if (second_class_no < 0)
{
second_class_no = append_clearance_class(p_board_rules, curr_pair[1]);
}
if (p_layer_no < 0)
{
p_board_rules.clearance_matrix.set_value(first_class_no, second_class_no, curr_clearance);
p_board_rules.clearance_matrix.set_value(second_class_no, first_class_no, curr_clearance);
}
else
{
p_board_rules.clearance_matrix.set_value(first_class_no, second_class_no, p_layer_no, curr_clearance);
p_board_rules.clearance_matrix.set_value(second_class_no, first_class_no, p_layer_no, curr_clearance);
}
}
return result;
}
static boolean contains_wire_clearance_pair(Collection<String> p_clearance_pairs)
{
for (String curr_pair : p_clearance_pairs)
{
if (curr_pair.startsWith("wire_") || curr_pair.endsWith("_wire"))
{
return true;
}
}
return false;
}
static private void create_default_clearance_classes(BoardRules p_board_rules)
{
append_clearance_class(p_board_rules, "via");
append_clearance_class(p_board_rules, "smd");
append_clearance_class(p_board_rules, "pin");
append_clearance_class(p_board_rules, "area");
}
static private int append_clearance_class(BoardRules p_board_rules, String p_name)
{
p_board_rules.clearance_matrix.append_class(p_name);
int result = p_board_rules.clearance_matrix.get_no(p_name);
rules.NetClass default_net_class = p_board_rules.get_default_net_class();
if (p_name.equals("via"))
{
default_net_class.default_item_clearance_classes.set(ItemClass.VIA, result);
}
else if (p_name.equals("pin"))
{
default_net_class.default_item_clearance_classes.set(ItemClass.PIN, result);
}
else if (p_name.equals("smd"))
{
default_net_class.default_item_clearance_classes.set(ItemClass.SMD, result);
}
else if (p_name.equals("area"))
{
default_net_class.default_item_clearance_classes.set(ItemClass.AREA, result);
}
return result;
}
/**
* Returns true, if all clearance values on the 2 input layers are equal.
*/
private static boolean clearance_equals(rules.ClearanceMatrix p_cl_matrix, int p_layer_1, int p_layer_2)
{
if (p_layer_1 == p_layer_2)
{
return true;
}
for (int i = 1; i < p_cl_matrix.get_class_count(); ++i)
{
for (int j = i; j < p_cl_matrix.get_class_count(); ++j)
{
if (p_cl_matrix.value(i, j, p_layer_1) != p_cl_matrix.value(i, j, p_layer_2))
{
return false;
}
}
}
return true;
}
private static boolean insert_keepout(Shape.ReadAreaScopeResult p_area, ReadScopeParameter p_par, KeepoutType p_keepout_type, FixedState p_fixed_state)
{
geometry.planar.Area keepout_area =
Shape.transform_area_to_board(p_area.shape_list, p_par.coordinate_transform);
if (keepout_area.dimension() < 2)
{
System.out.println("Structure.insert_keepout: keepout is not an area");
return true;
}
board.BasicBoard board = p_par.board_handling.get_routing_board();
if (board == null)
{
System.out.println("Structure.insert_keepout: board not initialized");
return false;
}
Layer curr_layer = (p_area.shape_list.iterator().next()).layer;
if (curr_layer == Layer.SIGNAL)
{
for (int i = 0; i < board.get_layer_count(); ++i)
{
if (p_par.layer_structure.arr[i].is_signal)
{
insert_keepout(board, keepout_area, i, p_area.clearance_class_name, p_keepout_type, p_fixed_state);
}
}
}
else if (curr_layer.no >= 0)
{
insert_keepout(board, keepout_area, curr_layer.no, p_area.clearance_class_name, p_keepout_type, p_fixed_state);
}
else
{
System.out.println("Structure.insert_keepout: unknown layer name");
return false;
}
return true;
}
private static void insert_keepout(board.BasicBoard p_board, geometry.planar.Area p_area, int p_layer,
String p_clearance_class_name, KeepoutType p_keepout_type, FixedState p_fixed_state)
{
int clearance_class_no;
if (p_clearance_class_name == null)
{
clearance_class_no =
p_board.rules.get_default_net_class().default_item_clearance_classes.get(rules.DefaultItemClearanceClasses.ItemClass.AREA);
}
else
{
clearance_class_no = p_board.rules.clearance_matrix.get_no(p_clearance_class_name);
if (clearance_class_no < 0)
{
System.out.println("Keepout.insert_leepout: clearance class not found");
clearance_class_no = BoardRules.clearance_class_none();
}
}
if (p_keepout_type == KeepoutType.via_keepout)
{
p_board.insert_via_obstacle(p_area, p_layer, clearance_class_no, p_fixed_state);
}
else if (p_keepout_type == KeepoutType.place_keepout)
{
p_board.insert_component_obstacle(p_area, p_layer, clearance_class_no, p_fixed_state);
}
else
{
p_board.insert_obstacle(p_area, p_layer, clearance_class_no, p_fixed_state);
}
}
enum KeepoutType
{
keepout, via_keepout, place_keepout
}
private static class BoardConstructionInfo
{
Collection<Layer> layer_info = new LinkedList<Layer>();
Shape bounding_shape;
java.util.List<Shape> outline_shapes = new LinkedList<Shape>();
String outline_clearance_class_name = null;
int found_layer_count = 0;
Collection<Rule> default_rules = new LinkedList<Rule>();
Collection<LayerRule> layer_dependent_rules = new LinkedList<LayerRule>();
}
private static class LayerRule
{
LayerRule(String p_layer_name, Collection<Rule> p_rule)
{
layer_name = p_layer_name;
rule = p_rule;
}
final String layer_name;
final Collection<Rule> rule;
}
/**
* Used to seperate the holes in the outline.
*/
private static class OutlineShape
{
public OutlineShape(PolylineShape p_shape)
{
shape = p_shape;
bounding_box = p_shape.bounding_box();
convex_shapes = p_shape.split_to_convex();
is_hole = false;
}
/**
* Returns true, if this shape contains all corners of p_other_shape.
*/
private boolean contains_all_corners(OutlineShape p_other_shape)
{
if (this.convex_shapes == null)
{
// calculation of the convex shapes failed
return false;
}
int corner_count = p_other_shape.shape.border_line_count();
for (int i = 0; i < corner_count; ++i)
{
Point curr_corner = p_other_shape.shape.corner(i);
boolean is_contained = false;
for (int j = 0; j < this.convex_shapes.length; ++j)
{
if (this.convex_shapes[j].contains(curr_corner))
{
is_contained = true;
break;
}
}
if (!is_contained)
{
return false;
}
}
return true;
}
final PolylineShape shape;
final IntBox bounding_box;
final TileShape[] convex_shapes;
boolean is_hole;
}
}