Geometry

The kipy.geometry module provides geometric types for working with positions and shapes.

from kipy.geometry import Vector2, Box2

Vector2

Represents a 2D point or vector.

Creating Vectors

from kipy.geometry import Vector2

# From millimeters (most common)
pos = Vector2.from_xy_mm(50, 25)

# From mils (1/1000 inch)
pos = Vector2.from_xy_mils(2000, 1000)

# From internal units (nanometers)
pos = Vector2(x=50000000, y=25000000)

# Zero vector
zero = Vector2.zero()

Accessing Values

pos = Vector2.from_xy_mm(50, 25)

# In internal units (nanometers)
print(pos.x, pos.y)  # 50000000, 25000000

# In millimeters
print(pos.x_mm, pos.y_mm)  # 50.0, 25.0

# In mils
print(pos.x_mils, pos.y_mils)  # 1968.5, 984.25

Operations

a = Vector2.from_xy_mm(10, 20)
b = Vector2.from_xy_mm(5, 10)

# Addition
c = a + b  # (15, 30)mm

# Subtraction
d = a - b  # (5, 10)mm

# Scalar multiplication
e = a * 2  # (20, 40)mm

# Negation
f = -a  # (-10, -20)mm

Distance and Magnitude

a = Vector2.from_xy_mm(0, 0)
b = Vector2.from_xy_mm(3, 4)

# Distance between points
dist = a.distance_to(b)
print(f"{dist / 1000000}mm")  # 5.0mm

# Magnitude (length)
length = b.magnitude()

Comparison

a = Vector2.from_xy_mm(10, 20)
b = Vector2.from_xy_mm(10, 20)
c = Vector2.from_xy_mm(10, 21)

print(a == b)  # True
print(a == c)  # False

Box2

Represents an axis-aligned bounding box.

Creating Boxes

from kipy.geometry import Box2

# From position and size
box = Box2.from_pos_size(
    pos=Vector2.from_xy_mm(10, 10),
    size=Vector2.from_xy_mm(50, 30)
)

# From corners
box = Box2.from_corners(
    min_corner=Vector2.from_xy_mm(10, 10),
    max_corner=Vector2.from_xy_mm(60, 40)
)

# From center and size
box = Box2.from_center_size(
    center=Vector2.from_xy_mm(35, 25),
    size=Vector2.from_xy_mm(50, 30)
)

Box Properties

box = Box2.from_pos_size(
    pos=Vector2.from_xy_mm(10, 10),
    size=Vector2.from_xy_mm(50, 30)
)

# Position (top-left)
print(box.position.x_mm, box.position.y_mm)  # 10, 10

# Size
print(box.size.x_mm, box.size.y_mm)  # 50, 30

# Width and height
print(box.width, box.height)  # in internal units

# Center
print(box.center.x_mm, box.center.y_mm)  # 35, 25

Box Operations

# Check if point is inside
point = Vector2.from_xy_mm(30, 20)
inside = box.contains(point)  # True

# Check intersection
box2 = Box2.from_pos_size(
    pos=Vector2.from_xy_mm(40, 20),
    size=Vector2.from_xy_mm(30, 20)
)
intersects = box.intersects(box2)  # True

# Expand box
expanded = box.expand(5000000)  # Expand by 5mm

# Union of boxes
union = box.union(box2)

Example: Calculate Bounding Box

from kipy import KiCad
from kipy.geometry import Vector2, Box2

kicad = KiCad()
board = kicad.get_board()

# Get all footprint positions
footprints = board.footprints.get_all()

if footprints:
    # Start with first footprint position
    min_x = max_x = footprints[0].position.x
    min_y = max_y = footprints[0].position.y

    # Find bounds
    for fp in footprints:
        min_x = min(min_x, fp.position.x)
        max_x = max(max_x, fp.position.x)
        min_y = min(min_y, fp.position.y)
        max_y = max(max_y, fp.position.y)

    bbox = Box2.from_corners(
        Vector2(min_x, min_y),
        Vector2(max_x, max_y)
    )

    print(f"Footprint bounds:")
    print(f"  Min: ({bbox.position.x_mm}, {bbox.position.y_mm})mm")
    print(f"  Size: ({bbox.size.x_mm}, {bbox.size.y_mm})mm")

Example: Grid Positions

from kipy.geometry import Vector2

def grid_positions(start, cols, rows, spacing_mm):
    """Generate a grid of positions."""
    positions = []
    for row in range(rows):
        for col in range(cols):
            pos = Vector2.from_xy_mm(
                start.x_mm + col * spacing_mm,
                start.y_mm + row * spacing_mm
            )
            positions.append(pos)
    return positions

# Generate 3x4 grid starting at (10, 10) with 10mm spacing
start = Vector2.from_xy_mm(10, 10)
grid = grid_positions(start, cols=3, rows=4, spacing_mm=10)

for i, pos in enumerate(grid):
    print(f"Position {i}: ({pos.x_mm}, {pos.y_mm})mm")

Example: Polar Coordinates

import math
from kipy.geometry import Vector2

def polar_position(center, radius_mm, angle_degrees):
    """Calculate position from polar coordinates."""
    angle_rad = math.radians(angle_degrees)
    x = center.x_mm + radius_mm * math.cos(angle_rad)
    y = center.y_mm + radius_mm * math.sin(angle_rad)
    return Vector2.from_xy_mm(x, y)

# Create circle of positions
center = Vector2.from_xy_mm(100, 100)
radius = 20

for angle in range(0, 360, 30):
    pos = polar_position(center, radius, angle)
    print(f"{angle}°: ({pos.x_mm:.1f}, {pos.y_mm:.1f})mm")

Unit Conversion Reference

UnitMultiplier to IU
1 nm1
1 µm1,000
1 mm1,000,000
1 mil25,400
1 inch25,400,000

Internal units (IU) are nanometers in the PCB editor.