/* * 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; } }