mirror of https://codeberg.org/topola/topola.git
Initial commit
Very basic routing routines and data model. No pathfinding implemented yet.
This commit is contained in:
commit
cf6460a3db
|
|
@ -0,0 +1,16 @@
|
|||
# ---> Rust
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
debug/
|
||||
target/
|
||||
|
||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||
Cargo.lock
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
|
||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||
*.pdb
|
||||
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
[package]
|
||||
name = "topola"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies.geo]
|
||||
version = "0.25.1"
|
||||
|
||||
[dependencies.rstar]
|
||||
version = "0.11.0"
|
||||
|
||||
[dependencies.petgraph]
|
||||
version = "0.6.3"
|
||||
|
||||
[dependencies.sdl2]
|
||||
version = "0.35.2"
|
||||
default-features = false
|
||||
features = ["gfx"]
|
||||
|
||||
[dependencies.enum-as-inner]
|
||||
version = "0.6.0"
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) <year> <copyright holders>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
use std::cell::{RefCell, Ref};
|
||||
use std::rc::Rc;
|
||||
use geo::geometry::Point;
|
||||
|
||||
use crate::math::Circle;
|
||||
use crate::mesh::{Mesh, Index, IndexRTreeWrapper, DotIndex, SegIndex, BendIndex};
|
||||
use crate::rules::{Rules, Conditions};
|
||||
use crate::primitive::Primitive;
|
||||
use crate::weight::{Weight, DotWeight, SegWeight, BendWeight};
|
||||
use crate::math;
|
||||
|
||||
|
||||
pub struct Layout {
|
||||
mesh: Mesh,
|
||||
rules: Rules,
|
||||
}
|
||||
|
||||
impl Default for Layout {
|
||||
fn default() -> Self {
|
||||
return Layout::new();
|
||||
}
|
||||
}
|
||||
|
||||
impl Layout {
|
||||
pub fn new() -> Self {
|
||||
Layout {
|
||||
mesh: Mesh::new(),
|
||||
rules: Rules::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn route_around(&mut self, from: DotIndex, around: DotIndex, cw: bool, width: f64) -> DotIndex {
|
||||
let maybe_bend = self.mesh.bend(from);
|
||||
|
||||
let conditions = Conditions {
|
||||
lower_net: None,
|
||||
higher_net: None,
|
||||
layer: None,
|
||||
zone: None,
|
||||
};
|
||||
|
||||
let from_circle = match maybe_bend {
|
||||
Some(bend) => {
|
||||
let from_around = self.mesh.weight(Index::Bend(bend)).as_bend().unwrap().around;
|
||||
let circle = self.mesh.weight(Index::Dot(from_around)).as_dot().unwrap().circle;
|
||||
self.guidecircle(circle, width + 5.0, conditions)
|
||||
}
|
||||
None => Circle {
|
||||
pos: self.mesh.weight(Index::Dot(from)).as_dot().unwrap().circle.pos,
|
||||
r: 0.0,
|
||||
},
|
||||
};
|
||||
let around_circle = self.mesh.weight(Index::Dot(around)).as_dot().unwrap().circle;
|
||||
|
||||
let to_circle = self.guidecircle(around_circle, width + 5.0, conditions);
|
||||
let tg_pt_pairs = math::tangent_points(from_circle, to_circle);
|
||||
|
||||
for tg_pt_pair in tg_pt_pairs {
|
||||
if let Some(bend) = maybe_bend {
|
||||
let start_cross = math::cross_product(tg_pt_pair.0, tg_pt_pair.1, from_circle.pos);
|
||||
|
||||
if (self.mesh.weight(Index::Bend(bend)).as_bend().unwrap().cw && start_cross <= 0.0)
|
||||
|| (!self.mesh.weight(Index::Bend(bend)).as_bend().unwrap().cw && start_cross >= 0.0) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let stop_cross = math::cross_product(tg_pt_pair.0, tg_pt_pair.1, to_circle.pos);
|
||||
|
||||
if (cw && stop_cross <= 0.0) || (!cw && stop_cross >= 0.0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if maybe_bend.is_some() {
|
||||
self.stretch_dangling_bend(from, tg_pt_pair.0);
|
||||
}
|
||||
|
||||
return self.route_seg_bend(from, around, tg_pt_pair.1, cw, width);
|
||||
}
|
||||
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
fn guidecircle(&self, circle: Circle, width: f64, conditions: Conditions) -> Circle {
|
||||
Circle {
|
||||
pos: circle.pos,
|
||||
r: circle.r + width + self.rules.ruleset(conditions).clearance.min,
|
||||
}
|
||||
}
|
||||
|
||||
fn stretch_dangling_bend(&mut self, dot: DotIndex, to: Point) {
|
||||
let bend = self.mesh.bend(dot).unwrap();
|
||||
let dot_weight = *self.mesh.weight(Index::Dot(dot)).as_dot().unwrap();
|
||||
let bend_weight = *self.mesh.weight(Index::Bend(bend)).as_bend().unwrap();
|
||||
|
||||
let fixed_dot: Index = self.mesh.dot_neighbors(Index::Bend(bend))
|
||||
.into_iter()
|
||||
.filter(|neighbor| *neighbor != Index::Dot(dot))
|
||||
.collect::<Vec<Index>>()[0];
|
||||
|
||||
self.mesh.remove_bend(bend);
|
||||
self.mesh.remove_dot(dot);
|
||||
|
||||
let new_dot = self.mesh.add_dot(DotWeight {
|
||||
net: dot_weight.net,
|
||||
circle: Circle {
|
||||
pos: to,
|
||||
r: dot_weight.circle.r,
|
||||
},
|
||||
});
|
||||
self.mesh.add_bend(*fixed_dot.as_dot().unwrap(), new_dot, bend_weight);
|
||||
}
|
||||
|
||||
fn route_seg_bend(&mut self, from: DotIndex, around: DotIndex, to: Point, cw: bool, width: f64) -> DotIndex {
|
||||
let bend_from = self.route_seg(from, to, width);
|
||||
let bend_to = self.add_dot(*self.mesh.primitive(Index::Dot(bend_from)).weight.as_dot().unwrap());
|
||||
let from_primitive = self.mesh.primitive(Index::Dot(from));
|
||||
let net = from_primitive.weight.as_dot().unwrap().net;
|
||||
|
||||
let bend = self.mesh.add_bend(bend_from, bend_to, BendWeight {net, around, cw});
|
||||
bend_to
|
||||
}
|
||||
|
||||
pub fn route_seg(&mut self, from: DotIndex, to: Point, width: f64) -> DotIndex {
|
||||
let from_primitive = self.mesh.primitive(Index::Dot(from));
|
||||
let net = from_primitive.weight.as_dot().unwrap().net;
|
||||
|
||||
assert!(width <= from_primitive.weight.as_dot().unwrap().circle.r * 2.0);
|
||||
|
||||
let to_index = self.mesh.add_dot(DotWeight {
|
||||
net,
|
||||
circle: Circle {pos: to, r: width / 2.0},
|
||||
});
|
||||
self.mesh.add_seg(from, to_index, SegWeight {net, width});
|
||||
to_index
|
||||
}
|
||||
|
||||
pub fn add_dot(&mut self, weight: DotWeight) -> DotIndex {
|
||||
self.mesh.add_dot(weight)
|
||||
}
|
||||
|
||||
pub fn remove_dot(&mut self, index: DotIndex) {
|
||||
self.mesh.remove_dot(index);
|
||||
}
|
||||
|
||||
pub fn add_seg(&mut self, from: DotIndex, to: DotIndex, width: f64) -> SegIndex {
|
||||
let from_primitive = self.mesh.primitive(Index::Dot(from));
|
||||
let net = from_primitive.weight.as_dot().unwrap().net;
|
||||
self.mesh.add_seg(from, to, SegWeight {net, width})
|
||||
}
|
||||
|
||||
pub fn primitives(&self) -> Box<dyn Iterator<Item=Primitive> + '_> {
|
||||
self.mesh.primitives()
|
||||
}
|
||||
|
||||
pub fn primitive(&self, index: Index) -> Primitive {
|
||||
return self.mesh.primitive(index);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
extern crate sdl2;
|
||||
|
||||
mod layout;
|
||||
mod rules;
|
||||
mod mesh;
|
||||
mod primitive;
|
||||
mod weight;
|
||||
mod math;
|
||||
|
||||
use std::mem::swap;
|
||||
use std::time::Duration;
|
||||
use geo::geometry::Point;
|
||||
use mesh::Index;
|
||||
use sdl2::pixels::Color;
|
||||
use sdl2::event::Event;
|
||||
use sdl2::keyboard::Keycode;
|
||||
use sdl2::gfx::primitives::DrawRenderer;
|
||||
|
||||
use crate::layout::Layout;
|
||||
use crate::primitive::Primitive;
|
||||
use crate::weight::{Weight, DotWeight};
|
||||
use crate::math::Circle;
|
||||
|
||||
fn main() {
|
||||
let sdl_context = sdl2::init().unwrap();
|
||||
let video_subsystem = sdl_context.video().unwrap();
|
||||
|
||||
let window = video_subsystem.window("rust-sdl2 demo", 800, 600)
|
||||
.position_centered()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let mut canvas = window.into_canvas().build().unwrap();
|
||||
|
||||
canvas.set_draw_color(Color::RGB(0, 0, 0));
|
||||
canvas.clear();
|
||||
canvas.present();
|
||||
|
||||
let mut event_pump = sdl_context.event_pump().unwrap();
|
||||
let mut i = 0;
|
||||
let mut layout = Layout::new();
|
||||
|
||||
let index = layout.add_dot(DotWeight {net: 0, circle: Circle {pos: (150.5, 80.5).into(), r: 8.0}});
|
||||
//layout.route_seg(index, Point {x: 400.5, y: 350.5}, 6.0);
|
||||
|
||||
let index2 = layout.add_dot(DotWeight {net: 0, circle: Circle {pos: (180.5, 150.5).into(), r: 8.0}});
|
||||
let barrier1 = layout.add_dot(DotWeight {net: 0, circle: Circle {pos: (90.5, 150.5).into(), r: 8.0}});
|
||||
layout.add_seg(index2, barrier1, 16.0);
|
||||
|
||||
let index3 = layout.add_dot(DotWeight {net: 0, circle: Circle {pos: (130.5, 250.5).into(), r: 8.0}});
|
||||
let barrier2 = layout.add_dot(DotWeight {net: 0, circle: Circle {pos: (190.5, 250.5).into(), r: 8.0}});
|
||||
layout.add_seg(index3, barrier2, 16.0);
|
||||
|
||||
let index4 = layout.route_around(index, index2, true, 5.0);
|
||||
let index5 = layout.route_around(index4, index3, false, 5.0);
|
||||
|
||||
let index6 = layout.add_dot(DotWeight {net: 0, circle: Circle {pos: (140.5, 300.5).into(), r: 8.0}});
|
||||
let index7 = layout.route_around(index5, index6, false, 5.0);
|
||||
|
||||
'running: loop {
|
||||
i = (i + 1) % 255;
|
||||
|
||||
canvas.set_draw_color(Color::RGB(0, 10, 35));
|
||||
canvas.clear();
|
||||
|
||||
for event in event_pump.poll_iter() {
|
||||
match event {
|
||||
Event::Quit {..} |
|
||||
Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
|
||||
break 'running
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
for primitive in layout.primitives() {
|
||||
match primitive.weight {
|
||||
Weight::Dot(dot) => {
|
||||
let _ = canvas.filled_circle(dot.circle.pos.x() as i16,
|
||||
dot.circle.pos.y() as i16,
|
||||
dot.circle.r as i16,
|
||||
Color::RGB(200, 52, 52));
|
||||
},
|
||||
Weight::Seg(seg) => {
|
||||
let dot_neighbor_weights = primitive.dot_neighbor_weights;
|
||||
let _ = canvas.thick_line(dot_neighbor_weights[0].circle.pos.x() as i16,
|
||||
dot_neighbor_weights[0].circle.pos.y() as i16,
|
||||
dot_neighbor_weights[1].circle.pos.x() as i16,
|
||||
dot_neighbor_weights[1].circle.pos.y() as i16,
|
||||
seg.width as u8,
|
||||
Color::RGB(200, 52, 52));
|
||||
},
|
||||
Weight::Bend(bend) => {
|
||||
let dot_neighbor_weights = primitive.dot_neighbor_weights;
|
||||
let around_circle = primitive.around_weight.unwrap().circle;
|
||||
|
||||
let delta1 = dot_neighbor_weights[0].circle.pos - around_circle.pos;
|
||||
let delta2 = dot_neighbor_weights[1].circle.pos - around_circle.pos;
|
||||
|
||||
let mut angle1 = delta1.y().atan2(delta1.x());
|
||||
let mut angle2 = delta2.y().atan2(delta2.x());
|
||||
|
||||
if !primitive.weight.as_bend().unwrap().cw {
|
||||
swap(&mut angle1, &mut angle2);
|
||||
}
|
||||
|
||||
for d in -3..3 {
|
||||
let _ = canvas.arc(
|
||||
around_circle.pos.x() as i16,
|
||||
around_circle.pos.y() as i16,
|
||||
(primitive.around_weight.unwrap().circle.r + 10.0 + (d as f64)) as i16,
|
||||
angle1.to_degrees() as i16,
|
||||
angle2.to_degrees() as i16,
|
||||
Color::RGB(200, 52, 52));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
canvas.present();
|
||||
::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 60));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
use std::ops::Sub;
|
||||
use geo::geometry::Point;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub struct Line {
|
||||
pub a: f64,
|
||||
pub b: f64,
|
||||
pub c: f64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub struct Circle {
|
||||
pub pos: Point,
|
||||
pub r: f64,
|
||||
}
|
||||
|
||||
impl Sub for Circle {
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, other: Self) -> Self {
|
||||
//return Self{pos: Point{x: self.pos.x() - other.pos.x(), y: self.pos.y() - other.pos.y()}, r: self.r};
|
||||
return Self {pos: self.pos - other.pos, r: self.r};
|
||||
}
|
||||
}
|
||||
|
||||
fn tangent(center: Point, r1: f64, r2: f64) -> Line {
|
||||
let epsilon = 1e-9;
|
||||
let dr = r2 - r1;
|
||||
let norm = center.x() * center.x() + center.y() * center.y();
|
||||
let discriminant = norm - dr * dr;
|
||||
|
||||
if discriminant < -epsilon {
|
||||
panic!();
|
||||
}
|
||||
|
||||
let sqrt_discriminant = f64::sqrt(f64::abs(discriminant));
|
||||
|
||||
Line {
|
||||
a: (center.x() * dr + center.y() * sqrt_discriminant) / norm,
|
||||
b: (center.y() * dr - center.x() * sqrt_discriminant) / norm,
|
||||
c: r1,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tangents(circle1: Circle, circle2: Circle) -> [Line; 4] {
|
||||
let mut tgs: [Line; 4] = [
|
||||
tangent((circle2 - circle1).pos, -circle1.r, -circle2.r),
|
||||
tangent((circle2 - circle1).pos, -circle1.r, circle2.r),
|
||||
tangent((circle2 - circle1).pos, circle1.r, -circle2.r),
|
||||
tangent((circle2 - circle1).pos, circle1.r, circle2.r),
|
||||
];
|
||||
|
||||
for tg in tgs.iter_mut() {
|
||||
tg.c -= tg.a * circle1.pos.x() + tg.b * circle1.pos.y();
|
||||
}
|
||||
|
||||
return tgs;
|
||||
}
|
||||
|
||||
fn cast_point_to_line(pt: Point, line: Line) -> Point {
|
||||
return (
|
||||
(line.b * (line.b * pt.x() - line.a * pt.y()) - line.a * line.c) / (line.a * line.a + line.b * line.b),
|
||||
(line.a * (-line.b * pt.x() + line.a * pt.y()) - line.b * line.c) / (line.a * line.a + line.b * line.b),
|
||||
).into();
|
||||
}
|
||||
|
||||
pub fn tangent_points(circle1: Circle, circle2: Circle) -> [(Point, Point); 4] {
|
||||
let tgs = tangents(circle1, circle2);
|
||||
|
||||
[
|
||||
(cast_point_to_line(circle1.pos, tgs[0]), cast_point_to_line(circle2.pos, tgs[0])),
|
||||
(cast_point_to_line(circle1.pos, tgs[1]), cast_point_to_line(circle2.pos, tgs[1])),
|
||||
(cast_point_to_line(circle1.pos, tgs[2]), cast_point_to_line(circle2.pos, tgs[2])),
|
||||
(cast_point_to_line(circle1.pos, tgs[3]), cast_point_to_line(circle2.pos, tgs[3])),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn cross_product(start: Point, stop: Point, reference: Point) -> f64 {
|
||||
let dx1 = stop.x() - start.x();
|
||||
let dy1 = stop.y() - start.y();
|
||||
let dx2 = reference.x() - stop.x();
|
||||
let dy2 = reference.y() - stop.y();
|
||||
dx1 * dy2 - dy1 * dx2
|
||||
}
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
use enum_as_inner::EnumAsInner;
|
||||
use petgraph::stable_graph::{StableUnGraph, NodeIndex, EdgeIndex};
|
||||
use petgraph::visit::EdgeRef;
|
||||
use rstar::{RTree, RTreeObject, AABB};
|
||||
use rstar::primitives::GeomWithData;
|
||||
|
||||
use crate::primitive::Primitive;
|
||||
use crate::weight::{Weight, DotWeight, SegWeight, BendWeight};
|
||||
|
||||
pub type DotIndex = NodeIndex<u32>;
|
||||
pub type SegIndex = EdgeIndex<u32>;
|
||||
pub type BendIndex = EdgeIndex<u32>;
|
||||
|
||||
#[derive(EnumAsInner, Copy, Clone, PartialEq)]
|
||||
pub enum Index {
|
||||
Dot(DotIndex),
|
||||
Seg(SegIndex),
|
||||
Bend(BendIndex),
|
||||
}
|
||||
|
||||
pub type IndexRTreeWrapper = GeomWithData<Primitive, Index>;
|
||||
|
||||
pub struct Mesh {
|
||||
pub rtree: RTree<IndexRTreeWrapper>,
|
||||
pub graph: StableUnGraph<Weight, Weight, u32>,
|
||||
}
|
||||
|
||||
impl Default for Mesh {
|
||||
fn default() -> Self {
|
||||
return Mesh::new();
|
||||
}
|
||||
}
|
||||
|
||||
impl Mesh {
|
||||
pub fn new() -> Self {
|
||||
return Mesh {
|
||||
rtree: RTree::new(),
|
||||
graph: StableUnGraph::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_dot(&mut self, weight: DotWeight) -> DotIndex {
|
||||
let dot_index = self.graph.add_node(Weight::Dot(weight));
|
||||
let index = Index::Dot(dot_index);
|
||||
self.rtree.insert(IndexRTreeWrapper::new(self.primitive(index), index));
|
||||
dot_index
|
||||
}
|
||||
|
||||
pub fn remove_dot(&mut self, dot: DotIndex) {
|
||||
self.rtree.remove(&IndexRTreeWrapper::new(self.primitive(Index::Dot(dot)), Index::Dot(dot)));
|
||||
self.graph.remove_node(dot);
|
||||
}
|
||||
|
||||
pub fn add_seg(&mut self, from: DotIndex, to: DotIndex, weight: SegWeight) -> SegIndex {
|
||||
let seg_index = self.graph.add_edge(from, to, Weight::Seg(weight));
|
||||
let index = Index::Seg(seg_index);
|
||||
self.rtree.insert(IndexRTreeWrapper::new(self.primitive(index), index));
|
||||
seg_index
|
||||
}
|
||||
|
||||
pub fn remove_seg(&mut self, seg: SegIndex) {
|
||||
self.rtree.remove(&IndexRTreeWrapper::new(self.primitive(Index::Seg(seg)), Index::Seg(seg)));
|
||||
self.graph.remove_edge(seg);
|
||||
}
|
||||
|
||||
pub fn add_bend(&mut self, from: DotIndex, to: DotIndex, weight: BendWeight) -> BendIndex {
|
||||
let bend_index = self.graph.add_edge(from, to, Weight::Bend(weight));
|
||||
let index = Index::Bend(bend_index);
|
||||
self.rtree.insert(IndexRTreeWrapper::new(self.primitive(index), index));
|
||||
bend_index
|
||||
}
|
||||
|
||||
pub fn remove_bend(&mut self, bend: BendIndex) {
|
||||
self.rtree.remove(&IndexRTreeWrapper::new(self.primitive(Index::Bend(bend)), Index::Bend(bend)));
|
||||
self.graph.remove_edge(bend);
|
||||
}
|
||||
|
||||
pub fn primitives(&self) -> Box<dyn Iterator<Item=Primitive> + '_> {
|
||||
Box::new(self.rtree.iter().map(|wrapper| self.primitive(wrapper.data)))
|
||||
}
|
||||
|
||||
pub fn primitive(&self, index: Index) -> Primitive {
|
||||
Primitive {
|
||||
weight: self.weight(index),
|
||||
dot_neighbor_weights:
|
||||
self.dot_neighbors(index)
|
||||
.into_iter()
|
||||
.map(|index| *self.weight(index).as_dot().unwrap())
|
||||
.collect(),
|
||||
around_weight: match index {
|
||||
Index::Bend(bend_index) => Some(*self.weight(Index::Dot((*self.weight(index).as_bend().unwrap()).around)).as_dot().unwrap()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dot_neighbors(&self, index: Index) -> Vec<Index> {
|
||||
match index {
|
||||
Index::Dot(node_index) =>
|
||||
return self.graph.neighbors(node_index).map(|ni| Index::Dot(ni)).collect(),
|
||||
Index::Seg(edge_index) | Index::Bend(edge_index) => {
|
||||
let endpoints = self.graph.edge_endpoints(edge_index).unwrap();
|
||||
return vec![Index::Dot(endpoints.0), Index::Dot(endpoints.1)]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bend(&self, index: NodeIndex) -> Option<BendIndex> {
|
||||
let edges: Vec<EdgeIndex<u32>> = self.graph.edges(index).map(|r| r.id()).collect();
|
||||
|
||||
if edges.len() != 1 {
|
||||
return None;
|
||||
}
|
||||
|
||||
return Some(edges[0]);
|
||||
}
|
||||
|
||||
pub fn weight(&self, index: Index) -> Weight {
|
||||
return match index {
|
||||
Index::Dot(node_index) =>
|
||||
*self.graph.node_weight(node_index).unwrap(),
|
||||
Index::Seg(edge_index) | Index::Bend(edge_index) =>
|
||||
*self.graph.edge_weight(edge_index).unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
use rstar::{RTreeObject, AABB};
|
||||
|
||||
use crate::weight::{Weight, DotWeight};
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub struct Primitive {
|
||||
pub weight: Weight,
|
||||
pub dot_neighbor_weights: Vec<DotWeight>,
|
||||
pub around_weight: Option<DotWeight>,
|
||||
}
|
||||
|
||||
impl Primitive {
|
||||
pub fn envelope(&self) -> AABB<[f64; 2]> {
|
||||
match self.weight {
|
||||
Weight::Dot(dot) => {
|
||||
return AABB::from_corners(
|
||||
[dot.circle.pos.x() - dot.circle.r, dot.circle.pos.y() - dot.circle.r],
|
||||
[dot.circle.pos.x() + dot.circle.r, dot.circle.pos.y() + dot.circle.r]
|
||||
);
|
||||
},
|
||||
Weight::Seg(..) | Weight::Bend(..) => {
|
||||
// TODO: Take widths into account.
|
||||
|
||||
let points: Vec<[f64; 2]> = self.dot_neighbor_weights.iter()
|
||||
.map(|neighbor| [neighbor.circle.pos.x(), neighbor.circle.pos.y()])
|
||||
|
||||
.collect();
|
||||
return AABB::<[f64; 2]>::from_points(&points);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/*pub fn weight(&self) -> Weight {
|
||||
return self.weight;
|
||||
}*/
|
||||
}
|
||||
|
||||
impl RTreeObject for Primitive {
|
||||
type Envelope = AABB<[f64; 2]>;
|
||||
fn envelope(&self) -> Self::Envelope {
|
||||
return self.envelope();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
|
||||
pub struct Conditions {
|
||||
pub lower_net: Option<i32>,
|
||||
pub higher_net: Option<i32>,
|
||||
pub layer: Option<i32>,
|
||||
pub zone: Option<i32>,
|
||||
}
|
||||
|
||||
impl Conditions {
|
||||
pub fn priority(&self) -> i32 {
|
||||
let mut priority = 0;
|
||||
priority += (self.lower_net.is_some() as i32) * 1;
|
||||
priority += (self.higher_net.is_some() as i32) * 2;
|
||||
priority += (self.layer.is_some() as i32) * 4;
|
||||
priority += (self.zone.is_some() as i32) * 8;
|
||||
priority
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Rules {
|
||||
rulesets: [Option<HashMap<Conditions, Ruleset>>; 16],
|
||||
}
|
||||
|
||||
impl Rules {
|
||||
pub fn new() -> Self {
|
||||
let mut me = Self {
|
||||
rulesets: Default::default(),
|
||||
};
|
||||
me.rulesets[0] = Some(HashMap::from([(Conditions {
|
||||
lower_net: None,
|
||||
higher_net: None,
|
||||
layer: None,
|
||||
zone: None,
|
||||
}, Ruleset::new())]));
|
||||
me
|
||||
}
|
||||
|
||||
pub fn ruleset(&self, conditions: Conditions) -> &Ruleset {
|
||||
let priority = conditions.priority();
|
||||
|
||||
for index in (1..(priority + 1)).rev() {
|
||||
if let Some(ruleset_hashmap) = &self.rulesets[index as usize] {
|
||||
if let Some(ruleset) = ruleset_hashmap.get(&conditions) {
|
||||
return ruleset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&self.rulesets[0].as_ref().unwrap()[&conditions]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Ruleset {
|
||||
pub length: Rule,
|
||||
pub clearance: Rule,
|
||||
}
|
||||
|
||||
impl Ruleset {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
length: Rule::new(),
|
||||
clearance: Rule::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Rule {
|
||||
pub min: f64,
|
||||
pub opt: Option<f64>,
|
||||
pub max: f64,
|
||||
}
|
||||
|
||||
impl Rule {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
min: 0.0,
|
||||
opt: None,
|
||||
max: f64::INFINITY,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
use enum_as_inner::EnumAsInner;
|
||||
use crate::{math::Circle, mesh::DotIndex};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub struct DotWeight {
|
||||
pub net: i32,
|
||||
pub circle: Circle,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub struct BendWeight {
|
||||
pub net: i32,
|
||||
pub around: DotIndex,
|
||||
pub cw: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub struct SegWeight {
|
||||
pub net: i32,
|
||||
pub width: f64,
|
||||
}
|
||||
|
||||
#[derive(EnumAsInner, Clone, Copy, PartialEq)]
|
||||
pub enum Weight {
|
||||
Dot(DotWeight),
|
||||
Seg(SegWeight),
|
||||
Bend(BendWeight),
|
||||
}
|
||||
Loading…
Reference in New Issue