freerouting/src/main/java/eu/mihosoft/freerouting/board/ShapeTraceEntries.java

967 lines
34 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.
*/
package eu.mihosoft.freerouting.board;
import eu.mihosoft.freerouting.geometry.planar.ConvexShape;
import eu.mihosoft.freerouting.geometry.planar.FloatPoint;
import eu.mihosoft.freerouting.geometry.planar.Line;
import eu.mihosoft.freerouting.geometry.planar.Point;
import eu.mihosoft.freerouting.geometry.planar.Polyline;
import eu.mihosoft.freerouting.geometry.planar.TileShape;
import java.util.Collection;
import java.util.Iterator;
/**
* Auxiliary class used by the shove functions
*
* @author Alfons Wirtz
*/
public class ShapeTraceEntries
{
/**
* Used for shoving traces and vias out of the input shape.
* p_from_side.no is the side of p_shape, from where the shove comes.
* if p_from_side.no < 0, it will be calculated internally.
*/
ShapeTraceEntries(TileShape p_shape, int p_layer, int[] p_own_net_nos, int p_cl_type,
CalcFromSide p_from_side, RoutingBoard p_board)
{
shape = p_shape;
layer = p_layer;
own_net_nos = p_own_net_nos;
cl_class = p_cl_type;
from_side = p_from_side;
board = p_board;
list_anchor = null;
trace_piece_count = 0;
max_stack_level = 0;
shove_via_list = new java.util.LinkedList<Via>();
}
/**
* Stores traces and vias in p_item_list.
* Returns false, if p_item_list contains obstacles,
* which cannot be shoved aside.
* If p_is_pad_check. the check is for vias, otherwise it is for traces.
* If p_copper_sharing_allowed, overlaps with traces or pads of the own net are allowed.
*/
boolean store_items(Collection<Item> p_item_list, boolean p_is_pad_check, boolean p_copper_sharing_allowed)
{
Iterator<Item> it = p_item_list.iterator();
while(it.hasNext())
{
Item curr_item = it.next();
if (!p_is_pad_check && curr_item instanceof ViaObstacleArea || curr_item instanceof ComponentObstacleArea)
{
continue;
}
boolean contains_own_net = curr_item.shares_net_no(this.own_net_nos);
if (curr_item instanceof ConductionArea &&
(contains_own_net || !((ConductionArea)curr_item).get_is_obstacle()))
{
continue;
}
if (curr_item.is_shove_fixed() && !contains_own_net)
{
this.found_obstacle = curr_item;
return false;
}
if (curr_item instanceof Via)
{
if (p_is_pad_check || !contains_own_net)
{
shove_via_list.add((Via) curr_item);
}
}
else if (curr_item instanceof PolylineTrace)
{
PolylineTrace curr_trace = (PolylineTrace) curr_item;
if (!store_trace(curr_trace))
{
return false;
}
}
else
{
if (contains_own_net)
{
if (!p_copper_sharing_allowed)
{
this.found_obstacle = curr_item;
return false;
}
if (p_is_pad_check && !((curr_item instanceof Pin) && ((Pin)curr_item).drill_allowed()))
{
this.found_obstacle = curr_item;
return false;
}
}
else
{
this.found_obstacle = curr_item;
return false;
}
}
}
search_from_side();
resort();
if (!calculate_stack_levels())
{
return false;
}
return true;
}
/**
* calculates the next substitute trace piece.
* Returns null at he end of the substitute trace list.
*/
PolylineTrace next_substitute_trace_piece()
{
EntryPoint[] entries = pop_piece();
if (entries == null)
{
return null;
}
PolylineTrace curr_trace = entries[0].trace;
TileShape offset_shape;
ShapeSearchTree search_tree = this.board.search_tree_manager.get_default_tree();
if (search_tree.is_clearance_compensation_used())
{
double curr_offset = curr_trace.get_compensated_half_width(search_tree) + c_offset_add;
offset_shape = (TileShape)shape.offset(curr_offset);
}
else
{
// enlarge the shape in 2 steps for symmetry reasons
offset_shape = (TileShape)shape.offset(curr_trace.get_half_width());
double cl_offset = board.clearance_value(curr_trace.clearance_class_no(), cl_class, layer) + c_offset_add;
offset_shape = (TileShape) offset_shape.offset(cl_offset);
}
int edge_count = shape.border_line_count();
int edge_diff = entries[1].edge_no - entries[0].edge_no;
// calculate the polyline of the substitute trace
Line [] piece_lines = new Line[edge_diff + 3];
// start with the intersecting line of the trace at the start entry.
piece_lines[0] = entries[0].trace.polyline().arr[entries[0].trace_line_no];
// end with the intersecting line of the trace at the end entry
piece_lines[piece_lines.length - 1] =
entries[1].trace.polyline().arr[entries[1].trace_line_no];
// fill the interiour lines of piece_lines with the appropriate edge
// lines of the offset shape
int curr_edge_no = entries[0].edge_no % edge_count;
for (int i = 1; i < piece_lines.length - 1; ++i)
{
piece_lines [i] = offset_shape.border_line(curr_edge_no);
if (curr_edge_no == edge_count - 1)
{
curr_edge_no = 0;
}
else
{
++curr_edge_no;
}
}
Polyline piece_polyline = new Polyline(piece_lines);
if (piece_polyline.is_empty())
{
// no valid trace piece, return the next one
return next_substitute_trace_piece();
}
return new PolylineTrace(piece_polyline, this.layer,
curr_trace.get_half_width(), curr_trace.net_no_arr,
curr_trace.clearance_class_no(), 0, 0, FixedState.UNFIXED, this.board);
}
/**
* returns the maximum recursion depth for shoving the obstacle traces
*/
int stack_depth()
{
return max_stack_level;
}
/**
* returns the number of substitute trace pieces.
*/
int substitute_trace_count()
{
return trace_piece_count;
}
/**
* Looks if an unconnected endpoint of a trace of a foreign net
* is contained in the interiour of the shape.
*/
public boolean trace_tails_in_shape()
{
return this.shape_contains_trace_tails;
}
/**
* Cuts out all traces in p_item_list out of the stored shape.
* Traces with net number p_except_net_no are ignored
*/
void cutout_traces(Collection<Item> p_item_list)
{
Iterator<Item> it = p_item_list.iterator();
while (it.hasNext())
{
Item curr_item = it.next();
if (curr_item instanceof PolylineTrace && !curr_item.shares_net_no(this.own_net_nos))
{
cutout_trace((PolylineTrace) curr_item, this.shape, this.cl_class);
}
}
}
/**
* Returns the item responsible for the failing, if the shove algorithm failed.
*/
Item get_found_obstacle()
{
return this.found_obstacle;
}
public static void cutout_trace(PolylineTrace p_trace, ConvexShape p_shape, int p_cl_class)
{
if (!p_trace.is_on_the_board())
{
System.out.println("ShapeTraceEntries.cutout_trace : trace is deleted");
return;
}
ConvexShape offset_shape;
BasicBoard board = p_trace.board;
ShapeSearchTree search_tree = board.search_tree_manager.get_default_tree();
if (search_tree.is_clearance_compensation_used())
{
double curr_offset = p_trace.get_compensated_half_width(search_tree) + c_offset_add;
offset_shape = p_shape.offset(curr_offset);
}
else
{
// enlarge the shape in 2 steps for symmetry reasons
double cl_offset = board.clearance_value(p_trace.clearance_class_no(),
p_cl_class, p_trace.get_layer()) + c_offset_add;
offset_shape = p_shape.offset(p_trace.get_half_width());
offset_shape = offset_shape.offset(cl_offset);
}
Polyline trace_lines = p_trace.polyline();
Polyline [] pieces = offset_shape.cutout(trace_lines);
if (pieces.length == 1 && pieces[0] == trace_lines)
{
// nothing cut off
return;
}
if (pieces.length == 2 && offset_shape.is_outside(pieces[0].first_corner())
&& offset_shape.is_outside(pieces[1].last_corner()))
{
fast_cutout_trace(p_trace, pieces[0], pieces[1]);
}
else
{
board.remove_item(p_trace);
for (int i = 0; i < pieces.length; ++i)
{
board.insert_trace_without_cleaning(pieces[i], p_trace.get_layer(),
p_trace.get_half_width(), p_trace.net_no_arr, p_trace.clearance_class_no(), FixedState.UNFIXED);
}
}
}
/** Optimized function handling the performance critical standard cutout case */
private static void fast_cutout_trace(PolylineTrace p_trace, Polyline p_start_piece, Polyline p_end_piece)
{
BasicBoard board = p_trace.board;
board.additional_update_after_change(p_trace);
board.item_list.save_for_undo(p_trace);
PolylineTrace start_piece = new PolylineTrace(p_start_piece, p_trace.get_layer(), p_trace.get_half_width(),
p_trace.net_no_arr, p_trace.clearance_class_no(), 0, 0, FixedState.UNFIXED, board);
start_piece.board = board;
board.item_list.insert(start_piece);
start_piece.set_on_the_board(true);
PolylineTrace end_piece = new PolylineTrace(p_end_piece, p_trace.get_layer(), p_trace.get_half_width(),
p_trace.net_no_arr, p_trace.clearance_class_no(), 0, 0, FixedState.UNFIXED, board);
end_piece.board = board;
board.item_list.insert(end_piece);
end_piece.set_on_the_board(true);
board.search_tree_manager.reuse_entries_after_cutout(p_trace, start_piece, end_piece);
board.remove_item(p_trace);
board.communication.observers.notify_new(start_piece);
board.communication.observers.notify_new(end_piece);
}
/** Stores all intersection points of p_trace
* with the border of the internal shape enlarged by the
* half width and the clearance of the corresponding trace pen.
*/
private boolean store_trace( PolylineTrace p_trace)
{
ShapeSearchTree search_tree = this.board.search_tree_manager.get_default_tree();
TileShape offset_shape;
if (search_tree.is_clearance_compensation_used())
{
double curr_offset = p_trace.get_compensated_half_width(search_tree) + c_offset_add;
offset_shape = (TileShape)shape.offset(curr_offset);
}
else
{
// enlarge the shape in 2 steps for symmetry reasons
double cl_offset = board.clearance_value(p_trace.clearance_class_no(),
this.cl_class, p_trace.get_layer()) + c_offset_add;
offset_shape = (TileShape)shape.offset(p_trace.get_half_width());
offset_shape = (TileShape) offset_shape.offset(cl_offset);
}
// using enlarge here instead offset causes problems because of a
// comparison in the constructor of class EntryPoint
int [][] entries = offset_shape.entrance_points(p_trace.polyline());
for (int i = 0; i < entries.length; ++i)
{
int [] entry_tuple = entries[i];
FloatPoint entry_approx =
p_trace.polyline().arr[entry_tuple[0]].
intersection_approx(offset_shape.border_line(entry_tuple[1]));
insert_entry_point(p_trace, entry_tuple[0],entry_tuple[1], entry_approx);
}
// Look, if an end point of the trace lies in the interiour of
// the shape. This may be the case, if a via touches the shape
if (!p_trace.shares_net_no(own_net_nos))
{
if (!(p_trace.nets_normal()))
{
return false;
}
Point end_corner = p_trace.first_corner();
Collection<Item> contact_list;
for (int i = 0; i < 2; ++i)
{
if (offset_shape.contains(end_corner))
{
if (i == 0)
{
contact_list = p_trace.get_start_contacts();
}
else
{
contact_list = p_trace.get_end_contacts();
}
int contact_count = 0;
boolean store_end_corner = true;
// check for contact object, which is not shovable
Iterator<Item> it = contact_list.iterator();
while (it.hasNext())
{
Item contact_item = it.next();
if (!contact_item.is_route())
{
this.found_obstacle = contact_item;
return false;
}
if (contact_item instanceof Trace)
{
if (contact_item.is_shove_fixed() || ((Trace)contact_item).get_half_width() != p_trace.get_half_width() ||
contact_item.clearance_class_no() != p_trace.clearance_class_no())
{
if (offset_shape.contains_inside(end_corner))
{
this.found_obstacle = contact_item;
return false;
}
}
}
else if (contact_item instanceof Via)
{
TileShape via_shape = ((Via) contact_item).get_tile_shape_on_layer(layer);
double via_trace_diff = via_shape.smallest_radius() - p_trace.get_compensated_half_width(search_tree);
if (!search_tree.is_clearance_compensation_used())
{
int via_clearance = board.clearance_value(contact_item.clearance_class_no(), this.cl_class, this.layer);
int trace_clearance = board.clearance_value(p_trace.clearance_class_no(), this.cl_class, this.layer);
if (trace_clearance > via_clearance)
{
via_trace_diff += via_clearance - trace_clearance;
}
}
if (via_trace_diff < 0)
{
// the via is smaller than the trace
this.found_obstacle = contact_item;
return false;
}
if (via_trace_diff == 0 && !offset_shape.contains_inside(end_corner))
{
// the via need not to be shoved
store_end_corner = false;
}
}
++contact_count;
}
if (contact_count == 1 && store_end_corner)
{
Point projection = offset_shape.nearest_border_point(end_corner);
{
int projection_side = offset_shape.contains_on_border_line_no(projection);
int trace_line_segment_no;
// the following may not be correct because the trace may not conntain a suitable
// line for the construction oof the end line of the substitute trace.
if (i == 0)
{
trace_line_segment_no = 0;
}
else
{
trace_line_segment_no = p_trace.polyline().arr.length - 1;
}
if (projection_side >= 0)
{
insert_entry_point(p_trace, trace_line_segment_no, projection_side, projection.to_float());
}
}
}
else if (contact_count == 0 && offset_shape.contains_inside(end_corner))
{
shape_contains_trace_tails = true;
}
}
end_corner = p_trace.last_corner();
}
}
this.found_obstacle = p_trace;
return true;
}
private void search_from_side()
{
if (this.from_side != null && this.from_side.no >= 0)
{
return; // from side is already legal
}
EntryPoint curr_node = this.list_anchor;
int curr_fromside_no = 0;
FloatPoint curr_entry_approx = null;
while (curr_node != null)
{
if (curr_node.trace.shares_net_no(this.own_net_nos))
{
curr_fromside_no = curr_node.edge_no;
curr_entry_approx = curr_node.entry_approx;
break;
}
curr_node = curr_node.next;
}
this.from_side = new CalcFromSide(curr_fromside_no, curr_entry_approx);
}
/**
* resorts the intersection points according to from_side_no and
* removes redundant points
*/
private void resort()
{
int edge_count = this.shape.border_line_count();
if (this.from_side.no < 0 || from_side.no >= edge_count)
{
System.out.println("ShapeTraceEntries.resort: from side not calculated");
return;
}
// resort the intersection points, so that they start in the
// middle of from_side.
FloatPoint compare_corner_1 = shape.corner_approx(this.from_side.no);
FloatPoint compare_corner_2;
if (from_side.no == edge_count - 1)
{
compare_corner_2 = shape.corner_approx(0);
}
else
{
compare_corner_2 = shape.corner_approx(from_side.no + 1);
}
double from_point_dist = 0;
FloatPoint from_point_projection = null;
if (from_side.border_intersection != null)
{
from_point_projection = from_side.border_intersection.projection_approx(shape.border_line(from_side.no));
from_point_dist = from_point_projection.distance_square(compare_corner_1);
if (from_point_dist >=
compare_corner_1.distance_square(compare_corner_2))
{
from_side = new CalcFromSide(from_side.no, null);
}
}
// search the first intersection point between the side middle
// and compare_corner_2
EntryPoint curr = list_anchor;
EntryPoint prev = null;
while (curr != null)
{
if (curr.edge_no > this.from_side.no)
{
break;
}
if (curr.edge_no == from_side.no)
{
if (from_side.border_intersection != null)
{
FloatPoint curr_projection = curr.entry_approx.projection_approx(shape.border_line(from_side.no));
if ( curr_projection.distance_square(compare_corner_1) >= from_point_dist
&& curr_projection.distance_square(from_point_projection) <=
curr_projection.distance_square(compare_corner_1))
{
break;
}
}
else
{
if (curr.entry_approx.distance_square(compare_corner_2)
<= curr.entry_approx.distance_square(compare_corner_1))
{
break;
}
}
}
prev = curr;
curr = prev.next;
}
if (curr != null && curr != list_anchor)
{
EntryPoint new_anchor = curr;
while (curr != null)
{
prev = curr;
curr = prev.next;
}
prev.next = list_anchor;
curr = list_anchor;
while (curr != new_anchor)
{
// add edge_count to curr.side to differentiate points
// before and after the middle of from_side
curr.edge_no += edge_count;
prev = curr;
curr = prev.next;
}
prev.next = null;
list_anchor = new_anchor;
}
// remove intersections between two other intersections of the same
// connected set, so that only first and last intersection is kept.
if (list_anchor == null)
{
return;
}
prev = list_anchor;
int[] prev_net_nos = prev.trace.net_no_arr;
curr = list_anchor.next;
int[] curr_net_nos;
EntryPoint next;
if (curr != null)
{
curr_net_nos = curr.trace.net_no_arr;
next = curr.next;
}
else
{
next = null;
curr_net_nos = new int[0];
}
EntryPoint before_prev = null;
while (next != null)
{
int[] next_net_nos = next.trace.net_no_arr;
if (net_nos_equal(prev_net_nos, curr_net_nos) && net_nos_equal(curr_net_nos, next_net_nos))
{
prev.next = next;
}
else
{
before_prev = prev;
prev = curr;
prev_net_nos = curr_net_nos;
}
curr_net_nos = next_net_nos;
curr = next;
next = curr.next;
}
// remove nodes of own net at start and end of the list
if (curr != null && net_nos_equal(curr_net_nos, own_net_nos))
{
prev.next = null;
if (net_nos_equal(prev_net_nos, own_net_nos))
{
if (before_prev != null)
{
before_prev.next = null;
}
else
{
list_anchor = null;
}
}
}
if (list_anchor != null && list_anchor.trace.nets_equal(own_net_nos))
{
list_anchor = list_anchor.next;
if (list_anchor != null && list_anchor.trace.nets_equal(own_net_nos))
{
list_anchor = list_anchor.next;
}
}
}
private boolean calculate_stack_levels()
{
if (list_anchor == null)
{
return true;
}
EntryPoint curr_entry = list_anchor;
int[] curr_net_nos = curr_entry.trace.net_no_arr;
int curr_level;
if (net_nos_equal(curr_net_nos, this.own_net_nos))
{
// ignore own net when calculating the stack level
curr_level = 0;
}
else
{
curr_level = 1;
}
while (curr_entry != null)
{
if (curr_entry.stack_level < 0) // not yet calculated
{
++trace_piece_count;
curr_entry.stack_level = curr_level;
if (curr_level > max_stack_level)
{
if (max_stack_level > 1)
{
this.found_obstacle = curr_entry.trace;
}
max_stack_level = curr_level;
}
}
// set stack level for all entries of the current net;
EntryPoint check_entry = curr_entry.next;
int index_of_next_foreign_set = 0;
int index_of_last_occurance_of_set = 0;
int next_index = 0;
EntryPoint last_own_entry = null;
EntryPoint first_foreign_entry = null;
while (check_entry != null)
{
++next_index;
int[] check_net_nos = check_entry.trace.net_no_arr;
if (net_nos_equal(check_net_nos, curr_net_nos))
{
index_of_last_occurance_of_set = next_index;
last_own_entry = check_entry;
check_entry.stack_level = curr_entry.stack_level;
}
else if (index_of_next_foreign_set == 0)
{
// first occurance of a foreign connected set
index_of_next_foreign_set = next_index;
first_foreign_entry = check_entry;
}
check_entry = check_entry.next;
}
EntryPoint next_entry = null;
if (next_index != 0)
{
if (index_of_next_foreign_set != 0
&& index_of_next_foreign_set < index_of_last_occurance_of_set)
// raise level
{
next_entry = first_foreign_entry;
if (next_entry.stack_level >= 0) // already calculated
{
// stack property failes
return false;
}
++curr_level;
}
else
{
if (index_of_last_occurance_of_set != 0)
{
next_entry = last_own_entry;
}
else
{
next_entry = first_foreign_entry;
if (next_entry.stack_level >= 0) // already calculated
{
--curr_level;
if (next_entry.stack_level != curr_level)
{
return false;
}
}
}
}
curr_net_nos = next_entry.trace.net_no_arr;
// remove all entries between curr_entry and next_entry, because
// they are irrelevant;
check_entry = curr_entry.next;
while (check_entry != next_entry)
{
check_entry = check_entry.next;
}
curr_entry.next = next_entry;
curr_entry = next_entry;
}
else
{
curr_entry = null;
}
}
if (curr_level != 1)
{
System.out.println(
"ShapeTraceEntries.calculate_stack_levels: curr_level inconsistent");
return false;
}
return true;
}
/**
* Pops the next piece with minimal level from the imtersection list
* Returns null, if the stack is empty.
* The returned array has 2 elements. The first is the first entry point,
* and the second is the last entry point of the minimal level.
*/
private EntryPoint [] pop_piece()
{
if (list_anchor == null)
{
if (this.trace_piece_count != 0)
{
System.out.println("ShapeTraceEntries: trace_piece_count is inconsistent");
}
return null;
}
EntryPoint first = list_anchor;
EntryPoint prev_first = null;
while (first != null)
{
if (first.stack_level == this.max_stack_level)
{
break;
}
prev_first = first;
first = first.next;
}
if (first == null)
{
System.out.println("ShapeTraceEntries: max_stack_level not found");
return null;
}
EntryPoint [] result = new EntryPoint [2];
result[0] = first;
EntryPoint last = first;
EntryPoint after_last = first.next;
while (after_last != null)
{
if (after_last.stack_level != max_stack_level ||
!after_last.trace.nets_equal( first.trace))
{
break;
}
last = after_last;
after_last = last.next;
}
result[1] = last;
// remove the nodes from first to last inclusive
if (prev_first != null)
{
prev_first.next = after_last;
}
else
{
list_anchor = after_last;
}
// recalculate max_stack_level;
max_stack_level = 0;
EntryPoint curr = list_anchor;
while (curr != null)
{
if (curr.stack_level > max_stack_level)
{
max_stack_level = curr.stack_level;
}
curr = curr.next;
}
--trace_piece_count;
if (first.trace.nets_equal(this.own_net_nos))
{
// own net is ignored and nay occur only at the lowest level
result = pop_piece();
}
return result;
}
private void insert_entry_point(PolylineTrace p_trace, int p_trace_line_no,
int p_edge_no, FloatPoint p_entry_approx)
{
EntryPoint new_entry = new EntryPoint(p_trace, p_trace_line_no, p_edge_no, p_entry_approx);
EntryPoint curr_prev = null;
EntryPoint curr_next = list_anchor;
// insert the new entry into the sorted list
while (curr_next != null)
{
if (curr_next.edge_no > new_entry.edge_no)
{
break;
}
if (curr_next.edge_no == new_entry.edge_no)
{
FloatPoint prev_corner = shape.corner_approx(p_edge_no);
FloatPoint next_corner;
if (p_edge_no == shape.border_line_count() - 1)
{
next_corner = shape.corner_approx(0);
}
else
{
next_corner = shape.corner_approx(new_entry.edge_no + 1);
}
if( prev_corner.scalar_product(p_entry_approx, next_corner)
<= prev_corner.scalar_product(curr_next.entry_approx, next_corner))
// the projection of the line from prev_corner to p_entry_approx
// onto the line from prev_corner to next_corner is smaller
// than the projection of the line from prev_corner to
// next.entry_approx onto the same line.
{
break;
}
}
curr_prev = curr_next;
curr_next = curr_next.next;
}
new_entry.next = curr_next;
if (curr_prev != null)
{
curr_prev.next = new_entry;
}
else
{
list_anchor = new_entry;
}
}
final Collection<Via> shove_via_list;
private final TileShape shape;
private final int layer;
private final int[] own_net_nos;
private final int cl_class;
private CalcFromSide from_side;
private final RoutingBoard board;
private EntryPoint list_anchor;
private int trace_piece_count;
private int max_stack_level;
private boolean shape_contains_trace_tails = false;
private Item found_obstacle = null;
private static final double c_offset_add = 1;
/**
* Information about an entry point of p_trace into the shape.
* The entry points are sorted around the border of the shape
*/
private static class EntryPoint
{
EntryPoint(PolylineTrace p_trace, int p_trace_line_no, int p_edge_no,
FloatPoint p_entry_approx)
{
trace = p_trace;
edge_no = p_edge_no;
trace_line_no = p_trace_line_no;
entry_approx = p_entry_approx;
stack_level = -1; //not yet calculated
}
final PolylineTrace trace;
final int trace_line_no;
int edge_no;
final FloatPoint entry_approx;
int stack_level;
EntryPoint next;
}
static private boolean net_nos_equal(int[] p_net_nos_1, int [] p_net_nos_2)
{
if (p_net_nos_1.length != p_net_nos_2.length)
{
return false;
}
for (int curr_net_no_1 : p_net_nos_1)
{
boolean net_no_found = false;
for (int curr_net_no_2 : p_net_nos_2)
{
if (curr_net_no_1 == curr_net_no_2)
{
net_no_found = true;
}
}
if (!net_no_found)
{
return false;
}
}
return true;
}
}