1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
// Copyright (c) 2020-2021 Thomas Kramer.
// SPDX-FileCopyrightText: 2022 Thomas Kramer <code@tkramer.ch>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

//! Air wire router for visualizing component connectivity. Does not create a legal routing.

use crate::route::simple_router::{SimpleRoutedNet, SimpleRouter};
use libreda_db::layout::types::SInt;
use libreda_db::prelude::{Path, Point, Rect, SimpleRPolygon, TryBoundingBox};
use std::collections::HashMap;

/// Router that creates air wires between routing terminals in a star-topology.
/// This does not produce a legal routing and has
/// the only purpose to visualize the connectivity of components.
pub struct AirWireRouter {
    /// Layer for the air wires.
    air_wire_layer: u8,
    /// Wire thickness.
    wire_thickness: SInt,
    /// Draw a full mesh instead of a star network.
    full_mesh: bool,
}

impl AirWireRouter {
    /// Create a default router.
    pub fn new(air_wire_layer: u8, wire_thickness: SInt, full_mesh: bool) -> Self {
        Self {
            air_wire_layer,
            wire_thickness,
            full_mesh,
        }
    }
}

impl SimpleRouter for AirWireRouter {
    fn name(&self) -> &str {
        "AirWireRouter"
    }

    fn compute_routes_impl(
        &self,
        _boundary: Rect<SInt>,
        net_terminals: &HashMap<usize, Vec<Vec<(SimpleRPolygon<SInt>, u8)>>>,
        _obstacles: &Vec<(SimpleRPolygon<SInt>, u8)>,
    ) -> HashMap<usize, SimpleRoutedNet> {
        let mut result = HashMap::new();

        for (net, pins) in net_terminals {
            let mut net_wires = Vec::new();
            // Of each pin take the first terminal shape.
            let terminals: Vec<_> = pins.iter().filter_map(|t| t.first()).collect();

            // Compute the center of the terminals.
            let center = {
                let mut num_elements = 0;
                let sum: Point<_> = terminals
                    .iter()
                    .filter_map(|(t, _)| t.try_bounding_box())
                    .map(|b| b.center())
                    .inspect(|_| num_elements += 1)
                    .sum();
                sum / num_elements
            };

            // Get some representative points of the terminals.
            let terminal_points: Vec<_> = terminals
                .iter()
                // Get a point close to the center of all terminal shapes.
                .filter_map(|(t, _layer)| t.points().min_by_key(|&p| (p - center).norm1()))
                .collect();

            if self.full_mesh {
                for (i, a) in terminal_points.iter().enumerate() {
                    for b in &terminal_points[i..] {
                        let ray = Path::new(vec![*a, *b], self.wire_thickness);
                        net_wires.push((self.air_wire_layer, ray.into()))
                    }
                }
            } else {
                // Draw star-network.
                // Get the center of weight to create a star topology.
                let sum: Point<_> = terminal_points.iter().copied().sum();
                let center = sum / terminal_points.len() as SInt;

                // Draw star network.
                for p in terminal_points {
                    let ray = Path::new(vec![p, center], self.wire_thickness);
                    net_wires.push((self.air_wire_layer, ray.into()))
                }
            }

            let routed_net = SimpleRoutedNet {
                routes: net_wires,
                vias: Default::default(),
            };

            result.insert(*net, routed_net);
        }

        result
    }
}