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

345 lines
14 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.
*
* ShoveViaAlgo.java
*
* Created on 12. Dezember 2005, 06:48
*
*/
package eu.mihosoft.freerouting.board;
import java.util.Collection;
import eu.mihosoft.freerouting.datastructures.TimeLimit;
import eu.mihosoft.freerouting.geometry.planar.TileShape;
import eu.mihosoft.freerouting.geometry.planar.ConvexShape;
import eu.mihosoft.freerouting.geometry.planar.IntOctagon;
import eu.mihosoft.freerouting.geometry.planar.IntBox;
import eu.mihosoft.freerouting.geometry.planar.IntPoint;
import eu.mihosoft.freerouting.geometry.planar.Point;
import eu.mihosoft.freerouting.geometry.planar.Vector;
import eu.mihosoft.freerouting.geometry.planar.FloatPoint;
/**
*
* Contains internal auxiliary functions of class RoutingBoard
* for shoving vias and pins
*
* @author Alfons Wirtz
*/
public class MoveDrillItemAlgo
{
/**
* checks, if p_drill_item can be translated by p_vector by shoving obstacle
* traces and vias aside, so that no clearance violations occur.
*/
public static boolean check(DrillItem p_drill_item, Vector p_vector, int p_max_recursion_depth,
int p_max_via_recursion_depth, Collection<Item> p_ignore_items,
RoutingBoard p_board, TimeLimit p_time_limit)
{
if (p_time_limit != null && p_time_limit.limit_exceeded())
{
return false;
}
if (p_drill_item.is_shove_fixed())
{
return false;
}
// Check, that p_drillitem is only connected to traces.
Collection<Item> contact_list = p_drill_item.get_normal_contacts();
for (Item curr_contact : contact_list)
{
if (!(curr_contact instanceof Trace || curr_contact instanceof ConductionArea))
{
return false;
}
}
Collection<Item> ignore_items;
if (p_ignore_items == null)
{
ignore_items = new java.util.LinkedList<Item>();
}
else
{
ignore_items = p_ignore_items;
}
ignore_items.add(p_drill_item);
ForcedPadAlgo forced_pad_algo = new ForcedPadAlgo(p_board);
boolean attach_allowed = false;
if (p_drill_item instanceof Via)
{
attach_allowed = ((Via)p_drill_item).attach_allowed;
}
ShapeSearchTree search_tree = p_board.search_tree_manager.get_default_tree();
for (int curr_layer = p_drill_item.first_layer(); curr_layer <= p_drill_item.last_layer(); ++curr_layer)
{
int curr_ind = curr_layer - p_drill_item.first_layer();
TileShape curr_shape = p_drill_item.get_tree_shape(search_tree, curr_ind);
if (curr_shape == null)
{
continue;
}
ConvexShape new_shape = (ConvexShape) curr_shape.translate_by(p_vector);
TileShape curr_tile_shape;
if (p_board.rules.get_trace_angle_restriction() == AngleRestriction.NINETY_DEGREE)
{
curr_tile_shape = new_shape.bounding_box();
}
else
{
curr_tile_shape = new_shape.bounding_octagon();
}
CalcFromSide from_side = new CalcFromSide(p_drill_item.get_center(), curr_tile_shape);
if (forced_pad_algo.check_forced_pad(curr_tile_shape, from_side, curr_layer,
p_drill_item.net_no_arr, p_drill_item.clearance_class_no(), attach_allowed,
ignore_items, p_max_recursion_depth, p_max_via_recursion_depth, true, p_time_limit)
== ForcedPadAlgo.CheckDrillResult.NOT_DRILLABLE)
{
return false;
}
}
return true;
}
/**
* Translates p_drill_item by p_vector by shoving obstacle
* traces and vias aside, so that no clearance violations occur.
* If p_tidy_region != null, it will be joined by the bounding octagons of the translated shapes.
*/
static boolean insert(DrillItem p_drill_item, Vector p_vector,
int p_max_recursion_depth, int p_max_via_recursion_depth, IntOctagon p_tidy_region,
RoutingBoard p_board)
{
if (p_drill_item.is_shove_fixed())
{
return false;
}
boolean attach_allowed = false;
if (p_drill_item instanceof Via)
{
attach_allowed = ((Via)p_drill_item).attach_allowed;
}
ForcedPadAlgo forced_pad_algo = new ForcedPadAlgo(p_board);
Collection<Item> ignore_items = new java.util.LinkedList<Item>();
ignore_items.add(p_drill_item);
ShapeSearchTree search_tree = p_board.search_tree_manager.get_default_tree();
for (int curr_layer = p_drill_item.first_layer(); curr_layer <= p_drill_item.last_layer(); ++curr_layer)
{
int curr_ind = curr_layer - p_drill_item.first_layer();
TileShape curr_shape = p_drill_item.get_tree_shape(search_tree, curr_ind);
if (curr_shape == null)
{
continue;
}
ConvexShape new_shape = (ConvexShape) curr_shape.translate_by(p_vector);
TileShape curr_tile_shape;
if (p_board.rules.get_trace_angle_restriction() == AngleRestriction.NINETY_DEGREE)
{
curr_tile_shape = new_shape.bounding_box();
}
else
{
curr_tile_shape = new_shape.bounding_octagon();
}
if (p_tidy_region != null)
{
p_tidy_region = p_tidy_region.union(curr_tile_shape.bounding_octagon());
}
CalcFromSide from_side = new CalcFromSide(p_drill_item.get_center(), curr_tile_shape);
if (!forced_pad_algo.forced_pad(curr_tile_shape, from_side, curr_layer, p_drill_item.net_no_arr,
p_drill_item.clearance_class_no(), attach_allowed,
ignore_items, p_max_recursion_depth, p_max_via_recursion_depth))
{
return false;
}
IntBox curr_bounding_box = curr_shape.bounding_box();
for (int j = 0; j < 4; ++j)
{
p_board.join_changed_area( curr_bounding_box.corner_approx(j), curr_layer);
}
}
p_drill_item.move_by(p_vector);
return true;
}
/**
* Shoves vias out of p_obstacle_shape. Returns false, if the database is damaged, so that an undo is necessary afterwards.
*/
static boolean shove_vias(TileShape p_obstacle_shape, CalcFromSide p_from_side, int p_layer, int[] p_net_no_arr,
int p_cl_type, Collection<Item> p_ignore_items, int p_max_recursion_depth,
int p_max_via_recursion_depth, boolean p_copper_sharing_allowed,
RoutingBoard p_board)
{
ShapeSearchTree search_tree = p_board.search_tree_manager.get_default_tree();
ShapeTraceEntries shape_entries =
new ShapeTraceEntries(p_obstacle_shape, p_layer, p_net_no_arr, p_cl_type, p_from_side, p_board);
Collection<Item> obstacles =
search_tree.overlapping_items_with_clearance(p_obstacle_shape, p_layer, new int[0], p_cl_type);
if (!shape_entries.store_items(obstacles, false, p_copper_sharing_allowed))
{
return true;
}
if (p_ignore_items != null)
{
shape_entries.shove_via_list.removeAll(p_ignore_items);
}
if (shape_entries.shove_via_list.isEmpty())
{
return true;
}
double shape_radius = 0.5 * p_obstacle_shape.bounding_box().min_width();
for (Via curr_via : shape_entries.shove_via_list)
{
if (curr_via.shares_net_no(p_net_no_arr))
{
continue;
}
if (p_max_via_recursion_depth <= 0)
{
return true;
}
IntPoint [] try_via_centers =
try_shove_via_points(p_obstacle_shape, p_layer, curr_via, p_cl_type, true, p_board);
IntPoint new_via_center = null;
double max_dist = 0.5 * curr_via.get_shape_on_layer(p_layer).bounding_box().max_width() + shape_radius;
double max_dist_square = max_dist * max_dist;
IntPoint curr_via_center = (IntPoint) curr_via.get_center();
FloatPoint check_via_center = curr_via_center.to_float();
Vector rel_coor = null;
for (int i = 0; i < try_via_centers.length; ++i)
{
if (i == 0 || check_via_center.distance_square(try_via_centers[i].to_float()) <= max_dist_square)
{
Collection<Item> ignore_items = new java.util.LinkedList<Item>();
if (p_ignore_items != null)
{
ignore_items.addAll(p_ignore_items);
}
rel_coor = try_via_centers[i].difference_by(curr_via_center);
// No time limit here because the item database is already changed.
boolean shove_ok = check(curr_via, rel_coor, p_max_recursion_depth,
p_max_via_recursion_depth - 1, ignore_items, p_board, null);
if (shove_ok)
{
new_via_center = try_via_centers[i];
break;
}
}
}
if (new_via_center == null)
{
continue;
}
if (!insert(curr_via, rel_coor, p_max_recursion_depth, p_max_via_recursion_depth - 1, null, p_board))
{
return false;
}
}
return true;
}
/**
* Calculates possible new location for a via to shove outside p_obstacle_shape.
* if p_extended_check is true, more than 1 possible new locations are calculated.
* The function isused here and in ShoveTraceAlgo.check.
*/
static IntPoint[] try_shove_via_points(TileShape p_obstacle_shape, int p_layer, Via p_via, int p_cl_class_no,
boolean p_extended_check, RoutingBoard p_board)
{
ShapeSearchTree search_tree = p_board.search_tree_manager.get_default_tree();
TileShape curr_via_shape = p_via.get_tree_shape_on_layer(search_tree, p_layer);
if (curr_via_shape == null)
{
return new IntPoint [0];
}
boolean is_int_octagon = p_obstacle_shape.is_IntOctagon();
double clearance_value = p_board.clearance_value(p_cl_class_no, p_via.clearance_class_no(), p_layer);
double shove_distance;
if (p_board.rules.get_trace_angle_restriction() == AngleRestriction.NINETY_DEGREE ||is_int_octagon )
{
shove_distance = 0.5 * curr_via_shape.bounding_box().max_width();
if (!search_tree.is_clearance_compensation_used())
{
shove_distance += clearance_value;
}
}
else
{
// a different algorithm is used for calculating the new via centers
shove_distance = 0;
if (!search_tree.is_clearance_compensation_used())
{
// enlarge p_obstacle_shape and curr_via_shape by half of the clearance value to syncronize
// with the check algorithm in ShapeSearchTree.overlapping_tree_entries_with_clearance
shove_distance += 0.5 * clearance_value;
}
}
// The additional constant 2 is an empirical value for the tolerance in case of diagonal shoving.
shove_distance += 2;
IntPoint curr_via_center = (IntPoint) p_via.get_center();
IntPoint [] try_via_centers;
int try_count = 1;
if (p_board.rules.get_trace_angle_restriction() == AngleRestriction.NINETY_DEGREE)
{
IntBox curr_offset_box = p_obstacle_shape.bounding_box().offset(shove_distance);
if (p_extended_check)
{
try_count = 2;
}
try_via_centers = curr_offset_box.nearest_border_projections(curr_via_center, try_count);
}
else if (is_int_octagon)
{
IntOctagon curr_offset_octagon = p_obstacle_shape.bounding_octagon().enlarge(shove_distance);
if (p_extended_check)
{
try_count = 4;
}
try_via_centers = curr_offset_octagon.nearest_border_projections(curr_via_center, try_count);
}
else
{
TileShape curr_offset_shape = (TileShape) p_obstacle_shape.enlarge(shove_distance);
if (!search_tree.is_clearance_compensation_used())
{
curr_via_shape = (TileShape) curr_via_shape.enlarge(0.5 * clearance_value);
}
if (p_extended_check)
{
try_count = 4;
}
FloatPoint[] shove_deltas = curr_offset_shape.nearest_relative_outside_locations(curr_via_shape, try_count);
try_via_centers = new IntPoint[shove_deltas.length];
for (int i = 0; i < try_via_centers.length; ++i)
{
Vector curr_delta = shove_deltas[i].round().difference_by(Point.ZERO);
try_via_centers[i] = (IntPoint) curr_via_center.translate_by(curr_delta);
}
}
return try_via_centers;
}
}