diff --git a/doc/_static/images/examples_perfboard.png b/doc/_static/images/examples_perfboard.png new file mode 100644 index 0000000..e7fa484 Binary files /dev/null and b/doc/_static/images/examples_perfboard.png differ diff --git a/doc/advanced_usage.rst b/doc/advanced_usage.rst index 236c023..96ac69e 100644 --- a/doc/advanced_usage.rst +++ b/doc/advanced_usage.rst @@ -25,8 +25,9 @@ For example, create a rectangular zone, and save the reference to it: .. code:: python - painter.layer("F_Cu") - zone = painter.rect_zone(0,0,10,10) + p = CircuitPainter() + p.layer("F_Cu") + zone = p.rect_zone(0,0,10,10) Then, modify the zone properties using the pcbnew api: @@ -48,7 +49,7 @@ with some different DRC settings: .. code:: python import pcbnew - settings = painter.pcb.GetDesignSettings() + settings = p.pcb.GetDesignSettings() settings.SetCopperLayerCount(4) # Change to a 4-layer board design settings.m_CopperEdgeClearance = pcbnew.FromMM(0.1) # Set the copper-to-edge spacing to 0.1mm @@ -68,7 +69,7 @@ Circuit Painter by passing it the file name: .. code:: python - painter = CircuitPainter('my_file.kicad_pcb') + p = CircuitPainter('my_file.kicad_pcb') New objects will then be added to that board, in a new group. diff --git a/doc/concepts.rst b/doc/concepts.rst new file mode 100644 index 0000000..d276f4a --- /dev/null +++ b/doc/concepts.rst @@ -0,0 +1,76 @@ +Core concepts +============= + +Circuit Painter works by drawing objects onto PCB layers. + +PCB layers +---------- + +There are a lot of layers that make up a PCB! Fortunately, we only need to +focus on a few of them: + +* Edge cuts +* Top silkscreen +* Top soldermask +* Top copper +* Bottom copper +* Bottom soldermask +* Bottom silkscreen + +In Circuit Painter, you set the active drawing layer by calling the layer() +function: + + .. code:: python + + painter.layer('F_Cu') + +Any objects created afterwards will be drawn on that layer, until it is +updated by another layer() call. Note that some objects, particularly the +conductive ones, can only be placed on a copper layer. + +Taxonomy of objects +------------------- + +Circuit Painter allows you to create two categories of objects- conductive, +and non-conductive. Conductive objects are used to carry electricity, and +are assigned to 'nets'. Non-conductive objects are used for making graphics, +markings, and defining the board outline. + + +Conductive: + +* Tracks +* Arc Tracks +* Polygons +* Footprints +* Vias + +Non-Conductive: + +* Lines +* Arcs +* Cirles +* Polygons +* Rect +* Text + +Object attribues +---------------- + +Many objects have attributes that need to be set when calling them. For +example, the width of a line or track is set by the width() function. + +Global attributes are: + +* width +* fill / no fill +* layer +* designators / no designators + +Drawing coordinates +------------------- + +Circuit Painter features a virtual transformation matrix, to make scripting +similar arrangements of objects anywhere on a board. It supports both linear +and rotational transformations for all objects, allowing for example LEDs to +be aligned around a circle. \ No newline at end of file diff --git a/doc/examples.rst b/doc/examples.rst index 74c66b2..d9def43 100644 --- a/doc/examples.rst +++ b/doc/examples.rst @@ -1,5 +1,5 @@ -Examples -======== +Example Projects +================ Here is a collection of example boards designed using CircuitPainter. @@ -29,4 +29,15 @@ Click on the photo to see the source code for the board. :width: 400 :target: https://github.com/Blinkinlabs/circuitpainter/blob/main/examples/hello_painter.py - Hello Painter \ No newline at end of file + Hello Painter + + * - .. image:: _static/images/examples_perfboard.png + :width: 400 + :target: https://github.com/Blinkinlabs/circuitpainter/blob/main/examples/perfboard.py + + Perfboard + + - + +Have you made a board with Circuit Painter? Share it with us so that we can +add it to the gallery! \ No newline at end of file diff --git a/doc/getting_started.rst b/doc/getting_started.rst index f5290de..a9fa25d 100644 --- a/doc/getting_started.rst +++ b/doc/getting_started.rst @@ -11,25 +11,25 @@ Start by creating a drawing context: .. code:: python from circuitpainter import CircuitPainter - painter = CircuitPainter() + p = CircuitPainter() Using the painter object, you can draw non-conductive and conductve shapes, footprints, and text onto the PCB. First, set the layer to place the object on (tip: use -print(painter.layers.keys()) to show all available layers): +print(p.layers.keys()) to show all available layers): .. code:: python - painter.layer('F_SilkS') + p.layer('F_SilkS') Next, draw some non-conductive objects: .. code:: python - painter.circle(0,0,3) # Draw a circle with radius 3 at the board origin - painter.line(0,0,10,10) # Draw a line from the board origin to (10,10) - painter.circle(10,10,3) # Draw a circle with raidus 3 at position (10,10) + p.circle(0,0,3) # Draw a circle with radius 3 at the board origin + p.line(0,0,10,10) # Draw a line from the board origin to (10,10) + p.circle(10,10,3) # Draw a circle with raidus 3 at position (10,10) So far, there hasn't been any visual indication of what you're making. To get a preview of what your design looks like, use the preview() @@ -37,7 +37,7 @@ function: .. code:: python - painter.preview() + p.preview() This will save the design to a temporary location, then open it in the KiCad editor: @@ -54,12 +54,12 @@ To change the width of lines, use the width() command: .. code:: python - painter.width(0.5) - painter.line(0,0,10,0) # line with width 0.5mm - painter.width(1) - painter.line(0,5,10,5) # line with width 1mm - painter.width(2) - painter.line(0,10,10,10) # line with width 2mm + p.width(0.5) + p.line(0,0,10,0) # line with width 0.5mm + p.width(1) + p.line(0,5,10,5) # line with width 1mm + p.width(2) + p.line(0,10,10,10) # line with width 2mm .. image:: _static/images/example-line-widths.png :width: 600 @@ -69,9 +69,9 @@ translate() and rotate() features: .. code:: python - painter.translate(10,10) - painter.rotate(30) - painter.rect(-5,-5,5,5) # Rectangle is drawn at a 30 degreen angle, centered at (10,10). + p.translate(10,10) + p.rotate(30) + p.rect(-5,-5,5,5) # Rectangle is drawn at a 30 degreen angle, centered at (10,10). .. image:: _static/images/example-rotate-rect.png :width: 600 @@ -81,12 +81,12 @@ calculated as a 2d transformation matrix) .. code:: python - painter.translate(10,10) - painter.rect(-5,-5,5,5) # Rectangle is drawn centered at (10,10). - painter.translate(10,10) - painter.rect(-5,-5,5,5) # Rectangle is drawn centered at (20,20). - painter.translate(10,10) - painter.rect(-5,-5,5,5) # Rectangle is drawn centered at (30,30). + p.translate(10,10) + p.rect(-5,-5,5,5) # Rectangle is drawn centered at (10,10). + p.translate(10,10) + p.rect(-5,-5,5,5) # Rectangle is drawn centered at (20,20). + p.translate(10,10) + p.rect(-5,-5,5,5) # Rectangle is drawn centered at (30,30). .. image:: _static/images/example-translate-rect.png :width: 600 @@ -97,11 +97,11 @@ and pop_matrix(). (Note: This is implemented as a stack, and multiple pushes can .. code:: python for angle in range(0,360,30): - painter.push_matrix() # Save the current transformation settings - painter.rotate(angle) - painter.translate(10,10) - painter.rect(-5,-5,5,5) - painter.pop_matrix() # Restore previous transformation settings + p.push_matrix() # Save the current transformation settings + p.rotate(angle) + p.translate(10,10) + p.rect(-5,-5,5,5) + p.pop_matrix() # Restore previous transformation settings .. image:: _static/images/example-push-pop-rect.png :width: 600 @@ -115,8 +115,8 @@ command: .. code:: python - painter.layer('F_Cu') - painter.footprint(0,0,"LED_SMD","LED_0805_2012Metric") + p.layer('F_Cu') + p.footprint(0,0,"LED_SMD","LED_0805_2012Metric") .. image:: _static/images/example-add-led.png :width: 600 @@ -129,10 +129,10 @@ your circuit is correct: .. code:: python - painter.layer('F_Cu') - painter.footprint(0,0,"Resistor_SMD","R_0805_2012Metric",nets=['gnd','led_n']) - painter.footprint(5,0,"LED_SMD","LED_0805_2012Metric",nets=['led_n','vcc']) - painter.track(1,0,4,0) + p.layer('F_Cu') + p.footprint(0,0,"Resistor_SMD","R_0805_2012Metric",nets=['gnd','led_n']) + p.footprint(5,0,"LED_SMD","LED_0805_2012Metric",nets=['led_n','vcc']) + p.track(1,0,4,0) .. image:: _static/images/example-connect-led.png :width: 600 @@ -153,15 +153,15 @@ the previous translation operations, to make a ring of LEDs: .. code:: python for angle in range(0,360,30): - painter.push_matrix() - painter.rotate(angle) # Rotation and translation for the next resistor/led combination - painter.translate(5,0) - painter.layer('F_Cu') - painter.footprint(0,0,"Resistor_SMD","R_0805_2012Metric",nets=['gnd',f'led_{angle}']) - painter.footprint(5,0,"LED_SMD","LED_0805_2012Metric",nets=[f'led_{angle}','vcc']) - painter.track(1,0,4,0) - - painter.pop_matrix() + p.push_matrix() + p.rotate(angle) # Rotation and translation for the next resistor/led combination + p.translate(5,0) + p.layer('F_Cu') + p.footprint(0,0,"Resistor_SMD","R_0805_2012Metric",nets=['gnd',f'led_{angle}']) + p.footprint(5,0,"LED_SMD","LED_0805_2012Metric",nets=[f'led_{angle}','vcc']) + p.track(1,0,4,0) + + p.pop_matrix() .. image:: _static/images/example-led-ring.png :width: 600 @@ -175,36 +175,36 @@ To make a complete board, here is the [rest of the owl](https://knowyourmeme.com from circuitpainter import CircuitPainter painter = CircuitPainter() - painter.no_designators() # Don't show reference designator names on the board silkscreen - painter.layer('F_Cu') - painter.width(.2) + p.no_designators() # Don't show reference designator names on the board silkscreen + p.layer('F_Cu') + p.width(.2) for angle in range(0,360,36): - painter.push_matrix() # Save the current transformation settings - painter.rotate(angle) - painter.translate(5,0) - painter.footprint(0,0,"Resistor_SMD","R_0805_2012Metric",nets=['gnd',f'led_{angle}']) - painter.footprint(5,0,"LED_SMD","LED_0805_2012Metric",nets=[f'led_{angle}','vcc']) - painter.track(1,0,4,0) # Connect the resistor to the LED - painter.track(-1,0,-2,0) # Connect the resistor to ground - painter.via(-2,0) - painter.track(6,0,7,0) # Connect the LED to vcc - painter.via(7,0) - painter.pop_matrix() + p.push_matrix() # Save the current transformation settings + p.rotate(angle) + p.translate(5,0) + p.footprint(0,0,"Resistor_SMD","R_0805_2012Metric",nets=['gnd',f'led_{angle}']) + p.footprint(5,0,"LED_SMD","LED_0805_2012Metric",nets=[f'led_{angle}','vcc']) + p.track(1,0,4,0) # Connect the resistor to the LED + p.track(-1,0,-2,0) # Connect the resistor to ground + p.via(-2,0) + p.track(6,0,7,0) # Connect the LED to vcc + p.via(7,0) + p.pop_matrix() # Fill the back of the board with a copper zone, and assign it to the 'vcc' net - painter.layer('B_Cu') - painter.circle_zone(0,0,14,net='vcc') + p.layer('B_Cu') + p.circle_zone(0,0,14,net='vcc') # Add a battery connector to the back - painter.layer('B_Cu') - painter.footprint(0,0,"Battery","BatteryHolder_Keystone_3000_1x12mm",nets=['vcc','vcc','gnd']) + p.layer('B_Cu') + p.footprint(0,0,"Battery","BatteryHolder_Keystone_3000_1x12mm",nets=['vcc','vcc','gnd']) # Make the board shape to a circle - painter.layer("Edge_Cuts") - painter.circle(0,0,14) + p.layer("Edge_Cuts") + p.circle(0,0,14) - painter.preview() + p.preview() .. image:: _static/images/example-rest-of-owl.png :width: 600 diff --git a/doc/index.rst b/doc/index.rst index 367a21a..9b79417 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -24,6 +24,13 @@ Circuit painter works as a front end / wrapper for `KiCad's pcbnew `_ about Circuit painter. +.. toctree:: + :hidden: + :caption: Introduction + + introduction + concepts + .. toctree:: :hidden: :caption: Getting Started diff --git a/doc/introduction.rst b/doc/introduction.rst new file mode 100644 index 0000000..78f9942 --- /dev/null +++ b/doc/introduction.rst @@ -0,0 +1,38 @@ +What is Circuit Painter? +======================== + +Circuit painter is a creative coding tool for making functional printed +circuit boards. + +Unlike traditional CAD tools, where you design a PCB by using a GUI to place +parts on a board by hand, in Circuit Painter, you design PCBs by writing code. +You could think of it as similar to how `OpenSCAD `_ +works for 3d modeling, but for PCBs. + +Projects that are a good fit for Circuit Painter +------------------------------------------------ + +* Explore / generative shapes +* Have a lot of simple, repetitive elements (LEDs, test points, mounting + holes, ...) +* Use things that are very hard to make with traditional CAD- arcs, spirals + +See the :doc:`examples` page for a gallery of boards made with Circuit Painter. + +Projects that are not a good fit for Circuit Painter +---------------------------------------------------- + +* Complex, non-repetitive designs such as a microcontroller development board +* Need integration with a traditional workflow + +Note that you still can integrate scripted elements into a manually designed +board! For example, Circuit Painter was used to create the spiral track +elements in this `Charlieplexed watch `_ +by Trammell Hudson. + +What's under the hood? +---------------------- + +Circuit painter works as a front end / wrapper for `KiCad's pcbnew `_. +It uses the `SWIG bindings `_ +to create and add objects to a KiCad PCB. \ No newline at end of file diff --git a/doc/notes.rst b/doc/notes.rst index 9d9da17..875f0c4 100644 --- a/doc/notes.rst +++ b/doc/notes.rst @@ -15,6 +15,8 @@ Similar nontraditional PCB design tools * `SVG-PCB `_ * `SVG2Shenzhen `_ +* `Gingerbread `_ +* `Gerbolize `_ Credits ------- diff --git a/examples/hex_perfboard.py b/examples/hex_perfboard.py index d2ebd5a..49a18b7 100644 --- a/examples/hex_perfboard.py +++ b/examples/hex_perfboard.py @@ -58,16 +58,16 @@ def HexPerfboard(count,spacing,hole_d,ring_d): return painter if __name__ == "__main__": - parser = ArgumentParser(description="perfboard generator") - parser.add_argument('-x',type=int,default=9, help="Number of holes in the x direction") + parser = ArgumentParser(description="hexagonal perfboard generator") + parser.add_argument('-c',type=int,default=9, help="Number of hex rings") parser.add_argument('-s','--spacing',type=float,default=2.54, help="Spacing between the holes (mm)") parser.add_argument('-d','--hole_diameter',type=float,default=1.02, help="Hole diameter (mm)") parser.add_argument('-r','--ring_diameter',type=float,default=2, help="Ring diameter (mm)") parser.add_argument('--save',action="store_true",help="Save the design to a KiCad file") args = parser.parse_args() - painter = HexPerfboard(args.x, args.spacing, args.hole_diameter,args.ring_diameter) + painter = HexPerfboard(args.c, args.spacing, args.hole_diameter,args.ring_diameter) if args.save: - painter.export_gerber('perfboard') + painter.export_gerber('hex_perfboard') else: painter.preview() diff --git a/examples/perfboard.py b/examples/perfboard.py index b83fd81..dcad4a7 100644 --- a/examples/perfboard.py +++ b/examples/perfboard.py @@ -1,18 +1,18 @@ from circuitpainter import CircuitPainter from argparse import ArgumentParser -def Perfboard(x_count,y_count,x_spacing,y_spacing,hole_d,ring_d): +def Perfboard(x_count,y_count,spacing,hole_d,ring_d): painter = CircuitPainter() # Make the board shape painter.layer("Edge_Cuts") - painter.rect(0,0,x_spacing*x_count,y_spacing*y_count) + painter.rect(0,0,spacing*x_count,spacing*y_count) painter.width(0.05) painter.fill() - for x_pos in [x_spacing*(x+0.5) for x in range(0,x_count)]: - for y_pos in [y_spacing*(y+0.5) for y in range(0,y_count)]: + for x_pos in [spacing*(x+0.5) for x in range(0,x_count)]: + for y_pos in [spacing*(y+0.5) for y in range(0,y_count)]: painter.layer('F_Cu') painter.via(x_pos,y_pos,d=hole_d,w=ring_d) @@ -37,7 +37,7 @@ def Perfboard(x_count,y_count,x_spacing,y_spacing,hole_d,ring_d): parser.add_argument('--save',action="store_true",help="Save the design to a KiCad file") args = parser.parse_args() - painter = Perfboard(args.x, args.y, args.spacing, args.spacing, args.hole_diameter,args.ring_diameter) + painter = Perfboard(args.x, args.y, args.spacing, args.hole_diameter,args.ring_diameter) if args.save: painter.export_gerber('perfboard') else: