SolidCode / SolidPython

A python frontend for solid modelling that compiles to OpenSCAD
1.12k stars 174 forks source link

PCBs? #145

Closed marmalodak closed 4 years ago

marmalodak commented 4 years ago

I was just watching https://youtu.be/_-YDuCqEwuw?t=98. He uses a tool that reminds me of CAD to design a specific style of PCB.

What would SolidPython or OpenSCAD have to emit to allow me to do this with SolidPython?

etjones commented 4 years ago

Wow, that was a really great video to watch! Before I watched it, I was going to say "Nah, just use a dedicated PCB design program", but seeing the drudgery of drawing all those motor coils by hand made me realize why you're thinking about doing it programmatically. An OpenSCAD version would be much, much more customizable than his hand-twiddled coils.

I'm no PCB authority, but I think the direction you'd want to go is exporting rendered 2-D OpenSCAD files as DXFs and generating them using polygon(). You'd probably want to build up a circuit-drawing library of functions so you could set the trace width, etc.

There's some capacity for this kind of 2D design in SolidPython, but not as much as you'd like. OpenSCAD's offset() is very powerful, but doesn't return you any intermediate points to alter once you've passed it a polygon, so you stop having much ability to further process points. For that reason, I wrote solid.utils.offset_points() years ago, that takes a set of points and returns a list of points offset from the originals by some distance, on one side of the originals or the other. However, I just discovered a bug there that keeps this from being as smooth as you'd like it. I'll attach the code below and see if I can fix this correctly.

image

And the code that generated it:

#! /usr/bin/env python
from pathlib import Path

from solid import *
from solid.utils import offset_points, euclidify
from euclid3 import Point2

SEGMENTS = 48

def trace_polygon():
    points = [Point2(*p) for p in [(0,0), (5,0), (10,5), (15,5)]]

    # A bug in solid.utils.offset_points() makes this shape look bad
    inside_points = offset_points(points, offset=1, internal=True)
    outside_points = list(reversed(offset_points(points, offset=1, internal=False)))
    all_points =  inside_points + outside_points
    poly1 = polygon(all_points)

    # here's what the offsets *should* do
    manual_points = [
        [0, 1], [4.5857864376, 1.0], [9.5857864376, 6.0], [15,6],
        [15,4], [10.4142135624, 4.0], [5.4142135624, -1.0], [0, -1], 
    ]
    poly2 = translate([0,10])(polygon(manual_points))

    a = poly1 + poly2
    return a

if __name__ == '__main__':
    a = trace_polygon()
    out_path = scad_render_to_file(a, file_header='$fn = %s;' % SEGMENTS, include_orig_code=True)
    print(f'Wrote SCAD file to {out_path}')
etjones commented 4 years ago

BTW, I just pushed SolidPython v0.4.9 to PyPI, with the offset_points() bug fixed, and I added solid.utils.path_2d() and solid.utils.path_2d_polygon() for doing things like PCB traces. Here's a spiral path inspired by that video's PCB motors:

#! /usr/bin/env python
import math

from solid import *
from solid.utils import *

SEGMENTS = 48

def spiral_path():
    path_width = 1
    gap_width = 1
    points = []

    revolutions = 4
    segments_per_rev = SEGMENTS
    angle_step = 2*math.pi/segments_per_rev
    for i in range(1 + revolutions * segments_per_rev):
        angle = (i + segments_per_rev)*angle_step 
        r = (path_width + gap_width)*angle/(2*math.pi)
        x = r * math.cos(angle)
        y = r * math.sin(angle)
        points.append((x,y))

    path_points = path_2d(points, width=path_width, closed=False)
    a = polygon(path_points)

    # Or, if you don't need the generated points at all, get the polygon
    # made for you.
    poly = path_2d_polygon(points, width=path_width, closed=False)
    a += forward(20)(poly)

    return a

if __name__ == '__main__':
    a = spiral_path()
    out_path = scad_render_to_file(a, file_header='$fn = %s;' % SEGMENTS, include_orig_code=True)
    print(f'Wrote file to {out_path}')