shibosoftwaredev/f1c100s-linux-nema17-stepper-controller
A four-layer NEMA 17 motor-controller PCB combining an Allwinner F1C100S Linux computer, USB‑PD/buck power system, W5500 Ethernet, STM32 real-time motion control, DRV8825 stepper driver, protection/temperature/current telemetry, USB interfaces, and local LED/buzzer diagnostics.
- Version
- 0.4.56
- License
- unset
- Stars
- 0
scripts/route_geometry.py
"""Exact pad/drill geometry and logical connectivity shared by routing tools."""
import json, math, re, os
from pathlib import Path
from shapely.geometry import Point, LineString, Polygon, box
from shapely.affinity import translate, rotate
LAYERS = ["top", "inner1", "inner2", "bottom"]
def load_circuit(path="routing/placement.json"):
if path=="routing/placement.json":path=os.environ.get("CIRCUIT_INPUT",path)
return json.loads(Path(path).read_text())
def connectivity(circuit):
parent = {}
def root(x):
parent.setdefault(x, x)
if parent[x] != x:
parent[x] = root(parent[x])
return parent[x]
for e in circuit:
ids = ((e.get("connected_source_port_ids", []) + e.get("connected_source_net_ids", []))
if e["type"] == "source_trace" else e.get("source_port_ids", [])
if e["type"] == "source_component_internal_connection" else [])
for x in ids[1:]:
parent[root(x)] = root(ids[0])
return root
def geometry(e, prefix=""):
if e.get("points"):
return Polygon([(p["x"], p["y"]) for p in e["points"]]).buffer(0)
shape = e.get("shape", e.get("hole_shape", "circle"))
x, y = e["x"], e["y"]
if shape == "circle":
d = e.get(prefix + "diameter", e.get("radius", 0) * 2)
if not d:
d = e.get("hole_diameter", e.get("outer_diameter", 0))
g = Point(0, 0).buffer(d / 2, quad_segs=16)
else:
w = e[prefix + "width"]
h = e[prefix + "height"]
r = min(w, h) / 2 if shape in ["pill", "oval"] else e.get("corner_radius", 0)
if r and min(w, h) <= 2 * r + 1e-8:
g = LineString([(-w/2+r, -h/2+r), (w/2-r, h/2-r)]).buffer(r, quad_segs=12)
elif r:
g = box(-w/2+r, -h/2+r, w/2-r, h/2-r).buffer(r, quad_segs=12)
else:
g = box(-w/2, -h/2, w/2, h/2)
return translate(rotate(g, e.get("ccw_rotation", 0), origin=(0, 0)), x, y)
def parse_sexpr(text):
tokens = re.findall(r'"(?:[^"\\]|\\.)*"|[()]|[^\s()]+',
text.replace('(string_quote ")', '(string_quote DQUOTE)'))
stack = []
result = None
for token in tokens:
if token == "(":
obj = []
if stack:
stack[-1].append(obj)
stack.append(obj)
elif token == ")":
result = stack.pop()
else:
stack[-1].append(token.strip('"'))
return result
def entries(obj, key):
return [e for e in obj if isinstance(e, list) and e and e[0] == key]
def one(obj, key):
return next(iter(entries(obj, key)), None)