diff --git a/src/pyedb/grpc/edb_core/edb_data/primitives_data.py b/src/pyedb/grpc/edb_core/edb_data/primitives_data.py index cde8dc5d1e..b9e3494ebe 100644 --- a/src/pyedb/grpc/edb_core/edb_data/primitives_data.py +++ b/src/pyedb/grpc/edb_core/edb_data/primitives_data.py @@ -844,6 +844,108 @@ def create_edge_port( else: return self._app.hfss.create_edge_port_vertical(self.id, pos, name, 50, reference_layer) + @pyedb_function_handler() + def create_via_fence(self, distance, gap, padstack_name): + """Create via fences on both sides of the trace. + Parameters + ---------- + distance: str, float + Distance between via fence and trace center line. + gap: str, float + Gap between vias. + padstack_name: str + Name of the via padstack. + Returns + ------- + """ + + def getAngle(v1, v2): # pragma: no cover + v1_mag = math.sqrt(v1[0] ** 2 + v1[1] ** 2) + v2_mag = math.sqrt(v2[0] ** 2 + v2[1] ** 2) + dotsum = v1[0] * v2[0] + v1[1] * v2[1] + if v1[0] * v2[1] - v1[1] * v2[0] > 0: + scale = 1 + else: + scale = -1 + dtheta = scale * math.acos(dotsum / (v1_mag * v2_mag)) + + return dtheta + + def getLocations(line, gap): # pragma: no cover + location = [line[0]] + residual = 0 + + for n in range(len(line) - 1): + x0, y0 = line[n] + x1, y1 = line[n + 1] + length = math.sqrt((x1 - x0) ** 2 + (y1 - y0) ** 2) + dx, dy = (x1 - x0) / length, (y1 - y0) / length + x = x0 - dx * residual + y = y0 - dy * residual + length = length + residual + while length >= gap: + x += gap * dx + y += gap * dy + location.append((x, y)) + length -= gap + + residual = length + return location + + def getParalletLines(pts, distance): # pragma: no cover + leftline = [] + rightline = [] + + x0, y0 = pts[0] + x1, y1 = pts[1] + vector = (x1 - x0, y1 - y0) + orientation1 = getAngle((1, 0), vector) + + leftturn = orientation1 + math.pi / 2 + righrturn = orientation1 - math.pi / 2 + leftPt = (x0 + distance * math.cos(leftturn), y0 + distance * math.sin(leftturn)) + leftline.append(leftPt) + rightPt = (x0 + distance * math.cos(righrturn), y0 + distance * math.sin(righrturn)) + rightline.append(rightPt) + + for n in range(1, len(pts) - 1): + x0, y0 = pts[n - 1] + x1, y1 = pts[n] + x2, y2 = pts[n + 1] + + v1 = (x1 - x0, y1 - y0) + v2 = (x2 - x1, y2 - y1) + dtheta = getAngle(v1, v2) + orientation1 = getAngle((1, 0), v1) + + leftturn = orientation1 + dtheta / 2 + math.pi / 2 + righrturn = orientation1 + dtheta / 2 - math.pi / 2 + + distance2 = distance / math.sin((math.pi - dtheta) / 2) + leftPt = (x1 + distance2 * math.cos(leftturn), y1 + distance2 * math.sin(leftturn)) + leftline.append(leftPt) + rightPt = (x1 + distance2 * math.cos(righrturn), y1 + distance2 * math.sin(righrturn)) + rightline.append(rightPt) + + x0, y0 = pts[-2] + x1, y1 = pts[-1] + + vector = (x1 - x0, y1 - y0) + orientation1 = getAngle((1, 0), vector) + leftturn = orientation1 + math.pi / 2 + righrturn = orientation1 - math.pi / 2 + leftPt = (x1 + distance * math.cos(leftturn), y1 + distance * math.sin(leftturn)) + leftline.append(leftPt) + rightPt = (x1 + distance * math.cos(righrturn), y1 + distance * math.sin(righrturn)) + rightline.append(rightPt) + return leftline, rightline + + distance = self._pedb.edb_value(distance).ToDouble() + gap = self._pedb.edb_value(gap).ToDouble() + center_line = self.get_center_line() + leftline, rightline = getParalletLines(center_line, distance) + for x, y in getLocations(rightline, gap) + getLocations(leftline, gap): + self._pedb.padstacks.place([x, y], padstack_name) class EdbRectangle(EDBPrimitives): def __init__(self, raw_primitive, core_app): diff --git a/src/pyedb/legacy/edb_core/edb_data/primitives_data.py b/src/pyedb/legacy/edb_core/edb_data/primitives_data.py index 81c6d34e7d..c33b03854f 100644 --- a/src/pyedb/legacy/edb_core/edb_data/primitives_data.py +++ b/src/pyedb/legacy/edb_core/edb_data/primitives_data.py @@ -819,7 +819,7 @@ def create_edge_port( Examples -------- - >>> edbapp = legacy.Edb("myproject.aedb") + >>> edbapp = pyedb.legacy.Edb("myproject.aedb") >>> sig = appedb.modeler.create_trace([[0, 0], ["9mm", 0]], "TOP", "1mm", "SIG", "Flat", "Flat") >>> sig.create_edge_port("pcb_port", "end", "Wave", None, 8, 8) @@ -834,6 +834,108 @@ def create_edge_port( else: return self._app.hfss.create_edge_port_vertical(self.id, pos, name, 50, reference_layer) + @pyedb_function_handler() + def create_via_fence(self, distance, gap, padstack_name): + """Create via fences on both sides of the trace. + Parameters + ---------- + distance: str, float + Distance between via fence and trace center line. + gap: str, float + Gap between vias. + padstack_name: str + Name of the via padstack. + Returns + ------- + """ + + def getAngle(v1, v2): # pragma: no cover + v1_mag = math.sqrt(v1[0] ** 2 + v1[1] ** 2) + v2_mag = math.sqrt(v2[0] ** 2 + v2[1] ** 2) + dotsum = v1[0] * v2[0] + v1[1] * v2[1] + if v1[0] * v2[1] - v1[1] * v2[0] > 0: + scale = 1 + else: + scale = -1 + dtheta = scale * math.acos(dotsum / (v1_mag * v2_mag)) + + return dtheta + + def getLocations(line, gap): # pragma: no cover + location = [line[0]] + residual = 0 + + for n in range(len(line) - 1): + x0, y0 = line[n] + x1, y1 = line[n + 1] + length = math.sqrt((x1 - x0) ** 2 + (y1 - y0) ** 2) + dx, dy = (x1 - x0) / length, (y1 - y0) / length + x = x0 - dx * residual + y = y0 - dy * residual + length = length + residual + while length >= gap: + x += gap * dx + y += gap * dy + location.append((x, y)) + length -= gap + + residual = length + return location + + def getParalletLines(pts, distance): # pragma: no cover + leftline = [] + rightline = [] + + x0, y0 = pts[0] + x1, y1 = pts[1] + vector = (x1 - x0, y1 - y0) + orientation1 = getAngle((1, 0), vector) + + leftturn = orientation1 + math.pi / 2 + righrturn = orientation1 - math.pi / 2 + leftPt = (x0 + distance * math.cos(leftturn), y0 + distance * math.sin(leftturn)) + leftline.append(leftPt) + rightPt = (x0 + distance * math.cos(righrturn), y0 + distance * math.sin(righrturn)) + rightline.append(rightPt) + + for n in range(1, len(pts) - 1): + x0, y0 = pts[n - 1] + x1, y1 = pts[n] + x2, y2 = pts[n + 1] + + v1 = (x1 - x0, y1 - y0) + v2 = (x2 - x1, y2 - y1) + dtheta = getAngle(v1, v2) + orientation1 = getAngle((1, 0), v1) + + leftturn = orientation1 + dtheta / 2 + math.pi / 2 + righrturn = orientation1 + dtheta / 2 - math.pi / 2 + + distance2 = distance / math.sin((math.pi - dtheta) / 2) + leftPt = (x1 + distance2 * math.cos(leftturn), y1 + distance2 * math.sin(leftturn)) + leftline.append(leftPt) + rightPt = (x1 + distance2 * math.cos(righrturn), y1 + distance2 * math.sin(righrturn)) + rightline.append(rightPt) + + x0, y0 = pts[-2] + x1, y1 = pts[-1] + + vector = (x1 - x0, y1 - y0) + orientation1 = getAngle((1, 0), vector) + leftturn = orientation1 + math.pi / 2 + righrturn = orientation1 - math.pi / 2 + leftPt = (x1 + distance * math.cos(leftturn), y1 + distance * math.sin(leftturn)) + leftline.append(leftPt) + rightPt = (x1 + distance * math.cos(righrturn), y1 + distance * math.sin(righrturn)) + rightline.append(rightPt) + return leftline, rightline + + distance = self._pedb.edb_value(distance).ToDouble() + gap = self._pedb.edb_value(gap).ToDouble() + center_line = self.get_center_line() + leftline, rightline = getParalletLines(center_line, distance) + for x, y in getLocations(rightline, gap) + getLocations(leftline, gap): + self._pedb.padstacks.place([x, y], padstack_name) class EdbRectangle(EDBPrimitives, RectangleDotNet): def __init__(self, raw_primitive, core_app): diff --git a/tests/grpc/system/test_edb.py b/tests/grpc/system/test_edb.py index 9186ddf646..5f86373064 100644 --- a/tests/grpc/system/test_edb.py +++ b/tests/grpc/system/test_edb.py @@ -1267,6 +1267,11 @@ def test_create_padstack_instance(self): assert pad_instance3.dcir_equipotential_region pad_instance3.dcir_equipotential_region = False assert not pad_instance3.dcir_equipotential_region + + trace = edb.modeler.create_trace([[0, 0], [0, 10e-3]], "1_Top", "0.1mm", "trace_with_via_fence") + edb.padstacks.create_padstack("via_0") + trace.create_via_fence("1mm", "1mm", "via_0") + edb.close() def test_assign_hfss_extent_non_multiple_with_simconfig(self): diff --git a/tests/legacy/system/test_edb.py b/tests/legacy/system/test_edb.py index b3dd1ad86b..29d06e6574 100644 --- a/tests/legacy/system/test_edb.py +++ b/tests/legacy/system/test_edb.py @@ -1232,6 +1232,11 @@ def test_create_padstack_instance(self): assert pad_instance3.dcir_equipotential_region pad_instance3.dcir_equipotential_region = False assert not pad_instance3.dcir_equipotential_region + + trace = edb.modeler.create_trace([[0, 0], [0, 10e-3]], "1_Top", "0.1mm", "trace_with_via_fence") + edb.padstacks.create_padstack("via_0") + trace.create_via_fence("1mm", "1mm", "via_0") + edb.close() def test_assign_hfss_extent_non_multiple_with_simconfig(self):