diff --git a/coil_plugin.py b/coil_plugin.py index c372da9..df632c0 100644 --- a/coil_plugin.py +++ b/coil_plugin.py @@ -6,8 +6,8 @@ def create_tracks(board, group, net, layer, thickness, coords): last_x = None last_y = None for coord in coords: - x = coord['x'] - y = coord['y'] + x = coord["x"] + y = coord["y"] track = pcbnew.PCB_TRACK(board) if last_x is not None: track.SetStart(pcbnew.wxPointMM(float(last_x), float(last_y))) @@ -33,12 +33,16 @@ class CoilPlugin(pcbnew.ActionPlugin): def Run(self): board = pcbnew.GetBoard() # load up the JSON with the coil parameters - coil_data = json.load(open("/Users/chrisgreening/Work/projects/pcb_motor/kicad-coil-plugins/coil.json")) + coil_data = json.load( + open( + "/Users/chrisgreening/Work/projects/pcb_motor/kicad-coil-plugins/coil.json" + ) + ) # parameters - track_width = coil_data['parameters']['trackWidth'] - stator_hole_radius = coil_data['parameters']['statorHoleRadius'] - via_diameter = coil_data['parameters']['viaDiameter'] - via_drill_diameter = coil_data['parameters']['viaDrillDiameter'] + track_width = coil_data["parameters"]["trackWidth"] + stator_hole_radius = coil_data["parameters"]["statorHoleRadius"] + via_diameter = coil_data["parameters"]["viaDiameter"] + via_drill_diameter = coil_data["parameters"]["viaDrillDiameter"] # put everything in a group to make it easier to manage pcb_group = pcbnew.PCB_GROUP(board) @@ -56,22 +60,23 @@ class CoilPlugin(pcbnew.ActionPlugin): # pcb_group.AddItem(arc) # create tracks - for track in coil_data["tracks"]['f']: + for track in coil_data["tracks"]["f"]: # find the matching net for the track - # net = board.FindNet(track['net']) - # if net is None: - # raise "Net not found: {}".format(track['net']) - create_tracks(board, pcb_group, None, pcbnew.F_Cu, track_width, track) - - for track in coil_data["tracks"]['b']: - create_tracks(board, pcb_group, None, pcbnew.B_Cu, track_width, track) + net = board.FindNet("coils") + if net is None: + raise "Net not found: {}".format(track["net"]) + create_tracks(board, pcb_group, net, pcbnew.F_Cu, track_width, track) + + for track in coil_data["tracks"]["b"]: + create_tracks(board, pcb_group, net, pcbnew.B_Cu, track_width, track) # create the vias - for via in coil_data['vias']: + for via in coil_data["vias"]: pcb_via = pcbnew.PCB_VIA(board) - pcb_via.SetPosition(pcbnew.wxPointMM(float(via['x']), float(via['y']))) + pcb_via.SetPosition(pcbnew.wxPointMM(float(via["x"]), float(via["y"]))) pcb_via.SetWidth(int(via_diameter * 1e6)) pcb_via.SetDrill(int(via_drill_diameter * 1e6)) + pcb_via.SetNetCode(net.GetNetCode()) board.Add(pcb_via) # pcb_group.AddItem(pcb_via) @@ -86,4 +91,5 @@ class CoilPlugin(pcbnew.ActionPlugin): board.Add(pcb_txt) # pcb_group.AddItem(pcb_txt) -CoilPlugin().register() # Instantiate and register to Pcbnew]) + +CoilPlugin().register() # Instantiate and register to Pcbnew]) diff --git a/coil_version2.ipynb b/coil_version2.ipynb index 9089bfe..12cda28 100644 --- a/coil_version2.ipynb +++ b/coil_version2.ipynb @@ -8,7 +8,9 @@ "source": [ "import pandas as pd\n", "import numpy as np\n", - "import matplotlib as plt\n", + "\n", + "# import matplotlib as plt\n", + "import matplotlib.pyplot as plt\n", "import scipy\n", "from skspatial.objects import LineSegment, Line\n", "from enum import Enum" @@ -46,7 +48,7 @@ "outputs": [], "source": [ "# plot the template shape wrapping around to the first point\n", - "plt.pyplot.plot(\n", + "plt.plot(\n", " [x for x, y in template] + [template[0][0]],\n", " [y for x, y in template] + [template[0][1]],\n", ")" @@ -81,7 +83,23 @@ " if intersection is not None:\n", " cache[angle] = (intersection, segment)\n", " except ValueError:\n", - " pass\n", + " try:\n", + " # nudge the angle slightly\n", + " new_angle = angle + 0.1\n", + " line = LineSegment(\n", + " np.array([0, 0]),\n", + " np.array(\n", + " [\n", + " 1000 * np.cos(np.deg2rad(new_angle)),\n", + " 1000 * np.sin(np.deg2rad(new_angle)),\n", + " ]\n", + " ),\n", + " )\n", + " intersection = line.intersect_line_segment(segment)\n", + " if intersection is not None:\n", + " cache[angle] = (intersection, segment)\n", + " except ValueError:\n", + " pass\n", " return cache\n", "\n", "\n", @@ -162,7 +180,7 @@ " # clamp dot between -1 and 1\n", " dot = max(-1, min(1, dot))\n", " angle = np.arccos(dot)\n", - " if angle > np.deg2rad(0.1):\n", + " if angle > np.deg2rad(5):\n", " optimized_points.append(points[i])\n", " print(\"Optimised from {} to {} points\".format(len(points), len(optimized_points)))\n", " return optimized_points\n", @@ -189,15 +207,13 @@ "outputs": [], "source": [ "points = get_points(template, 10, TRACK_SPACING + TRACK_WIDTH, Layer.BACK, None)\n", - "optimized_points = chaikin(optimize_points(points), 3)\n", - "# df = pd.DataFrame(points, columns=['x', 'y'])\n", - "# ax = df.plot.line(x='x', y='y', label='Coil A', color='blue')\n", - "# ax.axis('equal')\n", + "optimized_points = optimize_points(points)\n", + "df = pd.DataFrame(points, columns=[\"x\", \"y\"])\n", + "ax = df.plot.line(x=\"x\", y=\"y\", label=\"Coil A\", color=\"blue\")\n", + "ax.axis(\"equal\")\n", "\n", "df_optim = pd.DataFrame(optimized_points, columns=[\"x\", \"y\"])\n", - "\n", - "ax = df_optim.plot.line(x=\"x\", y=\"y\", label=\"Coil A\", color=\"green\")\n", - "ax.axis(\"equal\")\n", + "df_optim.plot.line(x=\"x\", y=\"y\", label=\"Coil A\", color=\"green\", ax=ax)\n", "print(len(df_optim), len(df))" ] }, @@ -216,11 +232,30 @@ "metadata": {}, "outputs": [], "source": [ + "vias = []\n", + "\n", "points_f = get_points(template, TURNS, TRACK_SPACING + TRACK_WIDTH, Layer.FRONT, cache)\n", "points_b = get_points(template, TURNS, TRACK_SPACING + TRACK_WIDTH, Layer.BACK, cache)\n", "\n", "points_f = [(0, 0)] + chaikin(optimize_points(points_f), 3)\n", "points_b = [(0, 0)] + chaikin(optimize_points(points_b), 3)\n", + "print(\"Track points\", len(points_f), len(points_b))\n", + "\n", + "# calculat the total length of the track to compute the resistance\n", + "total_length_front = 0\n", + "for i in range(len(points_f) - 1):\n", + " total_length_front += np.linalg.norm(\n", + " np.array(points_f[i + 1]) - np.array(points_f[i])\n", + " )\n", + "print(\"Total length front\", total_length_front)\n", + "\n", + "total_length_back = 0\n", + "for i in range(len(points_b) - 1):\n", + " total_length_back += np.linalg.norm(\n", + " np.array(points_b[i + 1]) - np.array(points_b[i])\n", + " )\n", + "print(\"Total length back\", total_length_back)\n", + "\n", "\n", "COIL_CENTER_RADIUS = STATOR_RADIUS / 2 + 1.5\n", "\n", @@ -228,6 +263,24 @@ "angle_B = 120\n", "angle_C = 240\n", "\n", + "# get the point on an arc at the given angle\n", + "def get_arc_point(angle, radius):\n", + " return (\n", + " radius * np.cos(np.deg2rad(angle)),\n", + " radius * np.sin(np.deg2rad(angle)),\n", + " )\n", + "\n", + "\n", + "# draw an arc\n", + "def draw_arc(start_angle, end_angle, radius, step=10):\n", + " points = []\n", + " for angle in np.arange(start_angle, end_angle + step, step):\n", + " x = radius * np.cos(np.deg2rad(angle))\n", + " y = radius * np.sin(np.deg2rad(angle))\n", + " points.append((x, y))\n", + " return points\n", + "\n", + "\n", "# roate the points by the required angle\n", "def rotate(points, angle):\n", " return [\n", @@ -250,6 +303,10 @@ " ]\n", "\n", "\n", + "def create_via(point):\n", + " return {\"x\": point[0], \"y\": point[1]}\n", + "\n", + "\n", "# flip the y coordinate\n", "def flip(points):\n", " return [[x, -y] for x, y in points]\n", @@ -293,123 +350,75 @@ " rotate(flip(points_b), angle_C_opp), COIL_CENTER_RADIUS, angle_C_opp\n", ")\n", "\n", + "# connect the front and back coils together\n", + "vias.append(create_via(get_arc_point(angle_A, COIL_CENTER_RADIUS)))\n", + "vias.append(create_via(get_arc_point(angle_B, COIL_CENTER_RADIUS)))\n", + "vias.append(create_via(get_arc_point(angle_C, COIL_CENTER_RADIUS)))\n", + "vias.append(create_via(get_arc_point(angle_A_opp, COIL_CENTER_RADIUS)))\n", + "vias.append(create_via(get_arc_point(angle_B_opp, COIL_CENTER_RADIUS)))\n", + "vias.append(create_via(get_arc_point(angle_C_opp, COIL_CENTER_RADIUS)))\n", + "\n", "# connect the front copper opposite coils together\n", "common_connection_radius = STATOR_RADIUS - (TRACK_WIDTH + TRACK_SPACING)\n", - "common_coil_connections_b = [\n", - " (\n", - " common_connection_radius * np.cos(np.deg2rad(angle)),\n", - " common_connection_radius * np.sin(np.deg2rad(angle)),\n", - " )\n", - " for angle in np.arange(angle_A_opp, angle_C_opp + 5, 5)\n", - "]\n", - "coil_A_opp_f.append(\n", - " (\n", - " common_connection_radius * np.cos(np.deg2rad(angle_A_opp)),\n", - " common_connection_radius * np.sin(np.deg2rad(angle_A_opp)),\n", - " )\n", - ")\n", - "coil_B_opp_f.append(\n", - " (\n", - " common_connection_radius * np.cos(np.deg2rad(angle_B_opp)),\n", - " common_connection_radius * np.sin(np.deg2rad(angle_B_opp)),\n", - " )\n", - ")\n", - "coil_C_opp_f.append(\n", - " (\n", - " common_connection_radius * np.cos(np.deg2rad(angle_C_opp)),\n", - " common_connection_radius * np.sin(np.deg2rad(angle_C_opp)),\n", - " )\n", - ")\n", + "common_coil_connections_b = draw_arc(angle_A_opp, angle_C_opp, common_connection_radius)\n", + "coil_A_opp_f.append(get_arc_point(angle_A_opp, common_connection_radius))\n", + "coil_B_opp_f.append(get_arc_point(angle_B_opp, common_connection_radius))\n", + "coil_C_opp_f.append(get_arc_point(angle_C_opp, common_connection_radius))\n", "\n", - "# connect coil A to it's opposite\n", + "vias.append(create_via(get_arc_point(angle_A_opp, common_connection_radius)))\n", + "vias.append(create_via(get_arc_point(angle_B_opp, common_connection_radius)))\n", + "vias.append(create_via(get_arc_point(angle_C_opp, common_connection_radius)))\n", + "\n", + "# route coil A and coil C round to coil B so we have a good place to connect to them\n", + "coil_input_radius = STATOR_RADIUS - (TRACK_WIDTH + TRACK_SPACING * 2 + VIA_DIAM / 2)\n", + "coil_A_input_connection_b = draw_arc(angle_A, 80, coil_input_radius)\n", + "coil_B_input_connection_f = draw_arc(90, angle_B, coil_input_radius)\n", + "coil_C_input_connection_b = draw_arc(100, angle_C, coil_input_radius)\n", + "\n", + "coil_A_f.append(coil_A_input_connection_b[0])\n", + "coil_B_f.append(get_arc_point(angle_B, coil_input_radius))\n", + "coil_C_f.append(coil_C_input_connection_b[-1])\n", + "\n", + "vias.append(create_via(coil_A_input_connection_b[0]))\n", + "vias.append(create_via(coil_C_input_connection_b[-1]))\n", + "\n", + "\n", + "# wires for connecting to opposite coils\n", "connection_radius1 = STATOR_HOLE_RADIUS + (TRACK_SPACING)\n", "connection_radius2 = connection_radius1 + (TRACK_SPACING + VIA_DIAM / 2)\n", - "# draw a 45 degree line from coil A at connection radius 1\n", + "\n", + "# draw a 45 degree line from each coil at connection radius 1\n", "# then connect up to connection radius 2\n", "# draw a 45 degree line to the opposite coil\n", - "coil_A_b.append(\n", - " (\n", - " connection_radius1 * np.cos(np.deg2rad(angle_A)),\n", - " connection_radius1 * np.sin(np.deg2rad(angle_A)),\n", - " )\n", - ")\n", - "coil_A_opp_b.append(\n", - " (\n", - " connection_radius2 * np.cos(np.deg2rad(angle_A_opp)),\n", - " connection_radius2 * np.sin(np.deg2rad(angle_A_opp)),\n", - " )\n", - ")\n", - "a_connection_b = [\n", - " (\n", - " connection_radius1 * np.cos(np.deg2rad(angle)),\n", - " connection_radius1 * np.sin(np.deg2rad(angle)),\n", - " )\n", - " for angle in np.arange(angle_A, angle_A + 90 + 5, 5)\n", - "]\n", - "a_connection_f = [\n", - " (\n", - " connection_radius2 * np.cos(np.deg2rad(angle)),\n", - " connection_radius2 * np.sin(np.deg2rad(angle)),\n", - " )\n", - " for angle in np.arange(angle_A + 90, angle_A + 180 + 5, 5)\n", - "]\n", + "\n", + "# coil A\n", + "coil_A_b.append(get_arc_point(angle_A, connection_radius1))\n", + "coil_A_opp_b.append(get_arc_point(angle_A_opp, connection_radius2))\n", + "a_connection_b = draw_arc(angle_A, angle_A + 90, connection_radius1)\n", + "a_connection_f = draw_arc(angle_A + 90, angle_A + 180, connection_radius2)\n", "a_connection_b.append(a_connection_f[0])\n", "\n", - "coil_B_b.append(\n", - " (\n", - " connection_radius1 * np.cos(np.deg2rad(angle_B)),\n", - " connection_radius1 * np.sin(np.deg2rad(angle_B)),\n", - " )\n", - ")\n", - "coil_B_opp_b.append(\n", - " (\n", - " connection_radius2 * np.cos(np.deg2rad(angle_B_opp)),\n", - " connection_radius2 * np.sin(np.deg2rad(angle_B_opp)),\n", - " )\n", - ")\n", - "b_connection_b = [\n", - " (\n", - " connection_radius1 * np.cos(np.deg2rad(angle)),\n", - " connection_radius1 * np.sin(np.deg2rad(angle)),\n", - " )\n", - " for angle in np.arange(angle_B, angle_B + 90 + 5, 5)\n", - "]\n", - "b_connection_f = [\n", - " (\n", - " connection_radius2 * np.cos(np.deg2rad(angle)),\n", - " connection_radius2 * np.sin(np.deg2rad(angle)),\n", - " )\n", - " for angle in np.arange(angle_B + 90, angle_B + 180 + 5, 5)\n", - "]\n", + "# coil B\n", + "coil_B_b.append(get_arc_point(angle_B, connection_radius1))\n", + "coil_B_opp_b.append(get_arc_point(angle_B_opp, connection_radius2))\n", + "b_connection_b = draw_arc(angle_B, angle_B + 90, connection_radius1)\n", + "b_connection_f = draw_arc(angle_B + 90, angle_B + 180, connection_radius2)\n", "b_connection_b.append(b_connection_f[0])\n", "\n", - "coil_C_b.append(\n", - " (\n", - " connection_radius1 * np.cos(np.deg2rad(angle_C)),\n", - " connection_radius1 * np.sin(np.deg2rad(angle_C)),\n", - " )\n", - ")\n", - "coil_C_opp_b.append(\n", - " (\n", - " connection_radius2 * np.cos(np.deg2rad(angle_C_opp)),\n", - " connection_radius2 * np.sin(np.deg2rad(angle_C_opp)),\n", - " )\n", - ")\n", - "c_connection_b = [\n", - " (\n", - " connection_radius1 * np.cos(np.deg2rad(angle)),\n", - " connection_radius1 * np.sin(np.deg2rad(angle)),\n", - " )\n", - " for angle in np.arange(angle_C, angle_C + 90 + 5, 5)\n", - "]\n", - "c_connection_f = [\n", - " (\n", - " connection_radius2 * np.cos(np.deg2rad(angle)),\n", - " connection_radius2 * np.sin(np.deg2rad(angle)),\n", - " )\n", - " for angle in np.arange(angle_C + 90, angle_C + 180 + 5, 5)\n", - "]\n", - "c_connection_b.append(c_connection_f[0])" + "# coil C\n", + "coil_C_b.append(get_arc_point(angle_C, connection_radius1))\n", + "coil_C_opp_b.append(get_arc_point(angle_C_opp, connection_radius2))\n", + "c_connection_b = draw_arc(angle_C, angle_C + 90, connection_radius1)\n", + "c_connection_f = draw_arc(angle_C + 90, angle_C + 180, connection_radius2)\n", + "c_connection_b.append(c_connection_f[0])\n", + "\n", + "vias.append(create_via(a_connection_f[0]))\n", + "vias.append(create_via(b_connection_f[0]))\n", + "vias.append(create_via(c_connection_f[0]))\n", + "\n", + "vias.append(create_via(a_connection_f[-1]))\n", + "vias.append(create_via(b_connection_f[-1]))\n", + "vias.append(create_via(c_connection_f[-1]))" ] }, { @@ -436,53 +445,7 @@ " \"viaDiameter\": VIA_DIAM,\n", " \"viaDrillDiameter\": VIA_DRILL,\n", " },\n", - " \"vias\": [\n", - " {\n", - " \"x\": COIL_CENTER_RADIUS * np.cos(np.deg2rad(angle_A)),\n", - " \"y\": COIL_CENTER_RADIUS * np.sin(np.deg2rad(angle_A)),\n", - " },\n", - " {\n", - " \"x\": COIL_CENTER_RADIUS * np.cos(np.deg2rad(angle_B)),\n", - " \"y\": COIL_CENTER_RADIUS * np.sin(np.deg2rad(angle_B)),\n", - " },\n", - " {\n", - " \"x\": COIL_CENTER_RADIUS * np.cos(np.deg2rad(angle_C)),\n", - " \"y\": COIL_CENTER_RADIUS * np.sin(np.deg2rad(angle_C)),\n", - " },\n", - " {\n", - " \"x\": COIL_CENTER_RADIUS * np.cos(np.deg2rad(angle_A_opp)),\n", - " \"y\": COIL_CENTER_RADIUS * np.sin(np.deg2rad(angle_A_opp)),\n", - " },\n", - " {\n", - " \"x\": COIL_CENTER_RADIUS * np.cos(np.deg2rad(angle_B_opp)),\n", - " \"y\": COIL_CENTER_RADIUS * np.sin(np.deg2rad(angle_B_opp)),\n", - " },\n", - " {\n", - " \"x\": COIL_CENTER_RADIUS * np.cos(np.deg2rad(angle_C_opp)),\n", - " \"y\": COIL_CENTER_RADIUS * np.sin(np.deg2rad(angle_C_opp)),\n", - " },\n", - " {\n", - " \"x\": common_connection_radius * np.cos(np.deg2rad(angle_A_opp)),\n", - " \"y\": common_connection_radius * np.sin(np.deg2rad(angle_A_opp)),\n", - " },\n", - " {\n", - " \"x\": common_connection_radius * np.cos(np.deg2rad(angle_B_opp)),\n", - " \"y\": common_connection_radius * np.sin(np.deg2rad(angle_B_opp)),\n", - " },\n", - " {\n", - " \"x\": common_connection_radius * np.cos(np.deg2rad(angle_C_opp)),\n", - " \"y\": common_connection_radius * np.sin(np.deg2rad(angle_C_opp)),\n", - " },\n", - " # coil A connections\n", - " {\"x\": a_connection_f[0][0], \"y\": a_connection_f[0][1]},\n", - " {\"x\": a_connection_f[-1][0], \"y\": a_connection_f[-1][1]},\n", - " # coil B connections\n", - " {\"x\": b_connection_f[0][0], \"y\": b_connection_f[0][1]},\n", - " {\"x\": b_connection_f[-1][0], \"y\": b_connection_f[-1][1]},\n", - " # coil C connections\n", - " {\"x\": c_connection_f[0][0], \"y\": c_connection_f[0][1]},\n", - " {\"x\": c_connection_f[-1][0], \"y\": c_connection_f[-1][1]},\n", - " ],\n", + " \"vias\": vias,\n", " \"silk\": [\n", " {\n", " \"x\": COIL_CENTER_RADIUS * np.cos(np.deg2rad(angle_A)),\n", @@ -513,6 +476,7 @@ " a_connection_f,\n", " b_connection_f,\n", " c_connection_f,\n", + " coil_B_input_connection_f,\n", " ]\n", " ],\n", " \"b\": [\n", @@ -528,6 +492,8 @@ " a_connection_b,\n", " b_connection_b,\n", " c_connection_b,\n", + " coil_A_input_connection_b,\n", + " coil_C_input_connection_b,\n", " ]\n", " ],\n", " },\n", @@ -545,21 +511,47 @@ "ax = df.plot.line(x=\"x\", y=\"y\", label=\"Coil B\", color=\"green\")\n", "ax.axis(\"equal\")\n", "\n", - "# plot all three coils on the same graph\n", - "# df = pd.DataFrame(coil_A, columns=['x', 'y'])\n", - "# ax = df.plot.line(x='x', y='y', label='Coil A', color='blue')\n", - "# ax.axis('equal')\n", - "# df = pd.DataFrame(coil_B, columns=['x', 'y'])\n", - "# df.plot.line(x='x', y='y', ax=ax, label='Coil B', color='green')\n", - "# df = pd.DataFrame(coil_C, columns=['x', 'y'])\n", - "# df.plot.line(x='x', y='y', ax=ax, label='Coil C', color='red')\n", + "# print the number of segments in each front track\n", + "for track in json_result[\"tracks\"][\"f\"]:\n", + " print(len(track))\n", "\n", - "# df = pd.DataFrame(coil_A_opposite, columns=['x', 'y'])\n", - "# df.plot.line(x='x', y='y', ax=ax, label='Coil A Opposite', color='blue')\n", - "# df = pd.DataFrame(coil_B_opposite, columns=['x', 'y'])\n", - "# df.plot.line(x='x', y='y', ax=ax, label='Coil B Opposite', color='green')\n", - "# df = pd.DataFrame(coil_C_opposite, columns=['x', 'y'])\n", - "# df.plot.line(x='x', y='y', ax=ax, label='Coil C Opposite', color='red')" + "# print the number of segments in each back track\n", + "for track in json_result[\"tracks\"][\"b\"]:\n", + " print(len(track))\n", + "\n", + "# print the total number of segments in both front and back tracks\n", + "print(sum([len(track) for track in json_result[\"tracks\"][\"f\"]]))\n", + "print(sum([len(track) for track in json_result[\"tracks\"][\"b\"]]))\n", + "\n", + "\n", + "# plot the back tracks\n", + "ax = None\n", + "for track in json_result[\"tracks\"][\"b\"]:\n", + " df = pd.DataFrame(track, columns=[\"x\", \"y\"])\n", + " ax = df.plot.line(x=\"x\", y=\"y\", color=\"blue\", ax=ax)\n", + " ax.axis(\"equal\")\n", + "\n", + "# plot the front tracks\n", + "for track in json_result[\"tracks\"][\"f\"]:\n", + " df = pd.DataFrame(track, columns=[\"x\", \"y\"])\n", + " ax = df.plot.line(x=\"x\", y=\"y\", color=\"red\", ax=ax)\n", + " ax.axis(\"equal\")\n", + "\n", + "# hide the legend\n", + "ax.legend().set_visible(False)\n", + "# make the plot bigger\n", + "ax.figure.set_size_inches(10, 10)\n", + "\n", + "# plot the vias\n", + "for via in json_result[\"vias\"]:\n", + " ax.add_patch(\n", + " plt.Circle(\n", + " (via[\"x\"], via[\"y\"]),\n", + " radius=VIA_DIAM / 2,\n", + " fill=True,\n", + " color=\"black\",\n", + " )\n", + " )" ] } ],