import math from app.models.schematic import Point, Bounds MAX_COORDINATE = 10_000_000 def number(p, key, default=0): v = float(p.get(key, default)) if not math.isfinite(v) or abs(v) > MAX_COORDINATE: raise ValueError(f'Invalid or excessive numeric property {key}') return v def integer(p, key, default=0): return int(number(p, key, default)) def flag(p, key): return p.get(key, '').upper() in ('T', 'TRUE', '1') def coordinate(p, key): # Stored base is 10 mil; fractional field is 1/10000 mil. return number(p, key) + number(p, key + '_FRAC') / 100000 def point(p, prefix='LOCATION'): return Point(x=coordinate(p, prefix+'.X'), y=-coordinate(p, prefix+'.Y')) def transform_point(p: Point, origin: Point, rotation: float, mirrored=False): """For local generated markers only. SchDoc child geometry is already placed.""" x = -p.x if mirrored else p.x a = math.radians(-rotation) return Point(x=origin.x+x*math.cos(a)-p.y*math.sin(a), y=origin.y+x*math.sin(a)+p.y*math.cos(a)) def bounds(points, padding=0): if not points: return Bounds(x=0,y=0,width=0,height=0) xs,ys = [p.x for p in points],[p.y for p in points] return Bounds(x=min(xs)-padding,y=min(ys)-padding, width=max(xs)-min(xs)+2*padding,height=max(ys)-min(ys)+2*padding) def union(boxes): return bounds([p for b in boxes for p in (Point(x=b.x,y=b.y),Point(x=b.x+b.width,y=b.y+b.height))]) def color(value): n = int(value) & 0xffffff return f'#{n&255:02x}{n>>8&255:02x}{n>>16&255:02x}' def poly_path(points, closed=False): return ' '.join(('M' if i==0 else 'L')+f'{p.x:.15g},{p.y:.15g}' for i,p in enumerate(points)) + (' Z' if closed else '')