topola 0.1.0

Work-in-progress free and open-source topological (rubberband) router and autorouter for printed circuit boards (PCBs)
Documentation
// SPDX-FileCopyrightText: 2026 Topola contributors
//
// SPDX-License-Identifier: MIT OR Apache-2.0

use derive_more::{Constructor, From};
use serde::{Deserialize, Serialize};

use crate::{
    layout::{
        Layout,
        compounds::{ComponentId, NetId},
        primitives::{JointId, PolyId, PrimitiveId, SegId, ViaId},
    },
    vector::Vector2,
};

#[derive(
    Clone,
    Constructor,
    Copy,
    Debug,
    Default,
    Deserialize,
    Eq,
    From,
    Ord,
    PartialEq,
    PartialOrd,
    Serialize,
)]
pub struct PinId(usize);

impl PinId {
    #[inline]
    pub fn index(self) -> usize {
        self.0
    }
}

#[derive(Clone, Copy, Debug)]
pub struct PinSpec {
    pub component: Option<ComponentId>,
    pub net: Option<NetId>,
}

#[derive(Clone, Debug)]
pub struct Pin {
    pub spec: PinSpec,
    pub joints: Vec<JointId>,
    pub segs: Vec<SegId>,
    pub vias: Vec<ViaId>,
    pub polys: Vec<PolyId>,
}

impl Pin {
    pub fn new(spec: PinSpec) -> Self {
        Pin {
            spec,
            joints: Vec::new(),
            segs: Vec::new(),
            vias: Vec::new(),
            polys: Vec::new(),
        }
    }

    pub fn primitives(&self) -> impl Iterator<Item = PrimitiveId> + '_ {
        self.joints
            .iter()
            .map(|&joint_id| PrimitiveId::Joint(joint_id))
            .chain(self.segs.iter().map(|&seg_id| PrimitiveId::Seg(seg_id)))
            .chain(self.vias.iter().map(|&via_id| PrimitiveId::Via(via_id)))
            .chain(self.polys.iter().map(|&poly_id| PrimitiveId::Poly(poly_id)))
    }
}

impl Layout {
    pub fn pin_centroid(&self, pin_id: PinId) -> Vector2<i64> {
        crate::profile_function!();
        let pin = self.pin(pin_id);
        let mut sum = Vector2::new(0, 0);
        let mut count = 0;

        for &joint_id in &pin.joints {
            sum = sum + self.joint(joint_id).center();
            count += 1;
        }
        for &seg_id in &pin.segs {
            sum = sum + self.seg(seg_id).center();
            count += 1;
        }
        for &via_id in &pin.vias {
            sum = sum + self.via(via_id).position;
            count += 1;
        }
        for &poly_id in &pin.polys {
            sum = sum + self.poly(poly_id).centroid;
            count += 1;
        }

        if count == 0 {
            return Vector2::new(0, 0);
        }

        let count = count as i64;
        Vector2::new(sum.x / count, sum.y / count)
    }
}