freerouting/src/main/java/eu/mihosoft/freerouting/autoroute/Connection.java

216 lines
7.6 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.
*
* Connection.java
*
* Created on 4. Juni 2006, 07:24
*
*/
package eu.mihosoft.freerouting.autoroute;
import java.util.Collection;
import java.util.Set;
import java.util.TreeSet;
import eu.mihosoft.freerouting.geometry.planar.Point;
import eu.mihosoft.freerouting.board.Item;
import eu.mihosoft.freerouting.board.Trace;
/**
* Describes a routing connection ending at the next fork or terminal item.
*
* @author Alfons Wirtz
*/
public class Connection
{
/**
* Gets the connection this item belongs to. A connection ends at the next fork or terminal item.
* Returns null, if p_item is not a route item, or if it is a via belonging to more than 1 connection.
*/
public static Connection get(Item p_item)
{
if (!p_item.is_route())
{
return null;
}
Connection precalculated_connection = p_item.get_autoroute_info().get_precalculated_connection();
if (precalculated_connection != null)
{
return precalculated_connection;
}
Set<Item> contacts = p_item.get_normal_contacts();
Set<Item> connection_items = new TreeSet<Item>();
connection_items.add(p_item);
Point start_point = null;
int start_layer = 0;
Point end_point = null;
int end_layer = 0;
for (Item curr_item : contacts)
{
Point prev_contact_point = p_item.normal_contact_point(curr_item);
if (prev_contact_point == null)
{
// no unique contact point
continue;
}
int prev_contact_layer = p_item.first_common_layer(curr_item);
boolean fork_found = false;
if (p_item instanceof Trace)
{
// Check, that there is only 1 contact at this location.
// Only for pins and vias items of more than 1 connection
// are collected
Trace start_trace = (Trace) p_item;
Collection<Item> check_contacts = start_trace.get_normal_contacts(prev_contact_point, false);
if (check_contacts.size() != 1)
{
fork_found = true;
}
}
// Search from curr_item along the contacts
// until the next fork or nonroute item.
for (;;)
{
if(!curr_item.is_route() || fork_found)
{
// connection ends
if (start_point == null)
{
start_point = prev_contact_point;
start_layer = prev_contact_layer;
}
else if (!prev_contact_point.equals(start_point))
{
end_point = prev_contact_point;
end_layer = prev_contact_layer;
}
break;
}
connection_items.add(curr_item);
Collection<Item> curr_item_contacts = curr_item.get_normal_contacts();
// filter the contacts at the previous contact point,
// because we were already there.
// If then there is not exactly 1 new contact left, there is
// a stub or a fork.
Point next_contact_point = null;
int next_contact_layer = -1;
Item next_contact = null;
for (Item tmp_contact : curr_item_contacts)
{
int tmp_contact_layer = curr_item.first_common_layer(tmp_contact);
if (tmp_contact_layer >= 0)
{
Point tmp_contact_point = curr_item.normal_contact_point(tmp_contact);
if (tmp_contact_point == null)
{
// no unique contact point
fork_found = true;
break;
}
if (prev_contact_layer != tmp_contact_layer ||
!prev_contact_point.equals(tmp_contact_point))
{
next_contact_point = tmp_contact_point;
next_contact_layer = tmp_contact_layer;
if (next_contact != null)
{
// second new contact found
fork_found = true;
break;
}
next_contact = tmp_contact;
}
}
}
if (next_contact == null)
{
break;
}
curr_item = next_contact;
prev_contact_point = next_contact_point;
prev_contact_layer = next_contact_layer;
}
}
Connection result = new Connection(start_point, start_layer, end_point, end_layer, connection_items);
for (Item curr_item : connection_items)
{
curr_item.get_autoroute_info().set_precalculated_connection(result);
}
return result;
}
/**
* Creates a new instance of Connection
*/
private Connection(Point p_start_point, int p_start_layer, Point p_end_point, int p_end_layer, Set<Item> p_item_list)
{
start_point = p_start_point;
start_layer = p_start_layer;
end_point = p_end_point;
end_layer = p_end_layer;
item_list = p_item_list;
}
/**
* Returns the cumulative length of the traces in this connection.
*/
public double trace_length()
{
double result = 0;
for (Item curr_item : item_list)
{
if (curr_item instanceof Trace)
{
result += ((Trace) curr_item).get_length();
}
}
return result;
}
/**
* Returns an estimation of the actual length of the connection divided by the minimal possible length.
*/
public double get_detour()
{
if (start_point == null || end_point == null)
{
return Integer.MAX_VALUE;
}
double min_trace_length = start_point.to_float().distance(end_point.to_float());
double detour = (this.trace_length() + DETOUR_ADD) / (min_trace_length + DETOUR_ADD) +
DETOUR_ITEM_COST * (item_list.size() - 1);
return detour;
}
/**
* If the connection ens in empty space, start_point or end_point may be null.
*/
public final Point start_point;
public final int start_layer;
public final Point end_point;
public final int end_layer;
public final Set<Item> item_list;
private static final double DETOUR_ADD = 100;
private static final double DETOUR_ITEM_COST = 0.1;
}