{"id":37401531,"url":"https://github.com/kaustavg/microcad","last_synced_at":"2026-05-01T22:01:45.395Z","repository":{"id":320628820,"uuid":"1025863071","full_name":"kaustavg/microcad","owner":"kaustavg","description":"Programmatically draw 3D microfluidic chips in Fusion 360 or FreeCAD.","archived":false,"fork":false,"pushed_at":"2026-04-29T14:18:10.000Z","size":258,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-29T15:34:34.796Z","etag":null,"topics":["cad","freecad","fusion360","layout","microfluidics"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kaustavg.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-07-24T23:53:24.000Z","updated_at":"2026-04-29T14:22:56.000Z","dependencies_parsed_at":null,"dependency_job_id":"cb8a30dd-8ea8-489f-af95-41aa426087df","html_url":"https://github.com/kaustavg/microcad","commit_stats":null,"previous_names":["kaustavg/microcad"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/kaustavg/microcad","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kaustavg%2Fmicrocad","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kaustavg%2Fmicrocad/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kaustavg%2Fmicrocad/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kaustavg%2Fmicrocad/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kaustavg","download_url":"https://codeload.github.com/kaustavg/microcad/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kaustavg%2Fmicrocad/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32514340,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-30T13:12:12.517Z","status":"online","status_checked_at":"2026-05-01T02:00:05.856Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cad","freecad","fusion360","layout","microfluidics"],"created_at":"2026-01-16T05:46:11.985Z","updated_at":"2026-05-01T22:01:45.389Z","avatar_url":"https://github.com/kaustavg.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Microcad\nProgrammatically draw 3D microfluidic chips in Fusion 360 or FreeCAD.\n\n## Overview\nThis package converts python scripts into 3D microfluidic circuit designs in Fusion 360 or FreeCAD. \n\nTypical workflow starts by writing a script in python which specifies the positions and connections of elements in a microfluidic circuit. Python objects from this package are used to define the circuit components and their connections in the script. Specifying the circuit in the form of a script rather than a diagram makes it easy to make small changes to component values (such as resistances) and to copy and re-use circuit blocks multiple times in a design. \n\nAfter creating the python script using the objects from this package, we run a special macro from within Fusion 360 or FreeCAD, which loads our script and executes it, drawing everything on the stage. After the script has been run, the design can be further modified using the usual Fusion 360 or FreeCAD GUI or the script itself can be tweaked and the design regenerated from scratch. It can then be exported as an STL file for 3D printing.\n\n## Installation\n1. Install Fusion 360 or FreeCAD on your computer.\n2. Place this python package in a folder accessible to PYTHONPATH in your computer, just like any other python package you install. This allows the computer to find the new package when you write programs with it. For Windows, if you want to add a new location to your computer's PYTHONPATH, you can run the following python command:\n```python\nimport sys\nsys.path.append(r'C:\\Your\\Folder\\Here\\microcad')\n```\n3. (Option 1) If you intend to run Microcad using Fusion 360, place the file \"RunMicrocad.py\" in the \"Scripts\" folder of your Fusion 360 installation. For Windows, this is typically located in \"C:\\Users\\YourUserNameHere\\AppData\\Roaming\\Autodesk\\Autodesk Fusion 360\\API\\Scripts\\\". This is the macro that will be run from within Fusion 360 to generate the design.\n4. (Option 2) If you intend to run Microcad using FreeCAD, place the file \"RunMicrocad.FCMacro\" in the \"Macros\" folder of your FreeCAD installation. For Windows, this is typically located in \"C:\\Users\\YourUserNameHere\\AppData\\Roaming\\FreeCAD\\Macro\\\". This is the macro that will be run from within FreeCAD to generate the design.\n\n## Annotated Example Script\nAs an example we will be drawing a simple common-source amplifier consisting of one transistor, one resistor, and four ports. The entire script to generate this design in Fusion 360 is given below:\n```python\nimport microcad as mc\n\ndef main():\n\tdesign = mc.Design(backend='fusion')\n\tcir = design.create_circuit()\n\t# Circuit Components\n\ttran1 = cir.M((0,0,0),anchor='S') # Transistor\n\ttrace1 = cir.T([tran1.D,tran1.D+(0,-500)]) # Short trace to resistor\n\tres1 = cir.R(trace1.P2,50,anchor='L',rotation=-90) # Resistor of 50 kPa*s/uL\n\t# Ports\n\tsup1 = cir.V(tran1.S+(0,5000)) # Supply Port\n\tgnd1 = cir.V(res1.R+(0,-5000)) # Ground Port\n\tout1 = cir.V((sup1.C%gnd1.C)+(6000,1000)) # Output Port\n\tinp1 = cir.V(tran1.G1+(-6000,0),zspan=[0,-cir.params['sub_H']]) # Input Port\n\t# Traces\n\tcir.T([sup1.C,tran1.S])\n\tcir.T([res1.R,gnd1.C])\n\tcir.T([tran1.D+(0,-250),tran1.D+(1000,-250),out1.C],trace_R=1000)\n\tcir.T([tran1.G1,inp1.C],secs=mc.CurveSec(W=250,H=-30))\n  ```\n  \nTo run this script in Fusion 360, make sure that RunMicrocad.py is correctly pointing to our script file. Then in Fusion 360 we navigate to Utilities \u003e Add-Ins \u003e Scripts and Add-Ins \u003e RunMicrocad.py. The drawing process should be done in a few seconds.\n\nTo run this script in FreeCAD, make sure that RunMicrocad.FCMacro is correctly pointing to our script file. Then in FreeCAD we navigate to Macro \u003e Macros... \u003e [Select RunMicrocad.FCMacro] \u003e Execute. The drawing process should be done in a few seconds.\n\nWe now explain each line in the above example script.\n\n```python\nimport microcad as mc\n\ndef main():\n\tdesign = mc.Design(backend='fusion')\n\tcir = design.create_circuit()\n```\nThis imports Microcad and defines the function that RunMicrocad.py will run (which must be called \"main\").\n\nWe start by creating a new Design object, for which you should probably have only one per file. The Design object can hold multiple circuits. You can set various default drawing parameters here (like drawing scale, trace widths, and even liquid viscosity used to calculate resistances), and these default parameters will automatically pass down to all daughter Circuits objects drawn in this Design object. For a list of these parameters, please see the end of this document.\n\nIn this Design object, we then create a new Circuit object. This is another chance to set default parameters for the drawing the circuit, and these default parameters will pass down to all daughter circuit elements that you draw within this circuit but will not affect other Circuit objects in the Design. This is useful if you want to try two versions of the same circuit, perhaps one with rounded channels and one with rectangular channels, both on the same Design. Each Circuit object will end up as a unique \"Component\" in Fusion 360.\n\n```python\n\t# Circuit Components\n\ttran1 = cir.M((0,0,0),anchor='S') # Transistor\n\ttrace1 = cir.T([tran1.D,tran1.D+(0,-500)]) # Short trace to resistor\n\tres1 = cir.R(trace1.P2,50,anchor='L',rotation=-90) # Resistor of 50 kPa*s/uL\n```\nWe start by adding a transistor to our circuit using the M function. Here we specify that the transistor is drawn such that the source terminal (S) is located at the origin. Other terminals (D, G1, G2) could just as easily be specified as the anchor point. We also name this transistor object 'tran1'. Here we drew this transistor with all the default settings for length, width, height, etc. but you could easily overwrite these defaults for this particular transistor by specifying keyword arguments.\n\nNext we draw a short trace starting at the drain of the tranistor and extending 500um down. The T function takes in a list of any number of Points in 3D space and connects them all up with a smooth trace. Note that these points can be plain tuples (as we used to define the position of the transistor) or we can use the Microcad Point object which has some useful features.\n\nThe first point in our trace is specified as the drain terminal of the transistor we made already (tran1.D). This way, if we move or modify the transistor, the trace moves along with it automatically. Internally, this first point is represented not as a tuple, but as a Microcad Point object. This means we can do intuitive operations on this point, like shifting it by 500um down by simply \"adding\" a tuple to it, which we have used for the second point in the trace. Note that if you do not specify the z coordinate, the program assumes a value of 0.\n\nWe then draw a resistor connected to the endpoint of the trace we just drew (trace1.P2). We specify that the resistance should be 50 kPa s/ul, and the computer will automatically size and draw a serpentine channel to meet this desired value. Similar to the transistor, we specify that the left side of the resistor (L) is the anchor point for drawing the component. Here we also specify an optional drawing parameter, rotation, using a keyword argument.\n\n```python\n\t# Ports\n\tsup1 = cir.V(tran1.S+(0,5000)) # Supply Port\n\tgnd1 = cir.V(res1.R+(0,-5000)) # Ground Port\n```\nLike before, we are just adding new circuit components by specifying their position relative to existing Points in the circuit. Here we see the usefullness of using Microcad Points, since we can easily add tuples to move the points around. These ports will automatically generate a barb head facing upwards for tubing.\nThe supply port is located above the transistor source terminal (S) and the ground port is located below the resistor right terminal (R)\n\n```python\n\tout1 = cir.V((sup1.C%gnd1.C)+(6000,1000)) # Output Port\n\tinp1 = cir.V(tran1.G1+(-6000,0),zspan=[0,-cir.params['sub_H']]) # Input Port\n```\nHere we see some more advanced Point operations. The % operator automatically returns the midpoint of two Points, so here we are specifying that the output port is located 6mm to the right and 1mm above the midpoint of the supply port center (C) and the ground port center (C).\n\nThe input port is a little special, because it is located on the *lower* layer of the chip and is facing downwards. We therefore modify the z-direction of the port, telling it to be drawn from z=0 to z=-4mm (the thickness of the resin substrate) instead of the default z=0 to z=4mm (facing upwards). We grab the exact value of the resin substrate thickness from the default circuit parameters.\n\n```python\n\t# Traces\n\tcir.T([sup1.C,tran1.S])\n\tcir.T([res1.R,gnd1.C])\n```\nLike our earlier trace, we are just connecting existing element terminals, in this case the source to the supply and the resistor to the ground. For a list of circuit elements and their terminals you can refer to, see the end of this document.\n\n```python\n\tcir.T([tran1.D+(0,-250),tran1.D+(1000,-250),out1.C],trace_R=1000)\n\tcir.T([tran1.G1,inp1.C],secs=mc.CurveSec(W=250,H=-30))\n```\nTraces can draw between any number of Points in a list. Here we also specify an optional drawing parameter for the trace, a radius-of-curvature of 1000um.\n\nThe final line hints at how extensible the trace function can be. So far we had been drawing all traces using the default cross-section, which is a rectangle 250um wide and 50um tall. However, we can specify any cross-sectional shape we want to draw our traces (and even our transistors) with. These cross-sections are termed here as \"Sections\", and you can specify various shapes (rectangular, curved edge rectangular, trapezoidal, tubular) with different dimensions. You can even specify a list of sections to be used for every point in a trace, and the program will smoothly transition from one cross-section to another as it draws the trace between the points specified, which enables tapers or complex trace shape changes.\n\nHere, we make a curved bottom channel from the gate of the transistor to the input port. These are both on the underside of the chip and we therefore use a section that runs beneath the membrane, where the height H is negative.\n\n## Details\n### Points\nMicrocad Point objects support the following operations (between a Point/Point, Tuple/Point, or a Point/Tuple, but not Tuple/Tuple)\n|Symbol| Operation|\n|:---:|:---|\n|+ | Add in x, y, and z|\n|- | Subtract in x, y, and z|\n|\\| | Take x of left point and y of right point|\n|^ | Take x, y of left point and z of right point|\n|% | Return midpoint in x and y|\n|* | Multiply elementwise by a scalar|\n|/ | Divide elementwise by a scalar|\n\n### Drawing Parameters\nThe default drawing paramters for a new Design are a python dictionary named \"params\":\n```python\nself.params = { # Default values\n\t# Constants\n\t'fluid_Mu':1.01e-3, # Fluid dynamic viscosity in Pa*s\n\t# Fab parameters\n\t'slop': 250, # Slop in alignment to space elements in UM\n\t'sub_H': 4000, # Substrate thickness in UM\n\t# Element parameters\n\t'trace_sec': RecSec(W=250, H=50), # Default section\n\t'trace_R': 250, # Trace radius of curvature in UM\n\t'trace_cap': 'none', # Trace endcap ('none','round','square')\n\t'chan_sec': RecSec(W=250, H=50), # Transistor flowchannel section\n\t'gate_sec': RecSec(W=250, H=-50), # Transistor gate section\n\t'res_sec': RecSec(W=50, H=50), # Resistor section\n\t'res_L': 1000, # Resistor bounding box length in UM\n\t'res_cap': 'none', # Resistor endcap ('none','round','square')\n\t'via_R': 350, # Via radius in UM\n\t}\n```\nWhen you create a new Circuit within a Design, you can specify new values for these parameters that only affect that new Circuit. Any parameters that have not been specified are copied from the Design unchanged. Similarly, when you create an Element within a Circuit, you can specify new values for these parameters that only affect that new Element. Any parameters that have not been specified are copied from the Circuit unchanged.\n\n### Elements\nBelow are the supported elements you can combine to make circuits.\n#### Trace (T)\nDraw a trace connecting a list of points.\n```\nTerminals\n\tP1 : Starting point\n\tP2 : Ending point\n\tC : Midpoint of start and end point\nRequired Arguments\n\tpts : List of Points or tuples to be connected in a trace\nOptional Arguments\n\ttrace_sec: Section to be used when drawing all points of the trace\n\ttrace_sec: List of sections (same length as pts) to draw individual sections for each point in the trace\n\ttrace_R: Radius of curvature in UM to apply to the entire trace path\n\ttrace_R: List of radii of curvatures (same length as pts) to specify the radius of curvature for each point in the trace\n```\n#### Transistor (M)\nDraw a fluidic transistor accounting for misalignments.\n```\nTerminals\n\tS : Source\n\tD : Drain\n\tG1 : Left side of Gate\n\tG2 : Right side of Gate\n\tC : Center\nRequired Arguments\n\tpoint : Location of anchor point\nOptional Arguments\n\tanchor : Which terminal should be used as anchor (default: 'S')\n\trotation : Rotation angle in degrees (default: 0)\n\tinvert : Whether to draw flow channel below or above membrane (default: False)\n\tflow_sec: Section to be used to draw flow channel\n\tgate_sec: Section to be used to draw gate channel\n\tslop: Extra margin added around transistor to account for misalignments\n```\n#### Resistor (R)\nDraw a serpentine resistor.\n```\nTerminals\n\tL : Left side\n\tR : Right side\n\tC : Midpoint of left and right side points\nRequired Arguments\n\tpoint : Location of anchor point\n\tval: Resistance value in kPa*s/uL\nOptional Arguments\n\tanchor : Which terminal should be used as anchor (default: 'L')\n\trotation : Rotation angle in degrees (default: 0)\n\tjustify : Whether to turn the serpentine towards the left or right side with respect to draw direction (default: 'left')\n\tres_sec: Section to be used in serpentine\n\tres_L: Bounding box length in um\n\tfluid_Mu: Dynamic vicosity used to calculate resistance in Pa*s\n```\n#### Via (V)\nDraw a cylinder extending vertically in the Z axis.\n```\nTerminals\n\tC : Starting point of via\nRequired Arguments\n\tpoint : Location of via starting point\nOptional Arguments\n\tzspan : Two-element list of starting and ending Z cordinates for the cylinder (default: [0, params['sub_H']])\n\tvia_R : Radius of via lumen in um\n```\n### Sections\nBelow are the supported Section objects you can use to specify the cross-sectional shapes for traces, resistors, and transistors.\n#### RecSec\nDraw a rectangular cross-section. Negative heights are drawn below the draw point.\n```\nOptional Arguments\n\tW : Width of rectangle in um (default: 250)\n\tH : Height of rectangle in um (default: 50)\n```\n#### CurveSec\nDraw a rectangular cross-section with filleted edges. Negative heights are drawn below the draw point.\n```\nOptional Arguments\n\tW : Width of rectangle in um (default: 250)\n\tH : Height of rectangle in um (default: 50)\n\tR : Fillet radius (default: H)\n```\n#### TrapzSec\nDraw a rectangular cross-section with chamfered edges. Negative heights are drawn below the draw point.\n```\nOptional Arguments\n\tW : Maximum width of cross-section in um (default: 250)\n\tH : Total height of cross-section in um (default: 50)\n\tWt : Minimum width of cross-section in um (default: W-2*H)\n\tHt : Z length along which the the width tapers to Wt in um (default: 45deg chamfer)\n```\n#### TubeSec\nDraw a circular cross-section.\n```\nOptional Arguments\n\tR : Radius of cross-section in um (default: 250)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkaustavg%2Fmicrocad","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkaustavg%2Fmicrocad","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkaustavg%2Fmicrocad/lists"}