///|
///|
struct VerticalPostWedge {
post : SceneNode
top_p0 : Vec2
top_p1 : Vec2
}
///|
struct VerticalWedge {
wedge : SceneNode
}
///|
struct VerticalCylinder {
cylinder : SceneNode
}
///|
/// solve_for_t solves the provided function for `t` where `0 <= t <= 1`.
///
/// If the solution can't be found, returns:
/// - `2` when `d > f(1)`
/// - `-1` when `d < f(0)`
fn solve_for_t(d : Double, f : (Double) -> Double) -> Double {
let d1 = f(1)
if d > d1 {
return 2
}
let d0 = f(0)
if d < d0 {
return -1
}
let mut t = 0.5
let mut dt = 0.25
for ;; {
let err = f(t) - d
if err.abs() < 0.005 {
return t
}
if err > 0 {
t = t - dt
} else {
t = t + dt
}
dt = dt / 2
}
}
///|
/// vertex_at returns a 2D point given polar coordinates around `center`.
fn vertex_at(center~ : Vec2, radius~ : Double, angle~ : Double) -> Vec2 {
(center.0 + radius * @math.cos(angle), center.1 + radius * @math.sin(angle))
}
///|
/// center_of_face returns the centroid of a polygon (simple average of vertices).
fn center_of_face(points : Array[Vec2]) -> Vec2 {
let mut sum = (0.0, 0.0)
for pt in points {
sum = (sum.0 + pt.0, sum.1 + pt.1)
}
let count = points.length().to_double()
(sum.0 / count, sum.1 / count)
}
///|
/// make_vertical_post_wedge builds a rectangular "post" profile at `inner_radius` and extrudes up.
///
/// Returns the SceneNode and the two top-edge points used to connect subsequent geometry.
fn make_vertical_post_wedge(
pos~ : Vec3,
inner_radius~ : Double,
start_angle~ : Double,
normal_angle~ : Double,
post_height~ : Double,
wire_width~ : Double,
color~ : @cad.Rgb = @cad.Rgb::red(),
) -> VerticalPostWedge {
let sx4 = pos.0 + inner_radius * @math.cos(start_angle)
let sy4 = pos.1 + inner_radius * @math.sin(start_angle)
let sx1 = sx4 + wire_width * @math.cos(normal_angle)
let sy1 = sy4 + wire_width * @math.sin(normal_angle)
let sx2 = pos.0 + (inner_radius + wire_width) * @math.cos(start_angle)
let sy2 = pos.1 + (inner_radius + wire_width) * @math.sin(start_angle)
let sx3 = sx2 + wire_width * @math.cos(normal_angle)
let sy3 = sy2 + wire_width * @math.sin(normal_angle)
let points = [(sx1, sy1), (sx4, sy4), (sx2, sy2), (sx3, sy3)]
let post = @cad.ExtrudedProfile::new(points, post_height)
.with_name("bfem:cage:post-wedge:r\{inner_radius}:a\{start_angle}")
.with_color(color)
.translate(z=pos.2)
{ post, top_p0: (sx2, sy2), top_p1: (sx3, sy3), }
}
///|
/// make_wedge generates an annular wedge and extrudes upward.
fn make_wedge(
pos~ : Vec3,
inner_radius~ : Double,
start_angle~ : Double,
end_angle~ : Double,
radial_length~ : Double,
height~ : Double,
color~ : @cad.Rgb = @cad.Rgb::red(),
name~ : String = "vertical-wedge-r\{inner_radius}-a\{start_angle}",
trim_last_edge~ : Bool = false,
wire_gap~ : Double = 0.0, // only used if trim_last_edge is true
) -> VerticalWedge {
let mut sx4 = pos.0 + inner_radius * @math.cos(start_angle)
let mut sy4 = pos.1 + inner_radius * @math.sin(start_angle)
if trim_last_edge {
let trim_angle = start_angle + @math.asin(wire_gap / inner_radius)
sx4 = pos.0 + inner_radius * @math.cos(trim_angle)
sy4 = pos.1 + inner_radius * @math.sin(trim_angle)
}
let sx2 = pos.0 + (inner_radius + radial_length) * @math.cos(start_angle)
let sy2 = pos.1 + (inner_radius + radial_length) * @math.sin(start_angle)
let sx5 = pos.0 + inner_radius * @math.cos(end_angle)
let sy5 = pos.1 + inner_radius * @math.sin(end_angle)
let sx6 = pos.0 + (inner_radius + radial_length) * @math.cos(end_angle)
let sy6 = pos.1 + (inner_radius + radial_length) * @math.sin(end_angle)
let points = [(sx5, sy5), (sx6, sy6), (sx2, sy2), (sx4, sy4)]
let wedge = @cad.ExtrudedProfile::new(points, height)
.with_name(name)
.with_color(color)
.translate(z=pos.2)
{ wedge, } // points }
}
///|
/// make_cylinder makes an n-sided vertical "cylinder" by extruding a polygonal circle.
fn make_cylinder(
pos~ : Vec3,
radius~ : Double,
segments~ : Int32,
height~ : Double,
color~ : @cad.Rgb = @cad.Rgb::red(),
name~ : String = "bfem:cage:cylinder",
) -> VerticalCylinder {
let points = []
for i in 1..<=segments {
let angle = -2.0 * @math.PI * i.to_double() / segments.to_double() // face downward
let x = pos.0 + radius * @math.cos(angle)
let y = pos.1 + radius * @math.sin(angle)
points.push((x, y))
}
let cylinder = @cad.ExtrudedProfile::new(points, height)
.with_name(name)
.with_color(color)
.translate(z=pos.2)
{ cylinder, }
}
///|
fn push_arc_point(
center : Vec2,
start_angle : Double,
angle_delta : Double,
radius : Double,
j : Int32,
points : Array[Vec2],
) -> Unit {
let angle = start_angle + j.to_double() * angle_delta
points.push(vertex_at(center~, radius~, angle~))
}
///|
struct CageContext {
center : Vec2
npairs : Int32
num_pairs : Double
segments_per_rail : Int32
lower_z : Double
coil_height : Double
extrude_height : Double
rail_angle_delta : Double
inner_radius : Double
outer_radius : Double
inner_dtheta : Double
outer_dtheta : Double
delta_z : Double
max_axial_connector_top_zs : Double
}
///|
fn make_cage_context(size : Vec3, params : Params) -> CageContext {
let npairs = params.num_pairs
let num_pairs = npairs.to_double()
let mut segments_per_rail = @math.ceil(
0.5 * params.num_segs.to_double() / num_pairs,
).to_int()
if segments_per_rail < 2 {
segments_per_rail = 2
}
let lower_z = params.pos.2 - params.wire_width / 2
let coil_height = 2.0 *
params.vert_turns *
(params.wire_width + params.wire_gap) +
params.wire_width
let extrude_height = coil_height + params.wire_gap + params.wire_width
let rail_angle_delta = @math.PI / num_pairs
let inner_radius = size.0 + params.wire_width / 2 + params.wire_gap
let outer_radius = inner_radius + params.radial_thickness
let inner_dtheta = @math.asin(params.wire_gap / (2 * inner_radius))
let outer_dtheta = @math.asin(params.wire_gap / (2 * outer_radius))
let delta_z = (params.wire_gap + params.wire_width) / (num_pairs - 1)
let center = (params.pos.0, params.pos.1)
let max_axial_connector_top_zs = extrude_height +
lower_z +
(num_pairs - 1) * delta_z
{
center,
npairs,
num_pairs,
segments_per_rail,
lower_z,
coil_height,
extrude_height,
rail_angle_delta,
inner_radius,
outer_radius,
inner_dtheta,
outer_dtheta,
delta_z,
max_axial_connector_top_zs,
}
}
///|
fn gen_inner_arc_points(
ctx : CageContext,
inner_start_angle : Double,
inner_angle_delta : Double,
) -> Array[Vec2] {
let points = Array(capacity=ctx.segments_per_rail + 1)
for j in 0..<=ctx.segments_per_rail {
push_arc_point(
ctx.center,
inner_start_angle,
inner_angle_delta,
ctx.inner_radius,
j,
points,
)
}
points
}
///|
fn append_axial_connectors(
ctx : CageContext,
params : Params,
axial_connector_top_zs : Array[Double],
wire_nodes : Array[@cad.SceneNode],
) -> Unit {
for i in 0..<(2 * ctx.npairs) {
let ni = i + 1
let idx = i
let i = i.to_double()
let inner_start_angle = i * ctx.rail_angle_delta
let inner_end_angle = (i + 1) * ctx.rail_angle_delta - 2 * ctx.inner_dtheta
let inner_angle_delta = (inner_end_angle - inner_start_angle) /
ctx.segments_per_rail.to_double()
//
let outer_start_angle = i * ctx.rail_angle_delta
let outer_end_angle = (i + 1) * ctx.rail_angle_delta - 2 * ctx.outer_dtheta
let outer_angle_delta = (outer_end_angle - outer_start_angle) /
ctx.segments_per_rail.to_double()
//
if inner_angle_delta <= 0 || outer_angle_delta <= 0 {
abort("wire_gap too large with given num_pairs and size")
}
//
let z = ctx.lower_z + (ctx.num_pairs - i) % ctx.num_pairs * ctx.delta_z
axial_connector_top_zs.push(z + ctx.extrude_height)
//
let polygon = Array(capacity=ctx.segments_per_rail * 2 + 2)
// inner points:
for j in 0..<=ctx.segments_per_rail {
push_arc_point(
ctx.center,
inner_start_angle,
inner_angle_delta,
ctx.inner_radius,
j,
polygon,
)
}
// outer points:
for j in 0..<=ctx.segments_per_rail {
push_arc_point(
ctx.center,
outer_start_angle,
outer_angle_delta,
ctx.outer_radius,
ctx.segments_per_rail - j,
polygon,
)
}
//
if i == 0 {
// final output connector of coil 1 (at outer edge)
let node = @cad.ExtrudedProfile::new(
polygon,
ctx.extrude_height +
1.5 * params.wire_width +
params.front_thickness +
params.back_thickness,
)
.with_name("bfem:cage:axial:outlet-red")
.translate(z=ctx.lower_z - params.front_thickness)
wire_nodes.push(node.with_color(@cad.Rgb::red()))
} else {
let node = @cad.ExtrudedProfile::new(
polygon,
ctx.max_axial_connector_top_zs -
ctx.lower_z +
params.front_thickness +
params.back_thickness,
)
.with_name("bfem:cage:axial:rail-\{idx}")
.translate(z=ctx.lower_z - params.front_thickness)
let color = if ni <= ctx.npairs {
@cad.Rgb::green()
} else if ni == ctx.npairs + 1 {
@cad.Rgb::orange() // half-way transition from even to odd coils
} else {
@cad.Rgb::red()
}
wire_nodes.push(node.with_color(color))
}
}
}
///|
fn append_helix_connectors_and_exit_wires(
ctx : CageContext,
params : Params,
rotations : Array[Double],
line_lengths : Array[Double],
zs : Array[Double],
red_cage_wire_support_info : Array[SupportInfo],
green_cage_wire_support_info : Array[SupportInfo],
wire_nodes : Array[@cad.SceneNode],
) -> Unit {
//
let gen_points = fn(inner_start_angle, inner_angle_delta) {
gen_inner_arc_points(ctx, inner_start_angle, inner_angle_delta)
}
// now generate both front/bottom and back/top connectors to the helices
for i in 0.. 1 {
let end_angle = rotations[ni - 2] -
@math.asin(params.wire_gap / connector_radius)
let start_angle = if ni < ctx.npairs {
rotations[ni - 1] - 0.1 // temporary debugging hack
} else {
rotations[ni - 1]
}
red_cage_wire_support_info.push({
inner_radius: connector_radius,
start_angle,
end_angle,
})
}
// this connects the bottom/front helix to the axial connector
let bottom_thickness = z - ctx.lower_z + params.front_thickness
let node = @cad.ExtrudedProfile::new(polygon, bottom_thickness)
.with_name("bfem:cage:front-bottom:red:pair-\{ni}")
.translate(z=z - bottom_thickness)
wire_nodes.push(node.with_color(@cad.Rgb::red()))
// add a vertical post to the back/top of the design for connecting the coils
// this is the back/top of the design
// these are the odd coils (1, 3, 5, etc.)
let mut top_angle = rotation
if i > 0 {
// half wire-width at outer radius in radians
let top_hww = 0.5 * @math.asin(params.wire_width / (2 * ctx.outer_radius))
top_angle = rotation - ctx.rail_angle_delta / 2 + top_hww
}
let top_helix_z = z + ctx.coil_height - 2 * params.wire_width
let sx4 = params.pos.0 + connector_radius * @math.cos(top_angle)
let sy4 = params.pos.1 + connector_radius * @math.sin(top_angle)
let sx1 = sx4 + params.wire_width * @math.cos(top_angle - @math.PI / 2)
let sy1 = sy4 + params.wire_width * @math.sin(top_angle - @math.PI / 2)
let sx2 = params.pos.0 +
(connector_radius + params.wire_width) * @math.cos(top_angle)
let sy2 = params.pos.1 +
(connector_radius + params.wire_width) * @math.sin(top_angle)
let sx3 = sx2 + params.wire_width * @math.cos(top_angle - @math.PI / 2)
let sy3 = sy2 + params.wire_width * @math.sin(top_angle - @math.PI / 2)
// this is the "up" part of the "up-and-over" connector on the back/top of the design for the odd coils:
// note the the "up" part for the last odd coil is simply the wire width.
let polygon = [(sx1, sy1), (sx4, sy4), (sx2, sy2), (sx3, sy3)]
let over_height = ctx.max_axial_connector_top_zs -
top_helix_z +
params.back_thickness // flat top/back (connector side)
let node = @cad.ExtrudedProfile::new(polygon, over_height)
.with_name("bfem:cage:back-top:red:post:pair-\{ni}")
.translate(z=top_helix_z)
wire_nodes.push(node.with_color(@cad.Rgb::red()))
// this is the "over" part of the "up-and-over" connector on the back/top of the design for the odd coils:
let over_start_angle = -(i + 1) * ctx.rail_angle_delta
let polygon = gen_points(over_start_angle, inner_angle_delta)
polygon.push((sx2, sy2))
polygon.push((sx3, sy3))
let top_thickness = if i == ctx.num_pairs {
over_height
} else {
over_height - params.wire_width - params.wire_gap
}
let node = @cad.ExtrudedProfile::new(
polygon,
top_thickness - params.wire_gap,
)
.with_name("bfem:cage:back-top:red:over:pair-\{ni}")
.translate(z=top_helix_z + over_height - top_thickness + params.wire_gap)
wire_nodes.push(node.with_color(@cad.Rgb::red()))
// for all but the first coil, the helix needs to be connected to the shifted back/top "up-and-over" connector
if i > 0 {
let sx5 = params.pos.0 + connector_radius * @math.cos(rotation)
let sy5 = params.pos.1 + connector_radius * @math.sin(rotation)
let sx6 = params.pos.0 +
(connector_radius + params.wire_width) * @math.cos(rotation)
let sy6 = params.pos.1 +
(connector_radius + params.wire_width) * @math.sin(rotation)
let polygon = [(sx5, sy5), (sx6, sy6), (sx2, sy2), (sx4, sy4)]
let node = @cad.ExtrudedProfile::new(polygon, params.wire_width)
.with_name("bfem:cage:back-top:red:helix-link:pair-\{ni}")
.translate(z=top_helix_z)
wire_nodes.push(node.with_color(@cad.Rgb::red()))
}
// second connection for pair directly opposite first connection
// this is the front/bottom of the design
let polygon = gen_points(inner_start_angle + @math.PI, inner_angle_delta)
let sx = params.pos.0 + connector_radius * @math.cos(rotation + @math.PI)
let sy = params.pos.1 + connector_radius * @math.sin(rotation + @math.PI)
let tx = params.pos.0 + connector_radius * @math.cos(t_angle + @math.PI)
let ty = params.pos.1 + connector_radius * @math.sin(t_angle + @math.PI)
polygon.push((tx, ty))
polygon.push((sx, sy))
let node = @cad.ExtrudedProfile::new(polygon, bottom_thickness)
.with_name("bfem:cage:front-bottom:green:pair-\{ni}")
.translate(z=z - bottom_thickness)
wire_nodes.push(node.with_color(@cad.Rgb::green()))
// Save this information so that the cage can be `tolerance` away from these wires:
if ni > 1 {
let end_angle = rotations[ni - 2] -
@math.asin(params.wire_gap / connector_radius) +
@math.PI
let start_angle = @math.PI +
(if ni < ctx.npairs {
rotations[ni - 1] - 0.1 // temporary debugging hack
} else {
rotations[ni - 1]
})
green_cage_wire_support_info.push({
inner_radius: connector_radius,
start_angle,
end_angle,
})
}
// add a vertical post to the back/top of the design for connecting the coils
// this is the back/top of the design
// these are the even coils (2, 4, 6, etc.)
if ni >= ctx.npairs {
// final output connector of last coil
top_angle = rotation - connector_dtheta
}
let top_helix_z = z + ctx.coil_height - 2 * params.wire_width
let sx4 = params.pos.0 + connector_radius * @math.cos(top_angle + @math.PI)
let sy4 = params.pos.1 + connector_radius * @math.sin(top_angle + @math.PI)
let mut sx1 = sx4 + params.wire_width * @math.cos(top_angle + @math.PI / 2)
let mut sy1 = sy4 + params.wire_width * @math.sin(top_angle + @math.PI / 2)
let sx2 = params.pos.0 +
(connector_radius + params.wire_width) * @math.cos(top_angle + @math.PI)
let sy2 = params.pos.1 +
(connector_radius + params.wire_width) * @math.sin(top_angle + @math.PI)
let mut sx3 = sx2 + params.wire_width * @math.cos(top_angle + @math.PI / 2)
let mut sy3 = sy2 + params.wire_width * @math.sin(top_angle + @math.PI / 2)
if ni >= ctx.npairs { // final output connector of last coil
let angle_diff = ctx.rail_angle_delta - 2 * ctx.outer_dtheta
sx1 = params.pos.0 +
connector_radius * @math.cos(top_angle + @math.PI - angle_diff)
sy1 = params.pos.1 +
connector_radius * @math.sin(top_angle + @math.PI - angle_diff)
sx3 = params.pos.0 +
(connector_radius + params.wire_width) *
@math.cos(top_angle + @math.PI - angle_diff)
sy3 = params.pos.1 +
(connector_radius + params.wire_width) *
@math.sin(top_angle + @math.PI - angle_diff)
}
let polygon = [(sx1, sy1), (sx4, sy4), (sx2, sy2), (sx3, sy3)]
//
if ni >= ctx.npairs { // final output connector of last coil (closer to center)
let node = @cad.ExtrudedProfile::new(polygon, params.wire_width)
.with_name("bfem:cage:back-top:green:outlet-outer")
.translate(z=top_helix_z)
wire_nodes.push(node.with_color(@cad.Rgb::green()))
// generate inner final output connector of last coil with same radial thickness as other connector
let angle_diff = ctx.rail_angle_delta - 2 * ctx.outer_dtheta
let ew_inner_radius = connector_radius -
0.6 * params.exit_wire_separation -
params.exit_wire_diameter
let s2 = vertex_at(
center=(params.pos.0, params.pos.1),
radius=ew_inner_radius,
angle=top_angle + @math.PI,
)
let s3 = vertex_at(
center=(params.pos.0, params.pos.1),
radius=ew_inner_radius,
angle=top_angle + @math.PI - angle_diff,
)
let polygon = [(sx4, sy4), (sx1, sy1), s3, s2]
let flat_top_height = 2.5 * params.wire_width + params.back_thickness
let node = @cad.ExtrudedProfile::new(polygon, flat_top_height)
.with_name("bfem:cage:back-top:green:outlet-inner")
.translate(z=top_helix_z)
wire_nodes.push(node.with_color(@cad.Rgb::green()))
// now make the exit wire cylinders
if !params.nowires {
let inner_wire_post_center = center_of_face(polygon)
let mut inner_wire_pos = inner_wire_post_center
let outer_wire_radius = (ctx.inner_radius + ctx.outer_radius) / 2
let outer_wire_dtheta = @math.asin(
params.wire_gap / (2 * outer_wire_radius),
)
let outer_wire_start_angle = 0.0
let outer_wire_end_angle = ctx.rail_angle_delta - 2 * outer_wire_dtheta
let outer_wire_post_center = vertex_at(
center=(params.pos.0, params.pos.1),
radius=outer_wire_radius,
angle=(outer_wire_end_angle - outer_wire_start_angle) / 2,
)
let mut outer_wire_pos = outer_wire_post_center
let centers_distance = vector_length(
outer_wire_post_center, inner_wire_post_center,
)
if centers_distance < params.exit_wire_separation {
let pa = inner_wire_post_center
let va = (polygon[3].0, polygon[3].1)
let pb = outer_wire_post_center
let vb = vertex_at(
center=(params.pos.0, params.pos.1),
radius=(ctx.inner_radius + ctx.outer_radius) / 2,
angle=outer_wire_start_angle,
)
//
let f1 = fn(t) {
(pa.0 + t * (va.0 - pa.0), pa.1 + t * (va.1 - pa.1))
}
let f2 = fn(t) {
(pb.0 + t * (vb.0 - pb.0), pb.1 + t * (vb.1 - pb.1))
}
let d = params.exit_wire_separation
let t = solve_for_t(d, fn(t) { vector_length(f1(t), f2(t)) })
inner_wire_pos = f1(t)
outer_wire_pos = f2(t)
}
let inner_exit_wire = make_cylinder(
pos=(
inner_wire_pos.0,
inner_wire_pos.1,
top_helix_z + flat_top_height,
),
radius=params.exit_wire_diameter / 2,
segments=12,
height=params.connector_length,
name="bfem:exit-wire:green",
)
wire_nodes.push(inner_exit_wire.cylinder.with_color(@cad.Rgb::green()))
let outer_exit_wire = make_cylinder(
pos=(
outer_wire_pos.0,
outer_wire_pos.1,
top_helix_z + flat_top_height,
),
radius=params.exit_wire_diameter / 2,
segments=12,
height=params.connector_length,
name="bfem:exit-wire:red",
)
wire_nodes.push(outer_exit_wire.cylinder.with_color(@cad.Rgb::red()))
}
}
//
if ni < ctx.npairs {
// this is the "up" part of the "up-and-over" connector on the back/top of the design for the even coils:
let post = make_vertical_post_wedge(
pos=(params.pos.0, params.pos.1, params.pos.2 + top_helix_z),
inner_radius=connector_radius,
start_angle=top_angle + @math.PI,
normal_angle=top_angle + @math.PI / 2,
post_height=over_height,
wire_width=params.wire_width,
)
wire_nodes.push(post.post.with_color(@cad.Rgb::green()))
// this is the "over" part of the "up-and-over" connector on the back/top of the design for the even coils:
let over_start_angle = -(i + 1) * ctx.rail_angle_delta + @math.PI
let polygon = gen_points(over_start_angle, inner_angle_delta)
polygon.push(post.top_p0)
polygon.push(post.top_p1)
let node = @cad.ExtrudedProfile::new(
polygon,
top_thickness - params.wire_gap,
)
.with_name("bfem:cage:back-top:green:over:pair-\{ni}")
.translate(
z=top_helix_z + over_height - top_thickness + params.wire_gap,
)
wire_nodes.push(node.with_color(@cad.Rgb::green()))
}
// for all but the first connector, the helix needs to be connected to the shifted connector
if ni > 1 {
let wire = make_wedge(
pos=(params.pos.0, params.pos.1, top_helix_z),
inner_radius=connector_radius,
start_angle=top_angle + @math.PI,
end_angle=rotation + @math.PI,
radial_length=params.wire_width,
height=params.wire_width,
name="bfem:cage:back-top:green:helix-link:pair-\{ni}",
)
wire_nodes.push(wire.wedge.with_color(@cad.Rgb::green()))
}
}
}
///|
fn append_supports(
ctx : CageContext,
size : Vec3,
params : Params,
red_cage_wire_support_info : Array[SupportInfo],
green_cage_wire_support_info : Array[SupportInfo],
support_nodes : Array[@cad.SceneNode],
) -> Unit {
// Finally, add support structures
let shaft_pos = (
params.pos.0,
params.pos.1,
params.pos.2 - params.wire_width / 2 - params.front_thickness,
)
let shaft_radius = params.inner_diam / 2 - params.tolerance
let front_inner_shaft = make_cylinder(
pos=shaft_pos,
radius=shaft_radius,
segments=params.num_segs,
// height=coil_height + params.front_thickness + params.back_thickness, // flush with top coil
height=ctx.extrude_height +
1.5 * params.wire_width +
params.front_thickness +
params.back_thickness +
params.connector_length,
name="bfem:support:inner-shaft",
)
support_nodes.push(front_inner_shaft.cylinder.with_color(@cad.Rgb::blue()))
//
for idx, info in red_cage_wire_support_info {
let height = params.wire_width -
info.end_angle / @math.PI * size.2 / 2 -
params.tolerance
let trim_last_edge = idx == red_cage_wire_support_info.length() - 1
add_support(
idx,
"red",
info,
shaft_pos~,
shaft_radius~,
height~,
params~,
support_nodes~,
trim_last_edge~,
)
}
for idx, info in green_cage_wire_support_info {
let height = params.wire_width +
(@math.PI - info.end_angle) / @math.PI * size.2 / 2 -
params.tolerance
let trim_last_edge = idx == green_cage_wire_support_info.length() - 1
add_support(
idx,
"green",
info,
shaft_pos~,
shaft_radius~,
height~,
params~,
support_nodes~,
trim_last_edge~,
)
}
}
///|
/// make_bfem_cage makes a wire cage around the bifilar electromagnet and wires up the coils.
/// It also returns an optional support structure to hold the coils.
fn make_bfem_cage(
size~ : Vec3,
params~ : Params,
) -> (Array[@cad.SceneNode], Array[@cad.SceneNode]) {
let ctx = make_cage_context(size, params)
let red_cage_wire_support_info = Array(capacity=ctx.npairs)
let green_cage_wire_support_info = Array(capacity=ctx.npairs)
let axial_connector_top_zs = Array(capacity=2 * ctx.npairs)
let wire_nodes = []
let support_nodes = []
// first, generate the long axial connectors
append_axial_connectors(ctx, params, axial_connector_top_zs, wire_nodes)
// Pre-calculate important values for all coil connectors
// rotations represents the angle around the coil where the connector starts
let rotations = Array(capacity=ctx.npairs)
let line_lengths = Array(capacity=ctx.npairs)
let zs = Array(capacity=ctx.npairs)
for i in 0.. Unit {
let radial_length = info.inner_radius - shaft_radius - params.tolerance
let support = make_wedge(
pos=shaft_pos,
inner_radius=shaft_radius,
start_angle=info.start_angle,
end_angle=info.end_angle,
radial_length~,
height~,
name="bfem:cage:support:\{name}:\{idx + 1}",
trim_last_edge~,
wire_gap=params.wire_gap,
)
support_nodes.push(support.wedge.with_color(@cad.Rgb::blue()))
}
///|
fn vector_length(a : Vec2, b : Vec2) -> Double {
let dx = b.0 - a.0
let dy = b.1 - a.1
(dx * dx + dy * dy).sqrt()
}