worked on sim engine quite a bit
This commit is contained in:
parent
ffd04c1905
commit
880a37ace7
130
assets/femm.svg
130
assets/femm.svg
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 983 B After Width: | Height: | Size: 21 KiB |
|
|
@ -15,3 +15,6 @@ femm-doc-mag = { workspace = true }
|
|||
iced = { version = "0.14", features = ["canvas", "svg", "tokio"] }
|
||||
rfd = "0.17"
|
||||
meval = "0.2"
|
||||
tokio = { version = "1", features = ["rt", "rt-multi-thread"] }
|
||||
libc = "0.2"
|
||||
tiny-skia = "0.11"
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use iced::widget::canvas::{
|
|||
};
|
||||
use iced::{Color, Element, Length, Point, Radians, Rectangle, Renderer, Theme, Vector, mouse};
|
||||
|
||||
const PADDING_PX: f32 = 24.0;
|
||||
const PADDING_PX: f32 = 48.0;
|
||||
const NODE_RADIUS: f32 = 3.0;
|
||||
const STROKE_WIDTH: f32 = 1.2;
|
||||
const LABEL_TICK_PX: f32 = 6.0;
|
||||
|
|
@ -29,9 +29,9 @@ const FLUX_LINE_COLOR: Color = Color::from_rgba(0.0, 0.0, 0.0, 0.85);
|
|||
const FLUX_LINE_STROKE: f32 = 0.6;
|
||||
const BAND_COUNT: usize = 20;
|
||||
const FLUX_LINE_COUNT: usize = 19;
|
||||
const GRID_COLOR: Color = Color::from_rgba(0.5, 0.5, 0.5, 0.25);
|
||||
const GRID_STROKE: f32 = 0.5;
|
||||
const GRID_MIN_PX_SPACING: f32 = 4.0;
|
||||
const GRID_COLOR: Color = Color::from_rgba(0.55, 0.65, 0.85, 0.55);
|
||||
const GRID_STROKE: f32 = 0.6;
|
||||
const GRID_MIN_PX_SPACING: f32 = 5.0;
|
||||
|
||||
/// field-plot mode applied on top of the FE solution.
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,423 @@
|
|||
//! offline RGBA frame renderer piping to an ffmpeg VP9 encoder.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::PathBuf;
|
||||
use std::process::{Command, Stdio};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread;
|
||||
|
||||
use femm_doc_mag::FemmDoc;
|
||||
use femm_doc_mag::ans::MagSolution;
|
||||
use tiny_skia::{Color, FillRule, Paint, PathBuilder, Pixmap, Stroke, Transform};
|
||||
|
||||
use crate::kinematic::{self, Track};
|
||||
|
||||
const BAND_COUNT: usize = 20;
|
||||
const FLUX_LINE_COUNT: usize = 19;
|
||||
const PADDING_PX: f64 = 48.0;
|
||||
|
||||
pub struct ExportInput {
|
||||
pub dest: PathBuf,
|
||||
pub base_doc: FemmDoc,
|
||||
pub tracks: Vec<Track>,
|
||||
pub dt: f64,
|
||||
pub buffer_size: usize,
|
||||
pub fps: f64,
|
||||
pub lut: HashMap<i64, MagSolution>,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub contour: bool,
|
||||
pub crf: u32,
|
||||
pub progress: Arc<Mutex<ExportProgress>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ExportProgress {
|
||||
pub phase: ExportPhase,
|
||||
pub frames_done: usize,
|
||||
pub frames_total: usize,
|
||||
pub bytes_sent: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||
pub enum ExportPhase {
|
||||
#[default]
|
||||
Idle,
|
||||
Rendering,
|
||||
Finalizing,
|
||||
Done,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ExportReport {
|
||||
pub written: usize,
|
||||
pub dest: PathBuf,
|
||||
}
|
||||
|
||||
/// renders each buffered frame to a pixmap and pipes the raw RGBA bytes to a libvpx-vp9 ffmpeg subprocess.
|
||||
pub fn export_webm(input: ExportInput) -> Result<ExportReport, String> {
|
||||
if input.buffer_size == 0 {
|
||||
return Err(String::from("buffer is empty - set a buffer size and run the sim first"));
|
||||
}
|
||||
for f in 0..(input.buffer_size as i64) {
|
||||
if !input.lut.contains_key(&f) {
|
||||
return Err(format!("frame {f} of {} not cached - fill the buffer before exporting", input.buffer_size));
|
||||
}
|
||||
}
|
||||
|
||||
let size_arg = format!("{}x{}", input.width, input.height);
|
||||
let fps_arg = format!("{:.6}", input.fps.max(1.0));
|
||||
let dest_str = input.dest.to_string_lossy().to_string();
|
||||
let ffmpeg = locate_ffmpeg().ok_or_else(|| String::from(
|
||||
"ffmpeg binary not found. install via `brew install ffmpeg`, or set FFMPEG_PATH to its absolute path. searched: $FFMPEG_PATH, $PATH, /opt/homebrew/bin/ffmpeg, /usr/local/bin/ffmpeg, /opt/local/bin/ffmpeg, /usr/bin/ffmpeg"
|
||||
))?;
|
||||
let crf_arg = input.crf.clamp(0, 63).to_string();
|
||||
let mut ff = Command::new(&ffmpeg)
|
||||
.args([
|
||||
"-y",
|
||||
"-loglevel", "info",
|
||||
"-f", "rawvideo",
|
||||
"-pixel_format", "rgba",
|
||||
"-video_size", size_arg.as_str(),
|
||||
"-framerate", fps_arg.as_str(),
|
||||
"-i", "-",
|
||||
"-c:v", "libvpx-vp9",
|
||||
"-pix_fmt", "yuv420p",
|
||||
"-crf", crf_arg.as_str(),
|
||||
"-b:v", "0",
|
||||
"-deadline", "good",
|
||||
"-cpu-used", "2",
|
||||
"-row-mt", "1",
|
||||
"-tile-columns", "2",
|
||||
"-threads", "8",
|
||||
dest_str.as_str(),
|
||||
])
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()
|
||||
.map_err(|e| format!("ffmpeg spawn failed: {e}"))?;
|
||||
let mut stdin = ff.stdin.take().ok_or_else(|| String::from("ffmpeg stdin pipe missing"))?;
|
||||
|
||||
let stderr_buf = Arc::new(Mutex::new(Vec::<u8>::new()));
|
||||
let stderr_pipe = ff.stderr.take().ok_or_else(|| String::from("ffmpeg stderr pipe missing"))?;
|
||||
let stderr_thread = {
|
||||
let buf = Arc::clone(&stderr_buf);
|
||||
thread::spawn(move || {
|
||||
let mut reader = stderr_pipe;
|
||||
let mut chunk = [0u8; 4096];
|
||||
loop {
|
||||
match reader.read(&mut chunk) {
|
||||
Ok(0) | Err(_) => break,
|
||||
Ok(n) => buf.lock().unwrap().extend_from_slice(&chunk[..n]),
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
let (xmin, xmax, ymin, ymax) = world_bounds(&input.base_doc);
|
||||
let expected_bytes_per_frame = (input.width as usize) * (input.height as usize) * 4;
|
||||
|
||||
{
|
||||
let mut p = input.progress.lock().unwrap();
|
||||
p.phase = ExportPhase::Rendering;
|
||||
p.frames_done = 0;
|
||||
p.frames_total = input.buffer_size;
|
||||
p.bytes_sent = 0;
|
||||
}
|
||||
|
||||
let mut written = 0usize;
|
||||
let mut bytes_sent = 0usize;
|
||||
for f in 0..(input.buffer_size as i64) {
|
||||
let sol = input.lut.get(&f).expect("frame presence verified above");
|
||||
let mut doc = input.base_doc.clone();
|
||||
let t = (f as f64) * input.dt;
|
||||
kinematic::apply_tracks(&mut doc, &input.base_doc, &input.tracks, t);
|
||||
|
||||
let pixmap = render_frame(&doc, sol, input.width, input.height, input.contour, xmin, xmax, ymin, ymax);
|
||||
let frame_bytes = pixmap.data();
|
||||
if frame_bytes.len() != expected_bytes_per_frame {
|
||||
drop(stdin);
|
||||
let _ = ff.kill();
|
||||
let _ = stderr_thread.join();
|
||||
return Err(format!(
|
||||
"frame {f} produced {} bytes, expected {} (width={}, height={})",
|
||||
frame_bytes.len(), expected_bytes_per_frame, input.width, input.height,
|
||||
));
|
||||
}
|
||||
if let Err(e) = stdin.write_all(frame_bytes) {
|
||||
drop(stdin);
|
||||
let _ = ff.wait();
|
||||
let _ = stderr_thread.join();
|
||||
let log = String::from_utf8_lossy(&stderr_buf.lock().unwrap()).to_string();
|
||||
return Err(format!(
|
||||
"write frame {f} to ffmpeg failed after {bytes_sent} bytes ({written} frames): {e}\n\n--- ffmpeg log ---\n{log}",
|
||||
));
|
||||
}
|
||||
bytes_sent += frame_bytes.len();
|
||||
written += 1;
|
||||
{
|
||||
let mut p = input.progress.lock().unwrap();
|
||||
p.frames_done = written;
|
||||
p.bytes_sent = bytes_sent;
|
||||
}
|
||||
}
|
||||
drop(stdin);
|
||||
{
|
||||
let mut p = input.progress.lock().unwrap();
|
||||
p.phase = ExportPhase::Finalizing;
|
||||
}
|
||||
let status = ff.wait().map_err(|e| format!("ffmpeg wait failed: {e}"))?;
|
||||
let _ = stderr_thread.join();
|
||||
let log = String::from_utf8_lossy(&stderr_buf.lock().unwrap()).to_string();
|
||||
|
||||
let dest_size = std::fs::metadata(&input.dest).map(|m| m.len()).unwrap_or(0);
|
||||
if !status.success() {
|
||||
return Err(format!(
|
||||
"ffmpeg exited with {status}. wrote {written} frames ({bytes_sent} bytes) to stdin, output {dest_size} bytes.\n\n--- ffmpeg log ---\n{log}",
|
||||
));
|
||||
}
|
||||
if dest_size == 0 {
|
||||
return Err(format!(
|
||||
"ffmpeg succeeded but the output is 0 bytes. wrote {written} frames ({bytes_sent} bytes) to stdin at {input_w}x{input_h} rgba, {fps} fps.\n\n--- ffmpeg log ---\n{log}",
|
||||
input_w = input.width, input_h = input.height, fps = fps_arg,
|
||||
));
|
||||
}
|
||||
{
|
||||
let mut p = input.progress.lock().unwrap();
|
||||
p.phase = ExportPhase::Done;
|
||||
}
|
||||
Ok(ExportReport { written, dest: input.dest })
|
||||
}
|
||||
|
||||
/// locates an ffmpeg binary via $FFMPEG_PATH, $PATH, and the standard Homebrew, MacPorts, and system locations.
|
||||
fn locate_ffmpeg() -> Option<std::path::PathBuf> {
|
||||
if let Ok(p) = std::env::var("FFMPEG_PATH") {
|
||||
let pb = std::path::PathBuf::from(p);
|
||||
if pb.is_file() { return Some(pb); }
|
||||
}
|
||||
if let Ok(path) = std::env::var("PATH") {
|
||||
for dir in path.split(':') {
|
||||
let pb = std::path::PathBuf::from(dir).join("ffmpeg");
|
||||
if pb.is_file() { return Some(pb); }
|
||||
}
|
||||
}
|
||||
for candidate in [
|
||||
"/opt/homebrew/bin/ffmpeg",
|
||||
"/usr/local/bin/ffmpeg",
|
||||
"/opt/local/bin/ffmpeg",
|
||||
"/usr/bin/ffmpeg",
|
||||
] {
|
||||
let pb = std::path::PathBuf::from(candidate);
|
||||
if pb.is_file() { return Some(pb); }
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn world_bounds(doc: &FemmDoc) -> (f64, f64, f64, f64) {
|
||||
let mut xmin = f64::INFINITY;
|
||||
let mut xmax = f64::NEG_INFINITY;
|
||||
let mut ymin = f64::INFINITY;
|
||||
let mut ymax = f64::NEG_INFINITY;
|
||||
let mut had_point = false;
|
||||
for n in &doc.nodes {
|
||||
if n.x < xmin { xmin = n.x; }
|
||||
if n.x > xmax { xmax = n.x; }
|
||||
if n.y < ymin { ymin = n.y; }
|
||||
if n.y > ymax { ymax = n.y; }
|
||||
had_point = true;
|
||||
}
|
||||
for l in &doc.block_labels {
|
||||
if l.x < xmin { xmin = l.x; }
|
||||
if l.x > xmax { xmax = l.x; }
|
||||
if l.y < ymin { ymin = l.y; }
|
||||
if l.y > ymax { ymax = l.y; }
|
||||
had_point = true;
|
||||
}
|
||||
if !had_point { return (-1.0, 1.0, -1.0, 1.0); }
|
||||
if (xmax - xmin).abs() < 1e-9 { xmin -= 0.5; xmax += 0.5; }
|
||||
if (ymax - ymin).abs() < 1e-9 { ymin -= 0.5; ymax += 0.5; }
|
||||
(xmin, xmax, ymin, ymax)
|
||||
}
|
||||
|
||||
struct View {
|
||||
scale: f64,
|
||||
offset_x: f64,
|
||||
offset_y: f64,
|
||||
y_max: f64,
|
||||
}
|
||||
|
||||
impl View {
|
||||
fn fit(width: u32, height: u32, xmin: f64, xmax: f64, ymin: f64, ymax: f64) -> Self {
|
||||
let avail_w = (width as f64 - 2.0 * PADDING_PX).max(1.0);
|
||||
let avail_h = (height as f64 - 2.0 * PADDING_PX).max(1.0);
|
||||
let dx = (xmax - xmin).max(1e-9);
|
||||
let dy = (ymax - ymin).max(1e-9);
|
||||
let scale = (avail_w / dx).min(avail_h / dy);
|
||||
let drawn_w = dx * scale;
|
||||
let drawn_h = dy * scale;
|
||||
let offset_x = (width as f64 - drawn_w) / 2.0 - xmin * scale;
|
||||
let offset_y = (height as f64 - drawn_h) / 2.0;
|
||||
let _ = ymin;
|
||||
Self { scale, offset_x, offset_y, y_max: ymax }
|
||||
}
|
||||
fn map(&self, x: f64, y: f64) -> (f32, f32) {
|
||||
let px = x * self.scale + self.offset_x;
|
||||
let py = (self.y_max - y) * self.scale + self.offset_y;
|
||||
(px as f32, py as f32)
|
||||
}
|
||||
}
|
||||
|
||||
fn render_frame(
|
||||
doc: &FemmDoc,
|
||||
sol: &MagSolution,
|
||||
width: u32,
|
||||
height: u32,
|
||||
contour: bool,
|
||||
xmin: f64, xmax: f64, ymin: f64, ymax: f64,
|
||||
) -> Pixmap {
|
||||
let mut pixmap = Pixmap::new(width, height).expect("allocate pixmap");
|
||||
pixmap.fill(Color::from_rgba8(20, 20, 24, 255));
|
||||
let view = View::fit(width, height, xmin, xmax, ymin, ymax);
|
||||
|
||||
let (lo, hi) = sol.b_magnitude_range();
|
||||
let span = if hi > lo { hi - lo } else { 1.0 };
|
||||
|
||||
let mut paint = Paint::default();
|
||||
paint.anti_alias = true;
|
||||
|
||||
for i in 0..sol.mesh_elements.len() {
|
||||
let el = &sol.mesh_elements[i];
|
||||
let (Some(a), Some(b), Some(c)) = (
|
||||
sol.mesh_nodes.get(el.n[0] as usize),
|
||||
sol.mesh_nodes.get(el.n[1] as usize),
|
||||
sol.mesh_nodes.get(el.n[2] as usize),
|
||||
) else { continue };
|
||||
let (pa_x, pa_y) = view.map(a.x, a.y);
|
||||
let (pb_x, pb_y) = view.map(b.x, b.y);
|
||||
let (pc_x, pc_y) = view.map(c.x, c.y);
|
||||
let t = ((sol.b_magnitude(i) - lo) / span).clamp(0.0, 1.0);
|
||||
let t_mapped = if contour {
|
||||
let band = (t * BAND_COUNT as f64).floor().min(BAND_COUNT as f64 - 1.0);
|
||||
(band + 0.5) / BAND_COUNT as f64
|
||||
} else {
|
||||
t
|
||||
};
|
||||
paint.set_color(jet_color(t_mapped as f32));
|
||||
let mut pb = PathBuilder::new();
|
||||
pb.move_to(pa_x, pa_y);
|
||||
pb.line_to(pb_x, pb_y);
|
||||
pb.line_to(pc_x, pc_y);
|
||||
pb.close();
|
||||
if let Some(path) = pb.finish() {
|
||||
pixmap.fill_path(&path, &paint, FillRule::Winding, Transform::identity(), None);
|
||||
}
|
||||
}
|
||||
|
||||
if contour {
|
||||
let (a_lo, a_hi) = sol.a_real_range();
|
||||
if a_hi > a_lo {
|
||||
let mut levels = Vec::with_capacity(FLUX_LINE_COUNT);
|
||||
let denom = (FLUX_LINE_COUNT + 1) as f64;
|
||||
for k in 1..=FLUX_LINE_COUNT {
|
||||
levels.push(a_lo + (a_hi - a_lo) * (k as f64) / denom);
|
||||
}
|
||||
paint.set_color(Color::from_rgba8(20, 20, 24, 220));
|
||||
let stroke = Stroke { width: 1.0, ..Default::default() };
|
||||
for (p0, p1) in sol.flux_lines(&levels) {
|
||||
let (ax, ay) = view.map(p0.0, p0.1);
|
||||
let (bx, by) = view.map(p1.0, p1.1);
|
||||
let mut pb = PathBuilder::new();
|
||||
pb.move_to(ax, ay);
|
||||
pb.line_to(bx, by);
|
||||
if let Some(path) = pb.finish() {
|
||||
pixmap.stroke_path(&path, &paint, &stroke, Transform::identity(), None);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
paint.set_color(Color::from_rgba8(230, 230, 235, 255));
|
||||
let stroke = Stroke { width: 1.4, ..Default::default() };
|
||||
for s in &doc.segments {
|
||||
if let (Some(p0), Some(p1)) = (doc.nodes.get(s.n0 as usize), doc.nodes.get(s.n1 as usize)) {
|
||||
let (ax, ay) = view.map(p0.x, p0.y);
|
||||
let (bx, by) = view.map(p1.x, p1.y);
|
||||
let mut pb = PathBuilder::new();
|
||||
pb.move_to(ax, ay);
|
||||
pb.line_to(bx, by);
|
||||
if let Some(path) = pb.finish() {
|
||||
pixmap.stroke_path(&path, &paint, &stroke, Transform::identity(), None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for a in &doc.arcs {
|
||||
let (Some(p0), Some(p1)) = (doc.nodes.get(a.n0 as usize), doc.nodes.get(a.n1 as usize)) else { continue };
|
||||
let polyline = arc_polyline(p0.x, p0.y, p1.x, p1.y, a.arc_length, 24);
|
||||
let mut pb = PathBuilder::new();
|
||||
for (i, (x, y)) in polyline.iter().enumerate() {
|
||||
let (px, py) = view.map(*x, *y);
|
||||
if i == 0 { pb.move_to(px, py); } else { pb.line_to(px, py); }
|
||||
}
|
||||
if let Some(path) = pb.finish() {
|
||||
pixmap.stroke_path(&path, &paint, &stroke, Transform::identity(), None);
|
||||
}
|
||||
}
|
||||
|
||||
pixmap
|
||||
}
|
||||
|
||||
/// returns a polyline approximating an arc between two endpoints across a CCW degree sweep, capped at max_steps subdivisions.
|
||||
fn arc_polyline(x0: f64, y0: f64, x1: f64, y1: f64, sweep_deg: f64, max_steps: usize) -> Vec<(f64, f64)> {
|
||||
let theta = sweep_deg.to_radians();
|
||||
if theta.abs() < 1e-9 { return vec![(x0, y0), (x1, y1)]; }
|
||||
let dx = x1 - x0;
|
||||
let dy = y1 - y0;
|
||||
let chord = (dx * dx + dy * dy).sqrt();
|
||||
if chord < 1e-12 { return vec![(x0, y0)]; }
|
||||
let radius = chord / (2.0 * (theta / 2.0).sin().abs());
|
||||
let mid_x = (x0 + x1) / 2.0;
|
||||
let mid_y = (y0 + y1) / 2.0;
|
||||
let perp_x = -dy / chord;
|
||||
let perp_y = dx / chord;
|
||||
let h = radius * (theta / 2.0).cos();
|
||||
let sign = if theta > 0.0 { 1.0 } else { -1.0 };
|
||||
let cx = mid_x + sign * perp_x * h;
|
||||
let cy = mid_y + sign * perp_y * h;
|
||||
|
||||
let a0 = (y0 - cy).atan2(x0 - cx);
|
||||
let a1 = (y1 - cy).atan2(x1 - cx);
|
||||
let mut delta = a1 - a0;
|
||||
if theta > 0.0 {
|
||||
while delta < 0.0 { delta += std::f64::consts::TAU; }
|
||||
} else {
|
||||
while delta > 0.0 { delta -= std::f64::consts::TAU; }
|
||||
}
|
||||
let steps = ((sweep_deg.abs() / 4.0).ceil() as usize).clamp(2, max_steps);
|
||||
let mut out = Vec::with_capacity(steps + 1);
|
||||
for i in 0..=steps {
|
||||
let a = a0 + delta * (i as f64) / (steps as f64);
|
||||
out.push((cx + radius * a.cos(), cy + radius * a.sin()));
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
fn jet_color(t: f32) -> Color {
|
||||
let t = t.clamp(0.0, 1.0);
|
||||
let (r, g, b) = if t < 0.25 {
|
||||
let s = t * 4.0;
|
||||
(0.0, s, 1.0)
|
||||
} else if t < 0.5 {
|
||||
let s = (t - 0.25) * 4.0;
|
||||
(0.0, 1.0, 1.0 - s)
|
||||
} else if t < 0.75 {
|
||||
let s = (t - 0.5) * 4.0;
|
||||
(s, 1.0, 0.0)
|
||||
} else {
|
||||
let s = (t - 0.75) * 4.0;
|
||||
(1.0, 1.0 - s, 0.0)
|
||||
};
|
||||
Color::from_rgba8((r * 255.0) as u8, (g * 255.0) as u8, (b * 255.0) as u8, 255)
|
||||
}
|
||||
|
|
@ -54,42 +54,52 @@ impl Expression {
|
|||
Expression { source: String::from("0"), expr }
|
||||
}
|
||||
|
||||
/// evaluates the expression with bindings for s and t.
|
||||
/// evaluates the expression with bindings for s and t, plus the helper functions harm and series and the tau constant.
|
||||
pub fn eval(&self, s: f64, t: f64) -> f64 {
|
||||
use std::f64::consts::PI;
|
||||
let mut ctx = meval::Context::new();
|
||||
ctx.var("s", s);
|
||||
ctx.var("t", t);
|
||||
ctx.var("tau", 2.0 * PI);
|
||||
ctx.funcn("harm", |args| {
|
||||
let s = args[0];
|
||||
let t = args[1];
|
||||
let f0 = args[2];
|
||||
let n = args[3].round();
|
||||
if n < 1.0 { return 0.0; }
|
||||
(n * PI * s).sin() * (2.0 * PI * n * f0 * t).cos() / n
|
||||
}, 4);
|
||||
ctx.funcn("series", |args| {
|
||||
let s = args[0];
|
||||
let t = args[1];
|
||||
let f0 = args[2];
|
||||
let n_max = args[3].round() as i64;
|
||||
if n_max < 1 { return 0.0; }
|
||||
let mut acc = 0.0;
|
||||
for k in 1..=n_max {
|
||||
let kf = k as f64;
|
||||
acc += (kf * PI * s).sin() * (2.0 * PI * kf * f0 * t).cos() / kf;
|
||||
}
|
||||
acc
|
||||
}, 4);
|
||||
self.expr.eval_with_context(ctx).unwrap_or(0.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// one continuous-time track: clamped chord, axis, member nodes, and the closed-form displacement expression.
|
||||
/// one continuous-time track: clamped chord, axis, member nodes, the closed-form displacement expression, and the pre-subdivision edge spine used to re-subdivide on demand.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Track {
|
||||
pub label: String,
|
||||
pub anchor_a: usize,
|
||||
pub anchor_b: usize,
|
||||
pub member_nodes: Vec<usize>,
|
||||
pub axis: Axis,
|
||||
pub expression: Expression,
|
||||
pub label: String,
|
||||
pub anchor_a: usize,
|
||||
pub anchor_b: usize,
|
||||
pub member_nodes: Vec<usize>,
|
||||
pub axis: Axis,
|
||||
pub expression: Expression,
|
||||
pub edge_endpoints: Vec<(usize, usize)>,
|
||||
pub edge_subdivisions: Vec<Vec<usize>>,
|
||||
}
|
||||
|
||||
/// projects a node onto the anchor_a-to-anchor_b chord, returning a parameter clamped to [0, 1].
|
||||
pub fn chord_parameter(base: &FemmDoc, anchor_a: usize, anchor_b: usize, node_idx: usize) -> f64 {
|
||||
let (Some(a), Some(b), Some(n)) = (
|
||||
base.nodes.get(anchor_a),
|
||||
base.nodes.get(anchor_b),
|
||||
base.nodes.get(node_idx),
|
||||
) else { return 0.0 };
|
||||
let dx = b.x - a.x;
|
||||
let dy = b.y - a.y;
|
||||
let len2 = dx * dx + dy * dy;
|
||||
if len2 < 1e-18 { return 0.0; }
|
||||
let s = ((n.x - a.x) * dx + (n.y - a.y) * dy) / len2;
|
||||
s.clamp(0.0, 1.0)
|
||||
}
|
||||
|
||||
/// resets every node position on doc from base, layering each track's evaluated displacement at simulated time t seconds.
|
||||
/// snaps doc nodes to base and layers each track's displacement at simulated time t.
|
||||
pub fn apply_tracks(doc: &mut FemmDoc, base: &FemmDoc, tracks: &[Track], t: f64) {
|
||||
for (i, n) in doc.nodes.iter_mut().enumerate() {
|
||||
if let Some(b) = base.nodes.get(i) {
|
||||
|
|
@ -99,8 +109,30 @@ pub fn apply_tracks(doc: &mut FemmDoc, base: &FemmDoc, tracks: &[Track], t: f64)
|
|||
}
|
||||
for track in tracks {
|
||||
let (ux, uy) = track.axis.unit();
|
||||
let along_x = matches!(track.axis, Axis::PlusY | Axis::MinusY);
|
||||
let val = |i: usize| -> Option<f64> {
|
||||
let n = base.nodes.get(i)?;
|
||||
Some(if along_x { n.x } else { n.y })
|
||||
};
|
||||
let mut lo = f64::INFINITY;
|
||||
let mut hi = f64::NEG_INFINITY;
|
||||
for &idx in &[track.anchor_a, track.anchor_b] {
|
||||
if let Some(v) = val(idx) {
|
||||
if v < lo { lo = v; }
|
||||
if v > hi { hi = v; }
|
||||
}
|
||||
}
|
||||
for &idx in &track.member_nodes {
|
||||
if let Some(v) = val(idx) {
|
||||
if v < lo { lo = v; }
|
||||
if v > hi { hi = v; }
|
||||
}
|
||||
}
|
||||
let span = hi - lo;
|
||||
if !span.is_finite() || span.abs() < 1e-12 { continue; }
|
||||
for &node_idx in &track.member_nodes {
|
||||
let s = chord_parameter(base, track.anchor_a, track.anchor_b, node_idx);
|
||||
let n_v = match val(node_idx) { Some(v) => v, None => continue };
|
||||
let s = ((n_v - lo) / span).clamp(0.0, 1.0);
|
||||
let delta = track.expression.eval(s, t);
|
||||
if let Some(n) = doc.nodes.get_mut(node_idx) {
|
||||
n.x += ux * delta;
|
||||
|
|
@ -150,5 +182,150 @@ pub fn track_from_selection(
|
|||
member_nodes: members,
|
||||
axis,
|
||||
expression,
|
||||
edge_endpoints: Vec::new(),
|
||||
edge_subdivisions: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
/// removes the chain of segments inside each edge and replaces it with a fresh chain at `count` subdivisions, appending new node indices to the doc. orphans the prior subdivision nodes to preserve every other track's index references.
|
||||
pub fn rebuild_track_subdivisions(track: &mut Track, doc: &mut FemmDoc, count: usize) -> Result<(), String> {
|
||||
if count < 2 {
|
||||
return Err(String::from("subdivision count must be at least 2"));
|
||||
}
|
||||
if track.edge_endpoints.is_empty() {
|
||||
let (eps, subs) = infer_edges_from_positions(track, doc);
|
||||
if eps.is_empty() {
|
||||
return Err(String::from("track has no recorded edges and inference from positions found none - delete and recreate the track"));
|
||||
}
|
||||
track.edge_endpoints = eps;
|
||||
track.edge_subdivisions = subs;
|
||||
}
|
||||
while track.edge_subdivisions.len() < track.edge_endpoints.len() {
|
||||
track.edge_subdivisions.push(Vec::new());
|
||||
}
|
||||
|
||||
let mut new_subdivisions: Vec<Vec<usize>> = Vec::with_capacity(track.edge_endpoints.len());
|
||||
for (edge_idx, (a, b)) in track.edge_endpoints.iter().copied().enumerate() {
|
||||
let old_subs = track.edge_subdivisions[edge_idx].clone();
|
||||
let edge_nodes: std::collections::HashSet<usize> = std::iter::once(a)
|
||||
.chain(std::iter::once(b))
|
||||
.chain(old_subs.iter().copied())
|
||||
.collect();
|
||||
doc.segments.retain(|s| {
|
||||
!(edge_nodes.contains(&(s.n0 as usize)) && edge_nodes.contains(&(s.n1 as usize)))
|
||||
});
|
||||
|
||||
let pa = match doc.nodes.get(a) {
|
||||
Some(n) => (n.x, n.y),
|
||||
None => return Err(format!("edge endpoint {a} out of bounds")),
|
||||
};
|
||||
let pb = match doc.nodes.get(b) {
|
||||
Some(n) => (n.x, n.y),
|
||||
None => return Err(format!("edge endpoint {b} out of bounds")),
|
||||
};
|
||||
|
||||
let mut new_subs: Vec<usize> = Vec::with_capacity(count.saturating_sub(1));
|
||||
for i in 1..count {
|
||||
let t = i as f64 / count as f64;
|
||||
let x = pa.0 + (pb.0 - pa.0) * t;
|
||||
let y = pa.1 + (pb.1 - pa.1) * t;
|
||||
let idx = doc.nodes.len();
|
||||
doc.nodes.push(femm_doc_mag::Node {
|
||||
x, y,
|
||||
boundary_marker: String::new(),
|
||||
in_group: 0,
|
||||
selected: false,
|
||||
});
|
||||
new_subs.push(idx);
|
||||
}
|
||||
|
||||
let mut prev = a;
|
||||
for &mid in &new_subs {
|
||||
doc.segments.push(femm_doc_mag::Segment {
|
||||
n0: prev as i32,
|
||||
n1: mid as i32,
|
||||
max_side_length: -1.0,
|
||||
boundary_marker: String::new(),
|
||||
hidden: false,
|
||||
in_group: 0,
|
||||
selected: false,
|
||||
});
|
||||
prev = mid;
|
||||
}
|
||||
doc.segments.push(femm_doc_mag::Segment {
|
||||
n0: prev as i32,
|
||||
n1: b as i32,
|
||||
max_side_length: -1.0,
|
||||
boundary_marker: String::new(),
|
||||
hidden: false,
|
||||
in_group: 0,
|
||||
selected: false,
|
||||
});
|
||||
new_subdivisions.push(new_subs);
|
||||
}
|
||||
|
||||
let mut corner_set: std::collections::HashSet<usize> = std::collections::HashSet::new();
|
||||
for &(a, b) in &track.edge_endpoints {
|
||||
corner_set.insert(a);
|
||||
corner_set.insert(b);
|
||||
}
|
||||
let mut new_members: Vec<usize> = Vec::new();
|
||||
for &c in &corner_set {
|
||||
if c != track.anchor_a && c != track.anchor_b { new_members.push(c); }
|
||||
}
|
||||
for subs in &new_subdivisions {
|
||||
for &s in subs { new_members.push(s); }
|
||||
}
|
||||
track.member_nodes = new_members;
|
||||
track.edge_subdivisions = new_subdivisions;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// reconstructs a track's edges from its node positions: clusters anchors+members by the lateral coordinate (perpendicular to the displacement axis), and treats each cluster as one edge with extremes as endpoints and middles as subdivisions.
|
||||
fn infer_edges_from_positions(track: &Track, doc: &FemmDoc) -> (Vec<(usize, usize)>, Vec<Vec<usize>>) {
|
||||
let along_x = matches!(track.axis, Axis::PlusY | Axis::MinusY);
|
||||
let pos = |i: usize| doc.nodes.get(i).map(|n| (n.x, n.y));
|
||||
let perp = move |i: usize| pos(i).map(|(x, y)| if along_x { x } else { y });
|
||||
let lat = move |i: usize| pos(i).map(|(x, y)| if along_x { y } else { x });
|
||||
|
||||
let mut all_indices: Vec<usize> = Vec::with_capacity(track.member_nodes.len() + 2);
|
||||
all_indices.push(track.anchor_a);
|
||||
all_indices.push(track.anchor_b);
|
||||
all_indices.extend(&track.member_nodes);
|
||||
|
||||
let tol = 0.05_f64;
|
||||
let mut clusters: Vec<(f64, Vec<usize>)> = Vec::new();
|
||||
for &idx in &all_indices {
|
||||
let v = match lat(idx) { Some(v) => v, None => continue };
|
||||
let mut placed = false;
|
||||
for cluster in &mut clusters {
|
||||
if (v - cluster.0).abs() < tol {
|
||||
cluster.1.push(idx);
|
||||
placed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if !placed { clusters.push((v, vec![idx])); }
|
||||
}
|
||||
|
||||
let mut endpoints = Vec::new();
|
||||
let mut subdivisions = Vec::new();
|
||||
for (_, cluster) in clusters {
|
||||
if cluster.len() < 2 { continue; }
|
||||
let mut sorted: Vec<(usize, f64)> = cluster.iter()
|
||||
.filter_map(|&i| perp(i).map(|p| (i, p)))
|
||||
.collect();
|
||||
sorted.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
|
||||
let a = sorted.first().unwrap().0;
|
||||
let b = sorted.last().unwrap().0;
|
||||
let subs: Vec<usize> = if sorted.len() >= 3 {
|
||||
sorted[1..sorted.len()-1].iter().map(|(i, _)| *i).collect()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
endpoints.push((a, b));
|
||||
subdivisions.push(subs);
|
||||
}
|
||||
|
||||
(endpoints, subdivisions)
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,37 @@
|
|||
//! single-file packaging of a base FemmDoc plus its SimMeta, round-tripping the full reset state into one .femsess blob.
|
||||
|
||||
use femm_doc_mag::FemmDoc;
|
||||
use crate::sim_meta::{self, SimMeta};
|
||||
|
||||
const SESSION_HEADER: &str = "# femm42 session v1\n";
|
||||
const DOC_OPEN: &str = "[base-doc]\n";
|
||||
const DOC_CLOSE: &str = "[/base-doc]\n";
|
||||
|
||||
/// joins the base .fem text and the sim metadata into one delimited session blob.
|
||||
pub fn serialize(base_doc: &FemmDoc, meta: &SimMeta) -> String {
|
||||
let mut out = String::new();
|
||||
out.push_str(SESSION_HEADER);
|
||||
out.push_str(DOC_OPEN);
|
||||
let doc_text = base_doc.write();
|
||||
out.push_str(&doc_text);
|
||||
if !doc_text.ends_with('\n') { out.push('\n'); }
|
||||
out.push_str(DOC_CLOSE);
|
||||
out.push_str(&sim_meta::serialize_meta(meta));
|
||||
out
|
||||
}
|
||||
|
||||
/// extracts the base FemmDoc and SimMeta from a session blob, failing if the markers or sub-payloads are malformed.
|
||||
pub fn parse(text: &str) -> Result<(FemmDoc, SimMeta), String> {
|
||||
let doc_start = text.find(DOC_OPEN).ok_or_else(|| String::from("missing [base-doc] section"))?;
|
||||
let body_start = doc_start + DOC_OPEN.len();
|
||||
let after_body = text[body_start..]
|
||||
.find(DOC_CLOSE)
|
||||
.ok_or_else(|| String::from("missing [/base-doc] marker"))?;
|
||||
let doc_text = &text[body_start..body_start + after_body];
|
||||
let meta_text = &text[body_start + after_body + DOC_CLOSE.len()..];
|
||||
|
||||
let doc = FemmDoc::parse(doc_text)
|
||||
.map_err(|e| format!("parse [base-doc] failed: {e}"))?;
|
||||
let meta = sim_meta::parse_meta(meta_text)?;
|
||||
Ok((doc, meta))
|
||||
}
|
||||
|
|
@ -0,0 +1,249 @@
|
|||
//! save/load format for simulation metadata: SPICE-suffixed scalars, per-track expressions and node references, plain text sections.
|
||||
|
||||
use femm_doc_mag::FemmDoc;
|
||||
use crate::kinematic::{Axis, Expression, Track};
|
||||
use crate::spice;
|
||||
use std::time::Duration;
|
||||
|
||||
pub struct SimMeta {
|
||||
pub source_fem: Option<String>,
|
||||
pub dt_text: String,
|
||||
pub interval_text: String,
|
||||
pub subdivisions: usize,
|
||||
pub buffer_size: usize,
|
||||
pub match_cycle: bool,
|
||||
pub loop_playback: bool,
|
||||
pub fundamental_hz: f64,
|
||||
pub tracks: Vec<TrackMeta>,
|
||||
}
|
||||
|
||||
pub struct TrackMeta {
|
||||
pub label: String,
|
||||
pub anchor_a: usize,
|
||||
pub anchor_b: usize,
|
||||
pub members: Vec<usize>,
|
||||
pub axis: Axis,
|
||||
pub expression: String,
|
||||
pub edges: Vec<(usize, usize, Vec<usize>)>,
|
||||
}
|
||||
|
||||
/// renders a SimMeta into the on-disk text format.
|
||||
pub fn serialize_meta(meta: &SimMeta) -> String {
|
||||
let mut s = String::new();
|
||||
s.push_str("# femm42 simulation metadata\n");
|
||||
s.push_str("# track node indices reference the doc loaded above; editing the doc may invalidate them.\n\n");
|
||||
if let Some(p) = &meta.source_fem {
|
||||
s.push_str("[source]\n");
|
||||
s.push_str(&format!("fem = {p}\n\n"));
|
||||
}
|
||||
s.push_str("[sim]\n");
|
||||
s.push_str(&format!("dt = {}\n", meta.dt_text));
|
||||
s.push_str(&format!("interval = {}\n", meta.interval_text));
|
||||
s.push_str(&format!("subdivisions = {}\n", meta.subdivisions));
|
||||
s.push_str(&format!("buffer_size = {}\n", meta.buffer_size));
|
||||
s.push_str(&format!("match_cycle = {}\n", meta.match_cycle));
|
||||
s.push_str(&format!("loop_playback = {}\n", meta.loop_playback));
|
||||
s.push_str(&format!("fundamental_hz = {}\n\n", meta.fundamental_hz));
|
||||
for track in &meta.tracks {
|
||||
s.push_str("[track]\n");
|
||||
s.push_str(&format!("label = {}\n", track.label));
|
||||
s.push_str(&format!("anchor_a = {}\n", track.anchor_a));
|
||||
s.push_str(&format!("anchor_b = {}\n", track.anchor_b));
|
||||
s.push_str(&format!("axis = {}\n", axis_to_str(track.axis)));
|
||||
s.push_str(&format!("expression = {}\n", track.expression));
|
||||
s.push_str("members =");
|
||||
for n in &track.members { s.push_str(&format!(" {n}")); }
|
||||
s.push_str("\n");
|
||||
for (a, b, subs) in &track.edges {
|
||||
s.push_str(&format!("edge = {a} {b}"));
|
||||
for n in subs { s.push_str(&format!(" {n}")); }
|
||||
s.push_str("\n");
|
||||
}
|
||||
s.push_str("\n");
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
/// parses the on-disk metadata text into a SimMeta, failing on unknown sections or unparseable values.
|
||||
pub fn parse_meta(text: &str) -> Result<SimMeta, String> {
|
||||
let mut source_fem: Option<String> = None;
|
||||
let mut dt_text = String::from("100u");
|
||||
let mut interval_text = String::from("50m");
|
||||
let mut subdivisions = 20usize;
|
||||
let mut buffer_size = 10usize;
|
||||
let mut match_cycle = false;
|
||||
let mut loop_playback = false;
|
||||
let mut fundamental_hz = 82.41f64;
|
||||
let mut tracks: Vec<TrackMeta> = Vec::new();
|
||||
|
||||
enum Section { None, Source, Sim, Track }
|
||||
let mut section = Section::None;
|
||||
let mut cur: Option<TrackMeta> = None;
|
||||
|
||||
for (line_no, raw) in text.lines().enumerate() {
|
||||
let line = raw.trim();
|
||||
if line.is_empty() || line.starts_with('#') { continue; }
|
||||
if let Some(name) = line.strip_prefix('[').and_then(|s| s.strip_suffix(']')) {
|
||||
if let Some(t) = cur.take() { tracks.push(t); }
|
||||
section = match name.trim() {
|
||||
"source" => Section::Source,
|
||||
"sim" => Section::Sim,
|
||||
"track" => { cur = Some(TrackMeta {
|
||||
label: format!("track {}", tracks.len() + 1),
|
||||
anchor_a: 0, anchor_b: 0,
|
||||
members: Vec::new(),
|
||||
axis: Axis::PlusY,
|
||||
expression: String::from("0"),
|
||||
edges: Vec::new(),
|
||||
}); Section::Track }
|
||||
other => return Err(format!("unknown section {other:?} at line {}", line_no + 1)),
|
||||
};
|
||||
continue;
|
||||
}
|
||||
let (key, value) = match line.split_once('=') {
|
||||
Some((k, v)) => (k.trim(), v.trim()),
|
||||
None => return Err(format!("expected key = value at line {}: {line:?}", line_no + 1)),
|
||||
};
|
||||
match section {
|
||||
Section::Source => match key {
|
||||
"fem" => source_fem = Some(value.trim_matches('"').to_string()),
|
||||
other => return Err(format!("unknown source key {other:?} at line {}", line_no + 1)),
|
||||
},
|
||||
Section::Sim => match key {
|
||||
"dt" => { dt_text = value.to_string(); }
|
||||
"interval" => { interval_text = value.to_string(); }
|
||||
"subdivisions" => subdivisions = parse_usize(value, line_no)?,
|
||||
"buffer_size" => buffer_size = parse_usize(value, line_no)?,
|
||||
"match_cycle" => match_cycle = parse_bool(value, line_no)?,
|
||||
"loop_playback" => loop_playback = parse_bool(value, line_no)?,
|
||||
"fundamental_hz" => fundamental_hz = parse_f64(value, line_no)?,
|
||||
other => return Err(format!("unknown sim key {other:?} at line {}", line_no + 1)),
|
||||
},
|
||||
Section::Track => {
|
||||
let track = cur.as_mut().ok_or_else(|| format!("track key outside [track] at line {}", line_no + 1))?;
|
||||
match key {
|
||||
"label" => track.label = value.to_string(),
|
||||
"anchor_a" => track.anchor_a = parse_usize(value, line_no)?,
|
||||
"anchor_b" => track.anchor_b = parse_usize(value, line_no)?,
|
||||
"axis" => track.axis = axis_from_str(value)
|
||||
.ok_or_else(|| format!("unknown axis {value:?} at line {}", line_no + 1))?,
|
||||
"expression" => track.expression = value.to_string(),
|
||||
"members" => {
|
||||
track.members.clear();
|
||||
for tok in value.split_whitespace() {
|
||||
track.members.push(parse_usize(tok, line_no)?);
|
||||
}
|
||||
}
|
||||
"edge" => {
|
||||
let mut iter = value.split_whitespace();
|
||||
let a = iter.next().ok_or_else(|| format!("edge missing first endpoint at line {}", line_no + 1))?;
|
||||
let b = iter.next().ok_or_else(|| format!("edge missing second endpoint at line {}", line_no + 1))?;
|
||||
let a = parse_usize(a, line_no)?;
|
||||
let b = parse_usize(b, line_no)?;
|
||||
let mut subs = Vec::new();
|
||||
for tok in iter { subs.push(parse_usize(tok, line_no)?); }
|
||||
track.edges.push((a, b, subs));
|
||||
}
|
||||
other => return Err(format!("unknown track key {other:?} at line {}", line_no + 1)),
|
||||
}
|
||||
}
|
||||
Section::None => return Err(format!("key = value before any section at line {}", line_no + 1)),
|
||||
}
|
||||
}
|
||||
if let Some(t) = cur.take() { tracks.push(t); }
|
||||
|
||||
Ok(SimMeta {
|
||||
source_fem,
|
||||
dt_text, interval_text,
|
||||
subdivisions, buffer_size,
|
||||
match_cycle, loop_playback,
|
||||
fundamental_hz,
|
||||
tracks,
|
||||
})
|
||||
}
|
||||
|
||||
/// validates a parsed metadata block against the currently-loaded doc, returning concrete Track values when every referenced node index is in bounds.
|
||||
pub fn resolve_tracks(meta: &SimMeta, doc: &FemmDoc) -> Result<Vec<Track>, String> {
|
||||
let n = doc.nodes.len();
|
||||
let mut out = Vec::with_capacity(meta.tracks.len());
|
||||
for (i, tm) in meta.tracks.iter().enumerate() {
|
||||
if tm.anchor_a >= n {
|
||||
return Err(format!("track {}: anchor_a {} out of bounds (doc has {n} nodes)", i + 1, tm.anchor_a));
|
||||
}
|
||||
if tm.anchor_b >= n {
|
||||
return Err(format!("track {}: anchor_b {} out of bounds (doc has {n} nodes)", i + 1, tm.anchor_b));
|
||||
}
|
||||
for &m in &tm.members {
|
||||
if m >= n {
|
||||
return Err(format!("track {}: member {} out of bounds (doc has {n} nodes)", i + 1, m));
|
||||
}
|
||||
}
|
||||
let expr = Expression::parse(&tm.expression)
|
||||
.map_err(|e| format!("track {}: expression parse failed: {e}", i + 1))?;
|
||||
let mut edge_endpoints = Vec::with_capacity(tm.edges.len());
|
||||
let mut edge_subdivisions = Vec::with_capacity(tm.edges.len());
|
||||
for (a, b, subs) in &tm.edges {
|
||||
if *a >= n || *b >= n {
|
||||
return Err(format!("track {}: edge endpoint out of bounds (doc has {n} nodes)", i + 1));
|
||||
}
|
||||
for &s in subs {
|
||||
if s >= n {
|
||||
return Err(format!("track {}: edge subdivision {s} out of bounds (doc has {n} nodes)", i + 1));
|
||||
}
|
||||
}
|
||||
edge_endpoints.push((*a, *b));
|
||||
edge_subdivisions.push(subs.clone());
|
||||
}
|
||||
out.push(Track {
|
||||
label: tm.label.clone(),
|
||||
anchor_a: tm.anchor_a,
|
||||
anchor_b: tm.anchor_b,
|
||||
member_nodes: tm.members.clone(),
|
||||
axis: tm.axis,
|
||||
expression: expr,
|
||||
edge_endpoints,
|
||||
edge_subdivisions,
|
||||
});
|
||||
}
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
fn parse_usize(v: &str, line: usize) -> Result<usize, String> {
|
||||
v.trim().parse::<usize>().map_err(|e| format!("integer parse failed at line {}: {e}", line + 1))
|
||||
}
|
||||
|
||||
fn parse_f64(v: &str, line: usize) -> Result<f64, String> {
|
||||
v.trim().parse::<f64>().map_err(|e| format!("float parse failed at line {}: {e}", line + 1))
|
||||
}
|
||||
|
||||
fn parse_bool(v: &str, line: usize) -> Result<bool, String> {
|
||||
match v.trim() {
|
||||
"true" | "1" | "yes" | "on" => Ok(true),
|
||||
"false" | "0" | "no" | "off" => Ok(false),
|
||||
other => Err(format!("bool parse failed at line {}: {other:?}", line + 1)),
|
||||
}
|
||||
}
|
||||
|
||||
fn axis_to_str(a: Axis) -> &'static str {
|
||||
match a {
|
||||
Axis::PlusX => "+x",
|
||||
Axis::MinusX => "-x",
|
||||
Axis::PlusY => "+y",
|
||||
Axis::MinusY => "-y",
|
||||
}
|
||||
}
|
||||
|
||||
fn axis_from_str(s: &str) -> Option<Axis> {
|
||||
Some(match s.trim() {
|
||||
"+x" | "x" | "PlusX" => Axis::PlusX,
|
||||
"-x" | "MinusX" => Axis::MinusX,
|
||||
"+y" | "y" | "PlusY" => Axis::PlusY,
|
||||
"-y" | "MinusY" => Axis::MinusY,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
/// parses a SPICE-format duration text into a tokio Duration, falling back to 50 ms on parse failure.
|
||||
pub fn parse_interval(text: &str) -> Duration {
|
||||
spice::parse_spice(text).map(Duration::from_secs_f64).unwrap_or(Duration::from_millis(50))
|
||||
}
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
[Format] = 4.0
|
||||
[Frequency] = 0
|
||||
[Precision] = 1e-008
|
||||
[MinAngle] = 25
|
||||
[Depth] = 5
|
||||
[LengthUnits] = millimeters
|
||||
[ProblemType] = planar
|
||||
[Coordinates] = cartesian
|
||||
[Comment] = "full 25.5-inch guitar scale inside a diametrically-magnetised NdFeB ring (inner radius 324 mm, outer 354 mm, mag along +x). six plain steel strings at the standard .009/.011/.016/.026/.036/.046 set, spanning roughly 647.7 mm from the N inner-face on the left to the S inner-face on the right. flux runs through every string in +x and closes through the ring body and surrounding air."
|
||||
[PointProps] = 0
|
||||
[BdryProps] = 1
|
||||
<BeginBdry>
|
||||
<BdryName> = "A=0"
|
||||
<BdryType> = 0
|
||||
<A_0> = 0
|
||||
<A_1> = 0
|
||||
<A_2> = 0
|
||||
<Phi> = 0
|
||||
<c0> = 0
|
||||
<c0i> = 0
|
||||
<c1> = 0
|
||||
<c1i> = 0
|
||||
<Mu_ssd> = 0
|
||||
<Sigma_ssd> = 0
|
||||
<EndBdry>
|
||||
[BlockProps] = 3
|
||||
<BeginBlock>
|
||||
<BlockName> = "Air"
|
||||
<Mu_x> = 1
|
||||
<Mu_y> = 1
|
||||
<H_c> = 0
|
||||
<H_cAngle> = 0
|
||||
<J_re> = 0
|
||||
<J_im> = 0
|
||||
<Sigma> = 0
|
||||
<d_lam> = 0
|
||||
<Phi_h> = 0
|
||||
<Phi_hx> = 0
|
||||
<Phi_hy> = 0
|
||||
<LamType> = 0
|
||||
<LamFill> = 1
|
||||
<NStrands> = 0
|
||||
<WireD> = 0
|
||||
<BHPoints> = 0
|
||||
<EndBlock>
|
||||
<BeginBlock>
|
||||
<BlockName> = "NdFeB"
|
||||
<Mu_x> = 1.05
|
||||
<Mu_y> = 1.05
|
||||
<H_c> = 915000
|
||||
<H_cAngle> = 0
|
||||
<J_re> = 0
|
||||
<J_im> = 0
|
||||
<Sigma> = 0.667
|
||||
<d_lam> = 0
|
||||
<Phi_h> = 0
|
||||
<Phi_hx> = 0
|
||||
<Phi_hy> = 0
|
||||
<LamType> = 0
|
||||
<LamFill> = 1
|
||||
<NStrands> = 0
|
||||
<WireD> = 0
|
||||
<BHPoints> = 0
|
||||
<EndBlock>
|
||||
<BeginBlock>
|
||||
<BlockName> = "Steel"
|
||||
<Mu_x> = 2500
|
||||
<Mu_y> = 2500
|
||||
<H_c> = 0
|
||||
<H_cAngle> = 0
|
||||
<J_re> = 0
|
||||
<J_im> = 0
|
||||
<Sigma> = 5.8
|
||||
<d_lam> = 0
|
||||
<Phi_h> = 0
|
||||
<Phi_hx> = 0
|
||||
<Phi_hy> = 0
|
||||
<LamType> = 0
|
||||
<LamFill> = 1
|
||||
<NStrands> = 0
|
||||
<WireD> = 0
|
||||
<BHPoints> = 0
|
||||
<EndBlock>
|
||||
[CircuitProps] = 0
|
||||
[NumPoints] = 32
|
||||
-150 -450 0 0
|
||||
800 -450 0 0
|
||||
800 450 0 0
|
||||
-150 450 0 0
|
||||
-30 0 0 0
|
||||
324 -354 0 0
|
||||
678 0 0 0
|
||||
324 354 0 0
|
||||
0.038 -4.9198 0 0
|
||||
0.049 -5.5802 0 0
|
||||
0.361 -15.2928 0 0
|
||||
0.406 -16.2072 0 0
|
||||
1.019 -25.6658 0 0
|
||||
1.114 -26.8342 0 0
|
||||
646.886 -26.8342 0 0
|
||||
646.981 -25.6658 0 0
|
||||
647.594 -16.2072 0 0
|
||||
647.639 -15.2928 0 0
|
||||
647.951 -5.5802 0 0
|
||||
647.962 -4.9198 0 0
|
||||
647.961 5.0468 0 0
|
||||
647.954 5.4532 0 0
|
||||
647.624 15.6103 0 0
|
||||
647.611 15.8897 0 0
|
||||
646.945 26.1357 0 0
|
||||
646.926 26.3643 0 0
|
||||
1.074 26.3643 0 0
|
||||
1.055 26.1357 0 0
|
||||
0.389 15.8897 0 0
|
||||
0.376 15.6103 0 0
|
||||
0.046 5.4532 0 0
|
||||
0.039 5.0468 0 0
|
||||
[NumSegments] = 16
|
||||
0 1 -1 1 0 0
|
||||
1 2 -1 1 0 0
|
||||
2 3 -1 1 0 0
|
||||
3 0 -1 1 0 0
|
||||
12 15 -1 0 0 0
|
||||
13 14 -1 0 0 0
|
||||
10 17 -1 0 0 0
|
||||
11 16 -1 0 0 0
|
||||
8 19 -1 0 0 0
|
||||
9 18 -1 0 0 0
|
||||
30 21 -1 0 0 0
|
||||
31 20 -1 0 0 0
|
||||
28 23 -1 0 0 0
|
||||
29 22 -1 0 0 0
|
||||
26 25 -1 0 0 0
|
||||
27 24 -1 0 0 0
|
||||
[NumArcSegments] = 28
|
||||
4 5 90 10 0 0 0
|
||||
5 6 90 10 0 0 0
|
||||
6 7 90 10 0 0 0
|
||||
7 4 90 10 0 0 0
|
||||
8 9 0.117 10 0 0 0
|
||||
9 10 1.719 10 0 0 0
|
||||
10 11 0.162 10 0 0 0
|
||||
11 12 1.677 10 0 0 0
|
||||
12 13 0.208 10 0 0 0
|
||||
13 14 170.494 10 0 0 0
|
||||
14 15 0.208 10 0 0 0
|
||||
15 16 1.677 10 0 0 0
|
||||
16 17 0.162 10 0 0 0
|
||||
17 18 1.719 10 0 0 0
|
||||
18 19 0.117 10 0 0 0
|
||||
19 20 1.762 10 0 0 0
|
||||
20 21 0.072 10 0 0 0
|
||||
21 22 1.798 10 0 0 0
|
||||
22 23 0.050 10 0 0 0
|
||||
23 24 1.816 10 0 0 0
|
||||
24 25 0.040 10 0 0 0
|
||||
25 26 170.664 10 0 0 0
|
||||
26 27 0.040 10 0 0 0
|
||||
27 28 1.816 10 0 0 0
|
||||
28 29 0.050 10 0 0 0
|
||||
29 30 1.798 10 0 0 0
|
||||
30 31 0.072 10 0 0 0
|
||||
31 8 1.762 10 0 0 0
|
||||
[NumHoles] = 0
|
||||
[NumBlockLabels] = 9
|
||||
-100 0 1 -1 0 0 0 1 2
|
||||
324 0 1 -1 0 0 0 1 0
|
||||
324 339 2 10 0 0 0 1 0
|
||||
324 -26.25 3 1 0 0 0 1 0
|
||||
324 -15.75 3 1 0 0 0 1 0
|
||||
324 -5.25 3 1 0 0 0 1 0
|
||||
324 5.25 3 1 0 0 0 1 0
|
||||
324 15.75 3 1 0 0 0 1 0
|
||||
324 26.25 3 1 0 0 0 1 0
|
||||
|
|
@ -0,0 +1,290 @@
|
|||
[Format] = 4.0
|
||||
[Frequency] = 0
|
||||
[Precision] = 1e-008
|
||||
[MinAngle] = 25
|
||||
[Depth] = 5
|
||||
[LengthUnits] = millimeters
|
||||
[ProblemType] = planar
|
||||
[Coordinates] = cartesian
|
||||
[Comment] = "six plain steel strings (.010-.046 inch gauges) over a 25.5-inch scale, anchored to a NdFeB bar magnet at the left (mag_dir 0, N facing the strings) and a passive steel block at the right. small NdFeB disc magnets sit above and below each string at 5/7 of the way toward the steel block, all magnetised in -y so the disc above each string has its N face downward and the disc below has its S face upward, driving a strong vertical bias field through each string at the probe point. flip every disc mag_dir from 270 to 90 to invert the bias direction."
|
||||
[PointProps] = 0
|
||||
[BdryProps] = 1
|
||||
<BeginBdry>
|
||||
<BdryName> = "A=0"
|
||||
<BdryType> = 0
|
||||
<A_0> = 0
|
||||
<A_1> = 0
|
||||
<A_2> = 0
|
||||
<Phi> = 0
|
||||
<c0> = 0
|
||||
<c0i> = 0
|
||||
<c1> = 0
|
||||
<c1i> = 0
|
||||
<Mu_ssd> = 0
|
||||
<Sigma_ssd> = 0
|
||||
<EndBdry>
|
||||
[BlockProps] = 3
|
||||
<BeginBlock>
|
||||
<BlockName> = "Air"
|
||||
<Mu_x> = 1
|
||||
<Mu_y> = 1
|
||||
<H_c> = 0
|
||||
<H_cAngle> = 0
|
||||
<J_re> = 0
|
||||
<J_im> = 0
|
||||
<Sigma> = 0
|
||||
<d_lam> = 0
|
||||
<Phi_h> = 0
|
||||
<Phi_hx> = 0
|
||||
<Phi_hy> = 0
|
||||
<LamType> = 0
|
||||
<LamFill> = 1
|
||||
<NStrands> = 0
|
||||
<WireD> = 0
|
||||
<BHPoints> = 0
|
||||
<EndBlock>
|
||||
<BeginBlock>
|
||||
<BlockName> = "NdFeB"
|
||||
<Mu_x> = 1.05
|
||||
<Mu_y> = 1.05
|
||||
<H_c> = 915000
|
||||
<H_cAngle> = 0
|
||||
<J_re> = 0
|
||||
<J_im> = 0
|
||||
<Sigma> = 0.667
|
||||
<d_lam> = 0
|
||||
<Phi_h> = 0
|
||||
<Phi_hx> = 0
|
||||
<Phi_hy> = 0
|
||||
<LamType> = 0
|
||||
<LamFill> = 1
|
||||
<NStrands> = 0
|
||||
<WireD> = 0
|
||||
<BHPoints> = 0
|
||||
<EndBlock>
|
||||
<BeginBlock>
|
||||
<BlockName> = "Steel"
|
||||
<Mu_x> = 2500
|
||||
<Mu_y> = 2500
|
||||
<H_c> = 0
|
||||
<H_cAngle> = 0
|
||||
<J_re> = 0
|
||||
<J_im> = 0
|
||||
<Sigma> = 5.8
|
||||
<d_lam> = 0
|
||||
<Phi_h> = 0
|
||||
<Phi_hx> = 0
|
||||
<Phi_hy> = 0
|
||||
<LamType> = 0
|
||||
<LamFill> = 1
|
||||
<NStrands> = 0
|
||||
<WireD> = 0
|
||||
<BHPoints> = 0
|
||||
<EndBlock>
|
||||
[CircuitProps] = 0
|
||||
[NumPoints] = 84
|
||||
-100 -100 0 0
|
||||
750 -100 0 0
|
||||
750 100 0 0
|
||||
-100 100 0 0
|
||||
-30 -30 0 0
|
||||
-30 30 0 0
|
||||
0 -30 0 0
|
||||
0 -26.834 0 0
|
||||
0 -25.666 0 0
|
||||
0 -16.207 0 0
|
||||
0 -15.293 0 0
|
||||
0 -5.580 0 0
|
||||
0 -4.920 0 0
|
||||
0 5.034 0 0
|
||||
0 5.466 0 0
|
||||
0 15.585 0 0
|
||||
0 15.915 0 0
|
||||
0 26.123 0 0
|
||||
0 26.377 0 0
|
||||
0 30 0 0
|
||||
647.7 -30 0 0
|
||||
647.7 -26.834 0 0
|
||||
647.7 -25.666 0 0
|
||||
647.7 -16.207 0 0
|
||||
647.7 -15.293 0 0
|
||||
647.7 -5.580 0 0
|
||||
647.7 -4.920 0 0
|
||||
647.7 5.034 0 0
|
||||
647.7 5.466 0 0
|
||||
647.7 15.585 0 0
|
||||
647.7 15.915 0 0
|
||||
647.7 26.123 0 0
|
||||
647.7 26.377 0 0
|
||||
647.7 30 0 0
|
||||
677.7 -30 0 0
|
||||
677.7 30 0 0
|
||||
461.14 27.377 0 0
|
||||
464.14 27.377 0 0
|
||||
464.14 29.377 0 0
|
||||
461.14 29.377 0 0
|
||||
461.14 23.123 0 0
|
||||
464.14 23.123 0 0
|
||||
464.14 25.123 0 0
|
||||
461.14 25.123 0 0
|
||||
461.14 16.915 0 0
|
||||
464.14 16.915 0 0
|
||||
464.14 18.915 0 0
|
||||
461.14 18.915 0 0
|
||||
461.14 12.585 0 0
|
||||
464.14 12.585 0 0
|
||||
464.14 14.585 0 0
|
||||
461.14 14.585 0 0
|
||||
461.14 6.466 0 0
|
||||
464.14 6.466 0 0
|
||||
464.14 8.466 0 0
|
||||
461.14 8.466 0 0
|
||||
461.14 2.034 0 0
|
||||
464.14 2.034 0 0
|
||||
464.14 4.034 0 0
|
||||
461.14 4.034 0 0
|
||||
461.14 -3.920 0 0
|
||||
464.14 -3.920 0 0
|
||||
464.14 -1.920 0 0
|
||||
461.14 -1.920 0 0
|
||||
461.14 -8.580 0 0
|
||||
464.14 -8.580 0 0
|
||||
464.14 -6.580 0 0
|
||||
461.14 -6.580 0 0
|
||||
461.14 -14.293 0 0
|
||||
464.14 -14.293 0 0
|
||||
464.14 -12.293 0 0
|
||||
461.14 -12.293 0 0
|
||||
461.14 -19.207 0 0
|
||||
464.14 -19.207 0 0
|
||||
464.14 -17.207 0 0
|
||||
461.14 -17.207 0 0
|
||||
461.14 -24.666 0 0
|
||||
464.14 -24.666 0 0
|
||||
464.14 -22.666 0 0
|
||||
461.14 -22.666 0 0
|
||||
461.14 -29.834 0 0
|
||||
464.14 -29.834 0 0
|
||||
464.14 -27.834 0 0
|
||||
461.14 -27.834 0 0
|
||||
[NumSegments] = 96
|
||||
0 1 -1 1 0 0
|
||||
1 2 -1 1 0 0
|
||||
2 3 -1 1 0 0
|
||||
3 0 -1 1 0 0
|
||||
4 5 -1 0 0 0
|
||||
5 19 -1 0 0 0
|
||||
6 4 -1 0 0 0
|
||||
19 18 -1 0 0 0
|
||||
18 17 -1 0 0 0
|
||||
17 16 -1 0 0 0
|
||||
16 15 -1 0 0 0
|
||||
15 14 -1 0 0 0
|
||||
14 13 -1 0 0 0
|
||||
13 12 -1 0 0 0
|
||||
12 11 -1 0 0 0
|
||||
11 10 -1 0 0 0
|
||||
10 9 -1 0 0 0
|
||||
9 8 -1 0 0 0
|
||||
8 7 -1 0 0 0
|
||||
7 6 -1 0 0 0
|
||||
20 34 -1 0 0 0
|
||||
34 35 -1 0 0 0
|
||||
35 33 -1 0 0 0
|
||||
33 32 -1 0 0 0
|
||||
32 31 -1 0 0 0
|
||||
31 30 -1 0 0 0
|
||||
30 29 -1 0 0 0
|
||||
29 28 -1 0 0 0
|
||||
28 27 -1 0 0 0
|
||||
27 26 -1 0 0 0
|
||||
26 25 -1 0 0 0
|
||||
25 24 -1 0 0 0
|
||||
24 23 -1 0 0 0
|
||||
23 22 -1 0 0 0
|
||||
22 21 -1 0 0 0
|
||||
21 20 -1 0 0 0
|
||||
8 22 -1 0 0 0
|
||||
7 21 -1 0 0 0
|
||||
10 24 -1 0 0 0
|
||||
9 23 -1 0 0 0
|
||||
12 26 -1 0 0 0
|
||||
11 25 -1 0 0 0
|
||||
14 28 -1 0 0 0
|
||||
13 27 -1 0 0 0
|
||||
16 30 -1 0 0 0
|
||||
15 29 -1 0 0 0
|
||||
18 32 -1 0 0 0
|
||||
17 31 -1 0 0 0
|
||||
36 37 -1 0 0 0
|
||||
37 38 -1 0 0 0
|
||||
38 39 -1 0 0 0
|
||||
39 36 -1 0 0 0
|
||||
40 41 -1 0 0 0
|
||||
41 42 -1 0 0 0
|
||||
42 43 -1 0 0 0
|
||||
43 40 -1 0 0 0
|
||||
44 45 -1 0 0 0
|
||||
45 46 -1 0 0 0
|
||||
46 47 -1 0 0 0
|
||||
47 44 -1 0 0 0
|
||||
48 49 -1 0 0 0
|
||||
49 50 -1 0 0 0
|
||||
50 51 -1 0 0 0
|
||||
51 48 -1 0 0 0
|
||||
52 53 -1 0 0 0
|
||||
53 54 -1 0 0 0
|
||||
54 55 -1 0 0 0
|
||||
55 52 -1 0 0 0
|
||||
56 57 -1 0 0 0
|
||||
57 58 -1 0 0 0
|
||||
58 59 -1 0 0 0
|
||||
59 56 -1 0 0 0
|
||||
60 61 -1 0 0 0
|
||||
61 62 -1 0 0 0
|
||||
62 63 -1 0 0 0
|
||||
63 60 -1 0 0 0
|
||||
64 65 -1 0 0 0
|
||||
65 66 -1 0 0 0
|
||||
66 67 -1 0 0 0
|
||||
67 64 -1 0 0 0
|
||||
68 69 -1 0 0 0
|
||||
69 70 -1 0 0 0
|
||||
70 71 -1 0 0 0
|
||||
71 68 -1 0 0 0
|
||||
72 73 -1 0 0 0
|
||||
73 74 -1 0 0 0
|
||||
74 75 -1 0 0 0
|
||||
75 72 -1 0 0 0
|
||||
76 77 -1 0 0 0
|
||||
77 78 -1 0 0 0
|
||||
78 79 -1 0 0 0
|
||||
79 76 -1 0 0 0
|
||||
80 81 -1 0 0 0
|
||||
81 82 -1 0 0 0
|
||||
82 83 -1 0 0 0
|
||||
83 80 -1 0 0 0
|
||||
[NumArcSegments] = 0
|
||||
[NumHoles] = 0
|
||||
[NumBlockLabels] = 21
|
||||
-50 -50 1 -1 0 0 0 1 2
|
||||
-15 0 2 5 0 0 0 1 0
|
||||
662.7 0 3 5 0 0 0 1 0
|
||||
320 -26.25 3 1 0 0 0 1 0
|
||||
320 -15.75 3 1 0 0 0 1 0
|
||||
320 -5.25 3 1 0 0 0 1 0
|
||||
320 5.25 3 1 0 0 0 1 0
|
||||
320 15.75 3 1 0 0 0 1 0
|
||||
320 26.25 3 1 0 0 0 1 0
|
||||
462.64 28.377 2 0.5 0 270 0 1 0
|
||||
462.64 24.123 2 0.5 0 270 0 1 0
|
||||
462.64 17.915 2 0.5 0 270 0 1 0
|
||||
462.64 13.585 2 0.5 0 270 0 1 0
|
||||
462.64 7.466 2 0.5 0 270 0 1 0
|
||||
462.64 3.034 2 0.5 0 270 0 1 0
|
||||
462.64 -2.920 2 0.5 0 270 0 1 0
|
||||
462.64 -7.580 2 0.5 0 270 0 1 0
|
||||
462.64 -13.293 2 0.5 0 270 0 1 0
|
||||
462.64 -18.207 2 0.5 0 270 0 1 0
|
||||
462.64 -23.666 2 0.5 0 270 0 1 0
|
||||
462.64 -28.834 2 0.5 0 270 0 1 0
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
[LengthUnits] = millimeters
|
||||
[ProblemType] = planar
|
||||
[Coordinates] = cartesian
|
||||
[Comment] = "guitar pickup geometry: 25.5-inch scale, 6 plain steel strings at real gauges 0.010-0.046 inch, NdFeB bar magnets at the neck and bridge ends. Both magnets present their N face inward toward the strings so flux is injected into each end of every string and cancels at the midpoint."
|
||||
[Comment] = "diametrically-magnetised NdFeB ring (inner radius 81 mm, outer 111 mm, mag along +x) with six plain steel strings (.010-.046 inch gauges) stretched across the bore. each string spans about 162 mm from the N inner-face on the left to the S inner-face on the right. flux runs through every string in +x and closes through the rest of the ring body and surrounding air."
|
||||
[PointProps] = 0
|
||||
[BdryProps] = 1
|
||||
<BeginBdry>
|
||||
|
|
@ -82,101 +82,93 @@
|
|||
<BHPoints> = 0
|
||||
<EndBlock>
|
||||
[CircuitProps] = 0
|
||||
[NumPoints] = 36
|
||||
-100 -100 0 0
|
||||
750 -100 0 0
|
||||
750 100 0 0
|
||||
-100 100 0 0
|
||||
-30 -30 0 0
|
||||
-30 30 0 0
|
||||
0 -30 0 0
|
||||
0 -26.377 0 0
|
||||
0 -26.123 0 0
|
||||
0 -15.915 0 0
|
||||
0 -15.585 0 0
|
||||
0 -5.466 0 0
|
||||
0 -5.034 0 0
|
||||
0 4.920 0 0
|
||||
0 5.580 0 0
|
||||
0 15.293 0 0
|
||||
0 16.207 0 0
|
||||
0 25.666 0 0
|
||||
0 26.834 0 0
|
||||
0 30 0 0
|
||||
647.7 -30 0 0
|
||||
647.7 -26.377 0 0
|
||||
647.7 -26.123 0 0
|
||||
647.7 -15.915 0 0
|
||||
647.7 -15.585 0 0
|
||||
647.7 -5.466 0 0
|
||||
647.7 -5.034 0 0
|
||||
647.7 4.920 0 0
|
||||
647.7 5.580 0 0
|
||||
647.7 15.293 0 0
|
||||
647.7 16.207 0 0
|
||||
647.7 25.666 0 0
|
||||
647.7 26.834 0 0
|
||||
647.7 30 0 0
|
||||
677.7 -30 0 0
|
||||
677.7 30 0 0
|
||||
[NumSegments] = 48
|
||||
[NumPoints] = 32
|
||||
-150 -150 0 0
|
||||
312 -150 0 0
|
||||
312 150 0 0
|
||||
-150 150 0 0
|
||||
-30 0 0 0
|
||||
81 -111 0 0
|
||||
192 0 0 0
|
||||
81 111 0 0
|
||||
0.149 -4.920 0 0
|
||||
0.192 -5.580 0 0
|
||||
1.457 -15.293 0 0
|
||||
1.638 -16.207 0 0
|
||||
4.174 -25.666 0 0
|
||||
4.574 -26.834 0 0
|
||||
157.426 -26.834 0 0
|
||||
157.826 -25.666 0 0
|
||||
160.362 -16.207 0 0
|
||||
160.543 -15.293 0 0
|
||||
161.808 -5.580 0 0
|
||||
161.851 -4.920 0 0
|
||||
161.844 5.034 0 0
|
||||
161.816 5.466 0 0
|
||||
160.487 15.585 0 0
|
||||
160.421 15.915 0 0
|
||||
157.672 26.123 0 0
|
||||
157.585 26.377 0 0
|
||||
4.415 26.377 0 0
|
||||
4.328 26.123 0 0
|
||||
1.579 15.915 0 0
|
||||
1.513 15.585 0 0
|
||||
0.184 5.466 0 0
|
||||
0.156 5.034 0 0
|
||||
[NumSegments] = 16
|
||||
0 1 -1 1 0 0
|
||||
1 2 -1 1 0 0
|
||||
2 3 -1 1 0 0
|
||||
3 0 -1 1 0 0
|
||||
4 5 -1 0 0 0
|
||||
5 19 -1 0 0 0
|
||||
4 6 -1 0 0 0
|
||||
6 7 -1 0 0 0
|
||||
7 8 -1 0 0 0
|
||||
8 9 -1 0 0 0
|
||||
9 10 -1 0 0 0
|
||||
10 11 -1 0 0 0
|
||||
11 12 -1 0 0 0
|
||||
12 13 -1 0 0 0
|
||||
12 15 -1 0 0 0
|
||||
13 14 -1 0 0 0
|
||||
14 15 -1 0 0 0
|
||||
15 16 -1 0 0 0
|
||||
16 17 -1 0 0 0
|
||||
17 18 -1 0 0 0
|
||||
18 19 -1 0 0 0
|
||||
20 21 -1 0 0 0
|
||||
21 22 -1 0 0 0
|
||||
22 23 -1 0 0 0
|
||||
23 24 -1 0 0 0
|
||||
24 25 -1 0 0 0
|
||||
25 26 -1 0 0 0
|
||||
26 27 -1 0 0 0
|
||||
27 28 -1 0 0 0
|
||||
28 29 -1 0 0 0
|
||||
29 30 -1 0 0 0
|
||||
30 31 -1 0 0 0
|
||||
31 32 -1 0 0 0
|
||||
32 33 -1 0 0 0
|
||||
20 34 -1 0 0 0
|
||||
34 35 -1 0 0 0
|
||||
33 35 -1 0 0 0
|
||||
7 21 -1 0 0 0
|
||||
8 22 -1 0 0 0
|
||||
9 23 -1 0 0 0
|
||||
10 24 -1 0 0 0
|
||||
11 25 -1 0 0 0
|
||||
12 26 -1 0 0 0
|
||||
13 27 -1 0 0 0
|
||||
14 28 -1 0 0 0
|
||||
15 29 -1 0 0 0
|
||||
16 30 -1 0 0 0
|
||||
17 31 -1 0 0 0
|
||||
18 32 -1 0 0 0
|
||||
[NumArcSegments] = 0
|
||||
10 17 -1 0 0 0
|
||||
11 16 -1 0 0 0
|
||||
8 19 -1 0 0 0
|
||||
9 18 -1 0 0 0
|
||||
30 21 -1 0 0 0
|
||||
31 20 -1 0 0 0
|
||||
28 23 -1 0 0 0
|
||||
29 22 -1 0 0 0
|
||||
26 25 -1 0 0 0
|
||||
27 24 -1 0 0 0
|
||||
[NumArcSegments] = 28
|
||||
4 5 90 10 0 0 0
|
||||
5 6 90 10 0 0 0
|
||||
6 7 90 10 0 0 0
|
||||
7 4 90 10 0 0 0
|
||||
8 9 0.47 10 0 0 0
|
||||
9 10 6.94 10 0 0 0
|
||||
10 11 0.66 10 0 0 0
|
||||
11 12 6.94 10 0 0 0
|
||||
12 13 0.83 10 0 0 0
|
||||
13 14 141.36 10 0 0 0
|
||||
14 15 0.83 10 0 0 0
|
||||
15 16 6.94 10 0 0 0
|
||||
16 17 0.66 10 0 0 0
|
||||
17 18 6.94 10 0 0 0
|
||||
18 19 0.47 10 0 0 0
|
||||
19 20 7.04 10 0 0 0
|
||||
20 21 0.31 10 0 0 0
|
||||
21 22 7.23 10 0 0 0
|
||||
22 23 0.23 10 0 0 0
|
||||
23 24 7.48 10 0 0 0
|
||||
24 25 0.19 10 0 0 0
|
||||
25 26 142.00 10 0 0 0
|
||||
26 27 0.19 10 0 0 0
|
||||
27 28 7.48 10 0 0 0
|
||||
28 29 0.23 10 0 0 0
|
||||
29 30 7.23 10 0 0 0
|
||||
30 31 0.31 10 0 0 0
|
||||
31 8 7.04 10 0 0 0
|
||||
[NumHoles] = 0
|
||||
[NumBlockLabels] = 9
|
||||
325 75 1 -1 0 0 0 1 2
|
||||
-15 0 2 5 0 0 0 1 0
|
||||
662.7 0 2 5 0 180 0 1 0
|
||||
325 -26.25 3 1 0 0 0 1 0
|
||||
325 -15.75 3 1 0 0 0 1 0
|
||||
325 -5.25 3 1 0 0 0 1 0
|
||||
325 5.25 3 1 0 0 0 1 0
|
||||
325 15.75 3 1 0 0 0 1 0
|
||||
325 26.25 3 1 0 0 0 1 0
|
||||
-100 0 1 -1 0 0 0 1 2
|
||||
81 0 1 -1 0 0 0 1 0
|
||||
81 96 2 5 0 0 0 1 0
|
||||
81 -26.25 3 1 0 0 0 1 0
|
||||
81 -15.75 3 1 0 0 0 1 0
|
||||
81 -5.25 3 1 0 0 0 1 0
|
||||
81 5.25 3 1 0 0 0 1 0
|
||||
81 15.75 3 1 0 0 0 1 0
|
||||
81 26.25 3 1 0 0 0 1 0
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
# femm42 simulation metadata
|
||||
# track node indices reference the doc loaded above; editing the doc may invalidate them.
|
||||
|
||||
[source]
|
||||
fem = /Volumes/External/Repositories/femm42src/examples/guitar_strings.fem
|
||||
|
||||
[sim]
|
||||
dt = 50us
|
||||
interval = 16.667ms
|
||||
subdivisions = 20
|
||||
buffer_size = 243
|
||||
match_cycle = true
|
||||
loop_playback = true
|
||||
fundamental_hz = 82.41
|
||||
|
||||
[track]
|
||||
label = track 1
|
||||
anchor_a = 17
|
||||
anchor_b = 30
|
||||
axis = +y
|
||||
expression = series(s, t, 82.41, 3)
|
||||
members = 18 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
|
||||
|
||||
[track]
|
||||
label = track 2
|
||||
anchor_a = 15
|
||||
anchor_b = 28
|
||||
axis = +y
|
||||
expression = series(s, t, 103.83, 3)
|
||||
members = 16 29 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
|
||||
|
||||
[track]
|
||||
label = track 3
|
||||
anchor_a = 13
|
||||
anchor_b = 26
|
||||
axis = +y
|
||||
expression = series(s, t, 123.47, 3)
|
||||
members = 14 27 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
|
||||
|
||||
[track]
|
||||
label = track 4
|
||||
anchor_a = 11
|
||||
anchor_b = 24
|
||||
axis = +y
|
||||
expression = series(s, t, 164.81, 3)
|
||||
members = 12 25 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
|
||||
|
||||
[track]
|
||||
label = track 5
|
||||
anchor_a = 9
|
||||
anchor_b = 22
|
||||
axis = +y
|
||||
expression = series(s, t, 246.94, 3)
|
||||
members = 10 23 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
|
||||
|
||||
[track]
|
||||
label = track 6
|
||||
anchor_a = 7
|
||||
anchor_b = 20
|
||||
axis = +y
|
||||
expression = series(s, t, 329.63, 3)
|
||||
members = 8 21 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
|
||||
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
# femm42 simulation metadata
|
||||
# track node indices reference the doc loaded above; editing the doc may invalidate them.
|
||||
|
||||
[source]
|
||||
fem = /Volumes/External/Repositories/femm42src/examples/guitar_bar_and_discs.fem
|
||||
|
||||
[sim]
|
||||
dt = 50us
|
||||
interval = 16.667ms
|
||||
subdivisions = 20
|
||||
buffer_size = 243
|
||||
match_cycle = true
|
||||
loop_playback = true
|
||||
fundamental_hz = 82.41
|
||||
|
||||
[track]
|
||||
label = track 1
|
||||
anchor_a = 17
|
||||
anchor_b = 30
|
||||
axis = +y
|
||||
expression = series(s, t, 82.41, 5)
|
||||
members = 18 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
|
||||
|
||||
[track]
|
||||
label = track 2
|
||||
anchor_a = 15
|
||||
anchor_b = 28
|
||||
axis = +y
|
||||
expression = series(s, t, 103.83, 5)
|
||||
members = 16 29 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
|
||||
|
||||
[track]
|
||||
label = track 3
|
||||
anchor_a = 13
|
||||
anchor_b = 26
|
||||
axis = +y
|
||||
expression = series(s, t, 123.47, 5)
|
||||
members = 14 27 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
|
||||
|
||||
[track]
|
||||
label = track 4
|
||||
anchor_a = 11
|
||||
anchor_b = 24
|
||||
axis = +y
|
||||
expression = series(s, t, 164.81, 5)
|
||||
members = 12 25 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
|
||||
|
||||
[track]
|
||||
label = track 5
|
||||
anchor_a = 9
|
||||
anchor_b = 22
|
||||
axis = +y
|
||||
expression = series(s, t, 246.94, 5)
|
||||
members = 10 23 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
|
||||
|
||||
[track]
|
||||
label = track 6
|
||||
anchor_a = 7
|
||||
anchor_b = 20
|
||||
axis = +y
|
||||
expression = series(s, t, 329.63, 5)
|
||||
members = 8 21 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
|
||||
|
||||
|
|
@ -0,0 +1,702 @@
|
|||
# femm42 session v1
|
||||
[base-doc]
|
||||
[Format] = 4.00000000000000000e0
|
||||
[Frequency] = 0.00000000000000000e0
|
||||
[Precision] = 1.00000000000000002e-8
|
||||
[MinAngle] = 2.50000000000000000e1
|
||||
[DoSmartMesh] = 1
|
||||
[Depth] = 5.00000000000000000e0
|
||||
[LengthUnits] = millimeters
|
||||
[ProblemType] = planar
|
||||
[Coordinates] = cartesian
|
||||
[ACSolver] = 0
|
||||
[PrevType] = 0
|
||||
[PrevSoln] = ""
|
||||
[Comment] = "diametrically-magnetised NdFeB ring (inner radius 81 mm, outer 111 mm, mag along +x) with six plain steel strings (.010-.046 inch gauges) stretched across the bore. each string spans about 162 mm from the N inner-face on the left to the S inner-face on the right. flux runs through every string in +x and closes through the rest of the ring body and surrounding air."
|
||||
[PointProps] = 0
|
||||
[BdryProps] = 1
|
||||
<BeginBdry>
|
||||
<BdryName> = "A=0"
|
||||
<BdryType> = 0
|
||||
<A_0> = 0.00000000000000000e0
|
||||
<A_1> = 0.00000000000000000e0
|
||||
<A_2> = 0.00000000000000000e0
|
||||
<Phi> = 0.00000000000000000e0
|
||||
<c0> = 0.00000000000000000e0
|
||||
<c0i> = 0.00000000000000000e0
|
||||
<c1> = 0.00000000000000000e0
|
||||
<c1i> = 0.00000000000000000e0
|
||||
<Mu_ssd> = 0.00000000000000000e0
|
||||
<Sigma_ssd> = 0.00000000000000000e0
|
||||
<innerangle> = 0.00000000000000000e0
|
||||
<outerangle> = 0.00000000000000000e0
|
||||
<EndBdry>
|
||||
[BlockProps] = 3
|
||||
<BeginBlock>
|
||||
<BlockName> = "Air"
|
||||
<Mu_x> = 1.00000000000000000e0
|
||||
<Mu_y> = 1.00000000000000000e0
|
||||
<H_c> = 0.00000000000000000e0
|
||||
<H_cAngle> = 0.00000000000000000e0
|
||||
<J_re> = 0.00000000000000000e0
|
||||
<J_im> = 0.00000000000000000e0
|
||||
<Sigma> = 0.00000000000000000e0
|
||||
<d_lam> = 0.00000000000000000e0
|
||||
<Phi_h> = 0.00000000000000000e0
|
||||
<Phi_hx> = 0.00000000000000000e0
|
||||
<Phi_hy> = 0.00000000000000000e0
|
||||
<LamType> = 0
|
||||
<LamFill> = 1.00000000000000000e0
|
||||
<NStrands> = 0
|
||||
<WireD> = 0.00000000000000000e0
|
||||
<BHPoints> = 0
|
||||
<EndBlock>
|
||||
<BeginBlock>
|
||||
<BlockName> = "NdFeB"
|
||||
<Mu_x> = 1.05000000000000004e0
|
||||
<Mu_y> = 1.05000000000000004e0
|
||||
<H_c> = 9.15000000000000000e5
|
||||
<H_cAngle> = 0.00000000000000000e0
|
||||
<J_re> = 0.00000000000000000e0
|
||||
<J_im> = 0.00000000000000000e0
|
||||
<Sigma> = 6.67000000000000037e-1
|
||||
<d_lam> = 0.00000000000000000e0
|
||||
<Phi_h> = 0.00000000000000000e0
|
||||
<Phi_hx> = 0.00000000000000000e0
|
||||
<Phi_hy> = 0.00000000000000000e0
|
||||
<LamType> = 0
|
||||
<LamFill> = 1.00000000000000000e0
|
||||
<NStrands> = 0
|
||||
<WireD> = 0.00000000000000000e0
|
||||
<BHPoints> = 0
|
||||
<EndBlock>
|
||||
<BeginBlock>
|
||||
<BlockName> = "Steel"
|
||||
<Mu_x> = 2.50000000000000000e3
|
||||
<Mu_y> = 2.50000000000000000e3
|
||||
<H_c> = 0.00000000000000000e0
|
||||
<H_cAngle> = 0.00000000000000000e0
|
||||
<J_re> = 0.00000000000000000e0
|
||||
<J_im> = 0.00000000000000000e0
|
||||
<Sigma> = 5.79999999999999982e0
|
||||
<d_lam> = 0.00000000000000000e0
|
||||
<Phi_h> = 0.00000000000000000e0
|
||||
<Phi_hx> = 0.00000000000000000e0
|
||||
<Phi_hy> = 0.00000000000000000e0
|
||||
<LamType> = 0
|
||||
<LamFill> = 1.00000000000000000e0
|
||||
<NStrands> = 0
|
||||
<WireD> = 0.00000000000000000e0
|
||||
<BHPoints> = 0
|
||||
<EndBlock>
|
||||
[CircuitProps] = 0
|
||||
[NumPoints] = 260
|
||||
-1.50000000000000000e2 -1.50000000000000000e2 0 0
|
||||
3.12000000000000000e2 -1.50000000000000000e2 0 0
|
||||
3.12000000000000000e2 1.50000000000000000e2 0 0
|
||||
-1.50000000000000000e2 1.50000000000000000e2 0 0
|
||||
-3.00000000000000000e1 0.00000000000000000e0 0 0
|
||||
8.10000000000000000e1 -1.11000000000000000e2 0 0
|
||||
1.92000000000000000e2 0.00000000000000000e0 0 0
|
||||
8.10000000000000000e1 1.11000000000000000e2 0 0
|
||||
1.48999999999999994e-1 -4.91999999999999993e0 0 0
|
||||
1.92000000000000004e-1 -5.58000000000000007e0 0 0
|
||||
1.45700000000000007e0 -1.52929999999999993e1 0 0
|
||||
1.63799999999999990e0 -1.62070000000000007e1 0 0
|
||||
4.17400000000000038e0 -2.56660000000000004e1 0 0
|
||||
4.57399999999999984e0 -2.68339999999999996e1 0 0
|
||||
1.57425999999999988e2 -2.68339999999999996e1 0 0
|
||||
1.57825999999999993e2 -2.56660000000000004e1 0 0
|
||||
1.60361999999999995e2 -1.62070000000000007e1 0 0
|
||||
1.60543000000000006e2 -1.52929999999999993e1 0 0
|
||||
1.61807999999999993e2 -5.58000000000000007e0 0 0
|
||||
1.61850999999999999e2 -4.91999999999999993e0 0 0
|
||||
1.61843999999999994e2 5.03399999999999981e0 0 0
|
||||
1.61816000000000003e2 5.46600000000000019e0 0 0
|
||||
1.60486999999999995e2 1.55850000000000009e1 0 0
|
||||
1.60420999999999992e2 1.59149999999999991e1 0 0
|
||||
1.57671999999999997e2 2.61230000000000011e1 0 0
|
||||
1.57585000000000008e2 2.63769999999999989e1 0 0
|
||||
4.41500000000000004e0 2.63769999999999989e1 0 0
|
||||
4.32800000000000029e0 2.61230000000000011e1 0 0
|
||||
1.57899999999999996e0 1.59149999999999991e1 0 0
|
||||
1.51299999999999990e0 1.55850000000000009e1 0 0
|
||||
1.83999999999999997e-1 5.46600000000000019e0 0 0
|
||||
1.56000000000000000e-1 5.03399999999999981e0 0 0
|
||||
1.22165999999999997e1 -2.68339999999999996e1 0 0
|
||||
1.98591999999999977e1 -2.68339999999999996e1 0 0
|
||||
2.75017999999999958e1 -2.68339999999999996e1 0 0
|
||||
3.51443999999999974e1 -2.68339999999999996e1 0 0
|
||||
4.27869999999999919e1 -2.68339999999999996e1 0 0
|
||||
5.04295999999999935e1 -2.68339999999999996e1 0 0
|
||||
5.80721999999999881e1 -2.68339999999999996e1 0 0
|
||||
6.57147999999999968e1 -2.68339999999999996e1 0 0
|
||||
7.33573999999999842e1 -2.68339999999999996e1 0 0
|
||||
8.09999999999999858e1 -2.68339999999999996e1 0 0
|
||||
8.86425999999999874e1 -2.68339999999999996e1 0 0
|
||||
9.62851999999999890e1 -2.68339999999999996e1 0 0
|
||||
1.03927799999999991e2 -2.68339999999999996e1 0 0
|
||||
1.11570399999999978e2 -2.68339999999999996e1 0 0
|
||||
1.19212999999999980e2 -2.68339999999999996e1 0 0
|
||||
1.26855599999999981e2 -2.68339999999999996e1 0 0
|
||||
1.34498199999999997e2 -2.68339999999999996e1 0 0
|
||||
1.42140799999999984e2 -2.68339999999999996e1 0 0
|
||||
1.49783400000000000e2 -2.68339999999999996e1 0 0
|
||||
1.18566000000000003e1 -2.56660000000000004e1 0 0
|
||||
1.95391999999999975e1 -2.56660000000000004e1 0 0
|
||||
2.72217999999999947e1 -2.56660000000000004e1 0 0
|
||||
3.49043999999999954e1 -2.56660000000000004e1 0 0
|
||||
4.25869999999999962e1 -2.56660000000000004e1 0 0
|
||||
5.02695999999999898e1 -2.56660000000000004e1 0 0
|
||||
5.79521999999999906e1 -2.56660000000000004e1 0 0
|
||||
6.56347999999999985e1 -2.56660000000000004e1 0 0
|
||||
7.33173999999999921e1 -2.56660000000000004e1 0 0
|
||||
8.10000000000000000e1 -2.56660000000000004e1 0 0
|
||||
8.86825999999999937e1 -2.56660000000000004e1 0 0
|
||||
9.63651999999999873e1 -2.56660000000000004e1 0 0
|
||||
1.04047799999999995e2 -2.56660000000000004e1 0 0
|
||||
1.11730399999999989e2 -2.56660000000000004e1 0 0
|
||||
1.19412999999999997e2 -2.56660000000000004e1 0 0
|
||||
1.27095599999999990e2 -2.56660000000000004e1 0 0
|
||||
1.34778199999999998e2 -2.56660000000000004e1 0 0
|
||||
1.42460799999999978e2 -2.56660000000000004e1 0 0
|
||||
1.50143399999999986e2 -2.56660000000000004e1 0 0
|
||||
9.57419999999999938e0 -1.62070000000000007e1 0 0
|
||||
1.75103999999999971e1 -1.62070000000000007e1 0 0
|
||||
2.54465999999999966e1 -1.62070000000000007e1 0 0
|
||||
3.33827999999999960e1 -1.62070000000000007e1 0 0
|
||||
4.13189999999999955e1 -1.62070000000000007e1 0 0
|
||||
4.92551999999999950e1 -1.62070000000000007e1 0 0
|
||||
5.71913999999999945e1 -1.62070000000000007e1 0 0
|
||||
6.51276000000000010e1 -1.62070000000000007e1 0 0
|
||||
7.30638000000000005e1 -1.62070000000000007e1 0 0
|
||||
8.10000000000000000e1 -1.62070000000000007e1 0 0
|
||||
8.89361999999999995e1 -1.62070000000000007e1 0 0
|
||||
9.68723999999999990e1 -1.62070000000000007e1 0 0
|
||||
1.04808599999999998e2 -1.62070000000000007e1 0 0
|
||||
1.12744799999999998e2 -1.62070000000000007e1 0 0
|
||||
1.20680999999999997e2 -1.62070000000000007e1 0 0
|
||||
1.28617199999999997e2 -1.62070000000000007e1 0 0
|
||||
1.36553399999999982e2 -1.62070000000000007e1 0 0
|
||||
1.44489599999999996e2 -1.62070000000000007e1 0 0
|
||||
1.52425800000000010e2 -1.62070000000000007e1 0 0
|
||||
9.41130000000000067e0 -1.52929999999999993e1 0 0
|
||||
1.73656000000000006e1 -1.52929999999999993e1 0 0
|
||||
2.53199000000000041e1 -1.52929999999999993e1 0 0
|
||||
3.32742000000000004e1 -1.52929999999999993e1 0 0
|
||||
4.12285000000000039e1 -1.52929999999999993e1 0 0
|
||||
4.91828000000000074e1 -1.52929999999999993e1 0 0
|
||||
5.71371000000000038e1 -1.52929999999999993e1 0 0
|
||||
6.50914000000000073e1 -1.52929999999999993e1 0 0
|
||||
7.30456999999999965e1 -1.52929999999999993e1 0 0
|
||||
8.10000000000000000e1 -1.52929999999999993e1 0 0
|
||||
8.89543000000000035e1 -1.52929999999999993e1 0 0
|
||||
9.69086000000000070e1 -1.52929999999999993e1 0 0
|
||||
1.04862900000000010e2 -1.52929999999999993e1 0 0
|
||||
1.12817200000000000e2 -1.52929999999999993e1 0 0
|
||||
1.20771500000000003e2 -1.52929999999999993e1 0 0
|
||||
1.28725800000000021e2 -1.52929999999999993e1 0 0
|
||||
1.36680100000000010e2 -1.52929999999999993e1 0 0
|
||||
1.44634399999999999e2 -1.52929999999999993e1 0 0
|
||||
1.52588700000000017e2 -1.52929999999999993e1 0 0
|
||||
8.27280000000000015e0 -5.58000000000000007e0 0 0
|
||||
1.63536000000000001e1 -5.58000000000000007e0 0 0
|
||||
2.44344000000000001e1 -5.58000000000000007e0 0 0
|
||||
3.25152000000000001e1 -5.58000000000000007e0 0 0
|
||||
4.05959999999999965e1 -5.58000000000000007e0 0 0
|
||||
4.86768000000000001e1 -5.58000000000000007e0 0 0
|
||||
5.67576000000000036e1 -5.58000000000000007e0 0 0
|
||||
6.48383999999999929e1 -5.58000000000000007e0 0 0
|
||||
7.29191999999999894e1 -5.58000000000000007e0 0 0
|
||||
8.09999999999999858e1 -5.58000000000000007e0 0 0
|
||||
8.90807999999999964e1 -5.58000000000000007e0 0 0
|
||||
9.71615999999999929e1 -5.58000000000000007e0 0 0
|
||||
1.05242399999999989e2 -5.58000000000000007e0 0 0
|
||||
1.13323200000000000e2 -5.58000000000000007e0 0 0
|
||||
1.21403999999999996e2 -5.58000000000000007e0 0 0
|
||||
1.29484800000000007e2 -5.58000000000000007e0 0 0
|
||||
1.37565600000000018e2 -5.58000000000000007e0 0 0
|
||||
1.45646400000000000e2 -5.58000000000000007e0 0 0
|
||||
1.53727200000000011e2 -5.58000000000000007e0 0 0
|
||||
8.23409999999999975e0 -4.91999999999999993e0 0 0
|
||||
1.63192000000000021e1 -4.91999999999999993e0 0 0
|
||||
2.44043000000000028e1 -4.91999999999999993e0 0 0
|
||||
3.24894000000000034e1 -4.91999999999999993e0 0 0
|
||||
4.05745000000000005e1 -4.91999999999999993e0 0 0
|
||||
4.86596000000000046e1 -4.91999999999999993e0 0 0
|
||||
5.67447000000000088e1 -4.91999999999999993e0 0 0
|
||||
6.48298000000000059e1 -4.91999999999999993e0 0 0
|
||||
7.29149000000000029e1 -4.91999999999999993e0 0 0
|
||||
8.10000000000000000e1 -4.91999999999999993e0 0 0
|
||||
8.90851000000000113e1 -4.91999999999999993e0 0 0
|
||||
9.71702000000000083e1 -4.91999999999999993e0 0 0
|
||||
1.05255300000000005e2 -4.91999999999999993e0 0 0
|
||||
1.13340400000000017e2 -4.91999999999999993e0 0 0
|
||||
1.21425500000000014e2 -4.91999999999999993e0 0 0
|
||||
1.29510600000000011e2 -4.91999999999999993e0 0 0
|
||||
1.37595700000000022e2 -4.91999999999999993e0 0 0
|
||||
1.45680800000000005e2 -4.91999999999999993e0 0 0
|
||||
1.53765900000000016e2 -4.91999999999999993e0 0 0
|
||||
8.24039999999999928e0 5.03399999999999981e0 0 0
|
||||
1.63247999999999962e1 5.03399999999999981e0 0 0
|
||||
2.44091999999999949e1 5.03399999999999981e0 0 0
|
||||
3.24935999999999936e1 5.03399999999999981e0 0 0
|
||||
4.05779999999999959e1 5.03399999999999981e0 0 0
|
||||
4.86623999999999910e1 5.03399999999999981e0 0 0
|
||||
5.67467999999999861e1 5.03399999999999981e0 0 0
|
||||
6.48311999999999955e1 5.03399999999999981e0 0 0
|
||||
7.29155999999999977e1 5.03399999999999981e0 0 0
|
||||
8.10000000000000000e1 5.03399999999999981e0 0 0
|
||||
8.90843999999999880e1 5.03399999999999981e0 0 0
|
||||
9.71687999999999903e1 5.03399999999999981e0 0 0
|
||||
1.05253199999999993e2 5.03399999999999981e0 0 0
|
||||
1.13337599999999981e2 5.03399999999999981e0 0 0
|
||||
1.21421999999999983e2 5.03399999999999981e0 0 0
|
||||
1.29506399999999985e2 5.03399999999999981e0 0 0
|
||||
1.37590799999999973e2 5.03399999999999981e0 0 0
|
||||
1.45675199999999990e2 5.03399999999999981e0 0 0
|
||||
1.53759599999999978e2 5.03399999999999981e0 0 0
|
||||
8.26559999999999917e0 5.46600000000000019e0 0 0
|
||||
1.63472000000000008e1 5.46600000000000019e0 0 0
|
||||
2.44287999999999990e1 5.46600000000000019e0 0 0
|
||||
3.25103999999999971e1 5.46600000000000019e0 0 0
|
||||
4.05919999999999987e1 5.46600000000000019e0 0 0
|
||||
4.86735999999999933e1 5.46600000000000019e0 0 0
|
||||
5.67551999999999950e1 5.46600000000000019e0 0 0
|
||||
6.48367999999999967e1 5.46600000000000019e0 0 0
|
||||
7.29183999999999912e1 5.46600000000000019e0 0 0
|
||||
8.10000000000000000e1 5.46600000000000019e0 0 0
|
||||
8.90815999999999946e1 5.46600000000000019e0 0 0
|
||||
9.71631999999999891e1 5.46600000000000019e0 0 0
|
||||
1.05244799999999998e2 5.46600000000000019e0 0 0
|
||||
1.13326399999999992e2 5.46600000000000019e0 0 0
|
||||
1.21408000000000001e2 5.46600000000000019e0 0 0
|
||||
1.29489599999999996e2 5.46600000000000019e0 0 0
|
||||
1.37571200000000005e2 5.46600000000000019e0 0 0
|
||||
1.45652799999999985e2 5.46600000000000019e0 0 0
|
||||
1.53734399999999994e2 5.46600000000000019e0 0 0
|
||||
9.46170000000000044e0 1.55850000000000009e1 0 0
|
||||
1.74103999999999992e1 1.55850000000000009e1 0 0
|
||||
2.53590999999999980e1 1.55850000000000009e1 0 0
|
||||
3.33078000000000003e1 1.55850000000000009e1 0 0
|
||||
4.12564999999999955e1 1.55850000000000009e1 0 0
|
||||
4.92051999999999978e1 1.55850000000000009e1 0 0
|
||||
5.71538999999999930e1 1.55850000000000009e1 0 0
|
||||
6.51025999999999954e1 1.55850000000000009e1 0 0
|
||||
7.30512999999999977e1 1.55850000000000009e1 0 0
|
||||
8.10000000000000000e1 1.55850000000000009e1 0 0
|
||||
8.89487000000000023e1 1.55850000000000009e1 0 0
|
||||
9.68974000000000046e1 1.55850000000000009e1 0 0
|
||||
1.04846100000000007e2 1.55850000000000009e1 0 0
|
||||
1.12794799999999995e2 1.55850000000000009e1 0 0
|
||||
1.20743499999999997e2 1.55850000000000009e1 0 0
|
||||
1.28692199999999985e2 1.55850000000000009e1 0 0
|
||||
1.36640899999999988e2 1.55850000000000009e1 0 0
|
||||
1.44589599999999990e2 1.55850000000000009e1 0 0
|
||||
1.52538299999999992e2 1.55850000000000009e1 0 0
|
||||
9.52109999999999879e0 1.59149999999999991e1 0 0
|
||||
1.74631999999999969e1 1.59149999999999991e1 0 0
|
||||
2.54052999999999969e1 1.59149999999999991e1 0 0
|
||||
3.33473999999999933e1 1.59149999999999991e1 0 0
|
||||
4.12894999999999968e1 1.59149999999999991e1 0 0
|
||||
4.92315999999999931e1 1.59149999999999991e1 0 0
|
||||
5.71736999999999966e1 1.59149999999999991e1 0 0
|
||||
6.51157999999999930e1 1.59149999999999991e1 0 0
|
||||
7.30578999999999894e1 1.59149999999999991e1 0 0
|
||||
8.09999999999999858e1 1.59149999999999991e1 0 0
|
||||
8.89420999999999822e1 1.59149999999999991e1 0 0
|
||||
9.68841999999999786e1 1.59149999999999991e1 0 0
|
||||
1.04826299999999975e2 1.59149999999999991e1 0 0
|
||||
1.12768399999999986e2 1.59149999999999991e1 0 0
|
||||
1.20710499999999982e2 1.59149999999999991e1 0 0
|
||||
1.28652599999999978e2 1.59149999999999991e1 0 0
|
||||
1.36594699999999989e2 1.59149999999999991e1 0 0
|
||||
1.44536799999999999e2 1.59149999999999991e1 0 0
|
||||
1.52478899999999982e2 1.59149999999999991e1 0 0
|
||||
1.19952000000000005e1 2.61230000000000011e1 0 0
|
||||
1.96623999999999981e1 2.61230000000000011e1 0 0
|
||||
2.73295999999999957e1 2.61230000000000011e1 0 0
|
||||
3.49968000000000004e1 2.61230000000000011e1 0 0
|
||||
4.26640000000000015e1 2.61230000000000011e1 0 0
|
||||
5.03311999999999955e1 2.61230000000000011e1 0 0
|
||||
5.79983999999999966e1 2.61230000000000011e1 0 0
|
||||
6.56655999999999977e1 2.61230000000000011e1 0 0
|
||||
7.33327999999999918e1 2.61230000000000011e1 0 0
|
||||
8.10000000000000000e1 2.61230000000000011e1 0 0
|
||||
8.86671999999999940e1 2.61230000000000011e1 0 0
|
||||
9.63343999999999880e1 2.61230000000000011e1 0 0
|
||||
1.04001599999999996e2 2.61230000000000011e1 0 0
|
||||
1.11668799999999990e2 2.61230000000000011e1 0 0
|
||||
1.19335999999999999e2 2.61230000000000011e1 0 0
|
||||
1.27003199999999993e2 2.61230000000000011e1 0 0
|
||||
1.34670400000000001e2 2.61230000000000011e1 0 0
|
||||
1.42337599999999981e2 2.61230000000000011e1 0 0
|
||||
1.50004799999999989e2 2.61230000000000011e1 0 0
|
||||
1.20735000000000010e1 2.63769999999999989e1 0 0
|
||||
1.97320000000000029e1 2.63769999999999989e1 0 0
|
||||
2.73905000000000030e1 2.63769999999999989e1 0 0
|
||||
3.50490000000000066e1 2.63769999999999989e1 0 0
|
||||
4.27075000000000031e1 2.63769999999999989e1 0 0
|
||||
5.03660000000000068e1 2.63769999999999989e1 0 0
|
||||
5.80245000000000033e1 2.63769999999999989e1 0 0
|
||||
6.56830000000000069e1 2.63769999999999989e1 0 0
|
||||
7.33415000000000106e1 2.63769999999999989e1 0 0
|
||||
8.10000000000000142e1 2.63769999999999989e1 0 0
|
||||
8.86585000000000178e1 2.63769999999999989e1 0 0
|
||||
9.63170000000000215e1 2.63769999999999989e1 0 0
|
||||
1.03975500000000025e2 2.63769999999999989e1 0 0
|
||||
1.11634000000000015e2 2.63769999999999989e1 0 0
|
||||
1.19292500000000018e2 2.63769999999999989e1 0 0
|
||||
1.26951000000000022e2 2.63769999999999989e1 0 0
|
||||
1.34609499999999997e2 2.63769999999999989e1 0 0
|
||||
1.42268000000000001e2 2.63769999999999989e1 0 0
|
||||
1.49926500000000004e2 2.63769999999999989e1 0 0
|
||||
[NumSegments] = 244
|
||||
0 1 -1 1 0 0
|
||||
1 2 -1 1 0 0
|
||||
2 3 -1 1 0 0
|
||||
3 0 -1 1 0 0
|
||||
13 32 -1 0 0 0
|
||||
32 33 -1 0 0 0
|
||||
33 34 -1 0 0 0
|
||||
34 35 -1 0 0 0
|
||||
35 36 -1 0 0 0
|
||||
36 37 -1 0 0 0
|
||||
37 38 -1 0 0 0
|
||||
38 39 -1 0 0 0
|
||||
39 40 -1 0 0 0
|
||||
40 41 -1 0 0 0
|
||||
41 42 -1 0 0 0
|
||||
42 43 -1 0 0 0
|
||||
43 44 -1 0 0 0
|
||||
44 45 -1 0 0 0
|
||||
45 46 -1 0 0 0
|
||||
46 47 -1 0 0 0
|
||||
47 48 -1 0 0 0
|
||||
48 49 -1 0 0 0
|
||||
49 50 -1 0 0 0
|
||||
50 14 -1 0 0 0
|
||||
12 51 -1 0 0 0
|
||||
51 52 -1 0 0 0
|
||||
52 53 -1 0 0 0
|
||||
53 54 -1 0 0 0
|
||||
54 55 -1 0 0 0
|
||||
55 56 -1 0 0 0
|
||||
56 57 -1 0 0 0
|
||||
57 58 -1 0 0 0
|
||||
58 59 -1 0 0 0
|
||||
59 60 -1 0 0 0
|
||||
60 61 -1 0 0 0
|
||||
61 62 -1 0 0 0
|
||||
62 63 -1 0 0 0
|
||||
63 64 -1 0 0 0
|
||||
64 65 -1 0 0 0
|
||||
65 66 -1 0 0 0
|
||||
66 67 -1 0 0 0
|
||||
67 68 -1 0 0 0
|
||||
68 69 -1 0 0 0
|
||||
69 15 -1 0 0 0
|
||||
11 70 -1 0 0 0
|
||||
70 71 -1 0 0 0
|
||||
71 72 -1 0 0 0
|
||||
72 73 -1 0 0 0
|
||||
73 74 -1 0 0 0
|
||||
74 75 -1 0 0 0
|
||||
75 76 -1 0 0 0
|
||||
76 77 -1 0 0 0
|
||||
77 78 -1 0 0 0
|
||||
78 79 -1 0 0 0
|
||||
79 80 -1 0 0 0
|
||||
80 81 -1 0 0 0
|
||||
81 82 -1 0 0 0
|
||||
82 83 -1 0 0 0
|
||||
83 84 -1 0 0 0
|
||||
84 85 -1 0 0 0
|
||||
85 86 -1 0 0 0
|
||||
86 87 -1 0 0 0
|
||||
87 88 -1 0 0 0
|
||||
88 16 -1 0 0 0
|
||||
10 89 -1 0 0 0
|
||||
89 90 -1 0 0 0
|
||||
90 91 -1 0 0 0
|
||||
91 92 -1 0 0 0
|
||||
92 93 -1 0 0 0
|
||||
93 94 -1 0 0 0
|
||||
94 95 -1 0 0 0
|
||||
95 96 -1 0 0 0
|
||||
96 97 -1 0 0 0
|
||||
97 98 -1 0 0 0
|
||||
98 99 -1 0 0 0
|
||||
99 100 -1 0 0 0
|
||||
100 101 -1 0 0 0
|
||||
101 102 -1 0 0 0
|
||||
102 103 -1 0 0 0
|
||||
103 104 -1 0 0 0
|
||||
104 105 -1 0 0 0
|
||||
105 106 -1 0 0 0
|
||||
106 107 -1 0 0 0
|
||||
107 17 -1 0 0 0
|
||||
9 108 -1 0 0 0
|
||||
108 109 -1 0 0 0
|
||||
109 110 -1 0 0 0
|
||||
110 111 -1 0 0 0
|
||||
111 112 -1 0 0 0
|
||||
112 113 -1 0 0 0
|
||||
113 114 -1 0 0 0
|
||||
114 115 -1 0 0 0
|
||||
115 116 -1 0 0 0
|
||||
116 117 -1 0 0 0
|
||||
117 118 -1 0 0 0
|
||||
118 119 -1 0 0 0
|
||||
119 120 -1 0 0 0
|
||||
120 121 -1 0 0 0
|
||||
121 122 -1 0 0 0
|
||||
122 123 -1 0 0 0
|
||||
123 124 -1 0 0 0
|
||||
124 125 -1 0 0 0
|
||||
125 126 -1 0 0 0
|
||||
126 18 -1 0 0 0
|
||||
8 127 -1 0 0 0
|
||||
127 128 -1 0 0 0
|
||||
128 129 -1 0 0 0
|
||||
129 130 -1 0 0 0
|
||||
130 131 -1 0 0 0
|
||||
131 132 -1 0 0 0
|
||||
132 133 -1 0 0 0
|
||||
133 134 -1 0 0 0
|
||||
134 135 -1 0 0 0
|
||||
135 136 -1 0 0 0
|
||||
136 137 -1 0 0 0
|
||||
137 138 -1 0 0 0
|
||||
138 139 -1 0 0 0
|
||||
139 140 -1 0 0 0
|
||||
140 141 -1 0 0 0
|
||||
141 142 -1 0 0 0
|
||||
142 143 -1 0 0 0
|
||||
143 144 -1 0 0 0
|
||||
144 145 -1 0 0 0
|
||||
145 19 -1 0 0 0
|
||||
31 146 -1 0 0 0
|
||||
146 147 -1 0 0 0
|
||||
147 148 -1 0 0 0
|
||||
148 149 -1 0 0 0
|
||||
149 150 -1 0 0 0
|
||||
150 151 -1 0 0 0
|
||||
151 152 -1 0 0 0
|
||||
152 153 -1 0 0 0
|
||||
153 154 -1 0 0 0
|
||||
154 155 -1 0 0 0
|
||||
155 156 -1 0 0 0
|
||||
156 157 -1 0 0 0
|
||||
157 158 -1 0 0 0
|
||||
158 159 -1 0 0 0
|
||||
159 160 -1 0 0 0
|
||||
160 161 -1 0 0 0
|
||||
161 162 -1 0 0 0
|
||||
162 163 -1 0 0 0
|
||||
163 164 -1 0 0 0
|
||||
164 20 -1 0 0 0
|
||||
30 165 -1 0 0 0
|
||||
165 166 -1 0 0 0
|
||||
166 167 -1 0 0 0
|
||||
167 168 -1 0 0 0
|
||||
168 169 -1 0 0 0
|
||||
169 170 -1 0 0 0
|
||||
170 171 -1 0 0 0
|
||||
171 172 -1 0 0 0
|
||||
172 173 -1 0 0 0
|
||||
173 174 -1 0 0 0
|
||||
174 175 -1 0 0 0
|
||||
175 176 -1 0 0 0
|
||||
176 177 -1 0 0 0
|
||||
177 178 -1 0 0 0
|
||||
178 179 -1 0 0 0
|
||||
179 180 -1 0 0 0
|
||||
180 181 -1 0 0 0
|
||||
181 182 -1 0 0 0
|
||||
182 183 -1 0 0 0
|
||||
183 21 -1 0 0 0
|
||||
29 184 -1 0 0 0
|
||||
184 185 -1 0 0 0
|
||||
185 186 -1 0 0 0
|
||||
186 187 -1 0 0 0
|
||||
187 188 -1 0 0 0
|
||||
188 189 -1 0 0 0
|
||||
189 190 -1 0 0 0
|
||||
190 191 -1 0 0 0
|
||||
191 192 -1 0 0 0
|
||||
192 193 -1 0 0 0
|
||||
193 194 -1 0 0 0
|
||||
194 195 -1 0 0 0
|
||||
195 196 -1 0 0 0
|
||||
196 197 -1 0 0 0
|
||||
197 198 -1 0 0 0
|
||||
198 199 -1 0 0 0
|
||||
199 200 -1 0 0 0
|
||||
200 201 -1 0 0 0
|
||||
201 202 -1 0 0 0
|
||||
202 22 -1 0 0 0
|
||||
28 203 -1 0 0 0
|
||||
203 204 -1 0 0 0
|
||||
204 205 -1 0 0 0
|
||||
205 206 -1 0 0 0
|
||||
206 207 -1 0 0 0
|
||||
207 208 -1 0 0 0
|
||||
208 209 -1 0 0 0
|
||||
209 210 -1 0 0 0
|
||||
210 211 -1 0 0 0
|
||||
211 212 -1 0 0 0
|
||||
212 213 -1 0 0 0
|
||||
213 214 -1 0 0 0
|
||||
214 215 -1 0 0 0
|
||||
215 216 -1 0 0 0
|
||||
216 217 -1 0 0 0
|
||||
217 218 -1 0 0 0
|
||||
218 219 -1 0 0 0
|
||||
219 220 -1 0 0 0
|
||||
220 221 -1 0 0 0
|
||||
221 23 -1 0 0 0
|
||||
27 222 -1 0 0 0
|
||||
222 223 -1 0 0 0
|
||||
223 224 -1 0 0 0
|
||||
224 225 -1 0 0 0
|
||||
225 226 -1 0 0 0
|
||||
226 227 -1 0 0 0
|
||||
227 228 -1 0 0 0
|
||||
228 229 -1 0 0 0
|
||||
229 230 -1 0 0 0
|
||||
230 231 -1 0 0 0
|
||||
231 232 -1 0 0 0
|
||||
232 233 -1 0 0 0
|
||||
233 234 -1 0 0 0
|
||||
234 235 -1 0 0 0
|
||||
235 236 -1 0 0 0
|
||||
236 237 -1 0 0 0
|
||||
237 238 -1 0 0 0
|
||||
238 239 -1 0 0 0
|
||||
239 240 -1 0 0 0
|
||||
240 24 -1 0 0 0
|
||||
26 241 -1 0 0 0
|
||||
241 242 -1 0 0 0
|
||||
242 243 -1 0 0 0
|
||||
243 244 -1 0 0 0
|
||||
244 245 -1 0 0 0
|
||||
245 246 -1 0 0 0
|
||||
246 247 -1 0 0 0
|
||||
247 248 -1 0 0 0
|
||||
248 249 -1 0 0 0
|
||||
249 250 -1 0 0 0
|
||||
250 251 -1 0 0 0
|
||||
251 252 -1 0 0 0
|
||||
252 253 -1 0 0 0
|
||||
253 254 -1 0 0 0
|
||||
254 255 -1 0 0 0
|
||||
255 256 -1 0 0 0
|
||||
256 257 -1 0 0 0
|
||||
257 258 -1 0 0 0
|
||||
258 259 -1 0 0 0
|
||||
259 25 -1 0 0 0
|
||||
[NumArcSegments] = 28
|
||||
4 5 9.00000000000000000e1 1.00000000000000000e1 0 0 0 1.00000000000000000e1
|
||||
5 6 9.00000000000000000e1 1.00000000000000000e1 0 0 0 1.00000000000000000e1
|
||||
6 7 9.00000000000000000e1 1.00000000000000000e1 0 0 0 1.00000000000000000e1
|
||||
7 4 9.00000000000000000e1 1.00000000000000000e1 0 0 0 1.00000000000000000e1
|
||||
8 9 4.69999999999999973e-1 1.00000000000000000e1 0 0 0 1.00000000000000000e1
|
||||
9 10 6.94000000000000039e0 1.00000000000000000e1 0 0 0 1.00000000000000000e1
|
||||
10 11 6.60000000000000031e-1 1.00000000000000000e1 0 0 0 1.00000000000000000e1
|
||||
11 12 6.94000000000000039e0 1.00000000000000000e1 0 0 0 1.00000000000000000e1
|
||||
12 13 8.29999999999999960e-1 1.00000000000000000e1 0 0 0 1.00000000000000000e1
|
||||
13 14 1.41360000000000014e2 1.00000000000000000e1 0 0 0 1.00000000000000000e1
|
||||
14 15 8.29999999999999960e-1 1.00000000000000000e1 0 0 0 1.00000000000000000e1
|
||||
15 16 6.94000000000000039e0 1.00000000000000000e1 0 0 0 1.00000000000000000e1
|
||||
16 17 6.60000000000000031e-1 1.00000000000000000e1 0 0 0 1.00000000000000000e1
|
||||
17 18 6.94000000000000039e0 1.00000000000000000e1 0 0 0 1.00000000000000000e1
|
||||
18 19 4.69999999999999973e-1 1.00000000000000000e1 0 0 0 1.00000000000000000e1
|
||||
19 20 7.04000000000000004e0 1.00000000000000000e1 0 0 0 1.00000000000000000e1
|
||||
20 21 3.09999999999999998e-1 1.00000000000000000e1 0 0 0 1.00000000000000000e1
|
||||
21 22 7.23000000000000043e0 1.00000000000000000e1 0 0 0 1.00000000000000000e1
|
||||
22 23 2.30000000000000010e-1 1.00000000000000000e1 0 0 0 1.00000000000000000e1
|
||||
23 24 7.48000000000000043e0 1.00000000000000000e1 0 0 0 1.00000000000000000e1
|
||||
24 25 1.90000000000000002e-1 1.00000000000000000e1 0 0 0 1.00000000000000000e1
|
||||
25 26 1.42000000000000000e2 1.00000000000000000e1 0 0 0 1.00000000000000000e1
|
||||
26 27 1.90000000000000002e-1 1.00000000000000000e1 0 0 0 1.00000000000000000e1
|
||||
27 28 7.48000000000000043e0 1.00000000000000000e1 0 0 0 1.00000000000000000e1
|
||||
28 29 2.30000000000000010e-1 1.00000000000000000e1 0 0 0 1.00000000000000000e1
|
||||
29 30 7.23000000000000043e0 1.00000000000000000e1 0 0 0 1.00000000000000000e1
|
||||
30 31 3.09999999999999998e-1 1.00000000000000000e1 0 0 0 1.00000000000000000e1
|
||||
31 8 7.04000000000000004e0 1.00000000000000000e1 0 0 0 1.00000000000000000e1
|
||||
[NumHoles] = 0
|
||||
[NumBlockLabels] = 9
|
||||
-1.00000000000000000e2 0.00000000000000000e0 1 -1 0 0.00000000000000000e0 0 1 2
|
||||
8.10000000000000000e1 0.00000000000000000e0 1 -1 0 0.00000000000000000e0 0 1 0
|
||||
8.10000000000000000e1 9.60000000000000000e1 2 5.00000000000000000e0 0 0.00000000000000000e0 0 1 0
|
||||
8.10000000000000000e1 -2.62500000000000000e1 3 1.00000000000000000e0 0 0.00000000000000000e0 0 1 0
|
||||
8.10000000000000000e1 -1.57500000000000000e1 3 1.00000000000000000e0 0 0.00000000000000000e0 0 1 0
|
||||
8.10000000000000000e1 -5.25000000000000000e0 3 1.00000000000000000e0 0 0.00000000000000000e0 0 1 0
|
||||
8.10000000000000000e1 5.25000000000000000e0 3 1.00000000000000000e0 0 0.00000000000000000e0 0 1 0
|
||||
8.10000000000000000e1 1.57500000000000000e1 3 1.00000000000000000e0 0 0.00000000000000000e0 0 1 0
|
||||
8.10000000000000000e1 2.62500000000000000e1 3 1.00000000000000000e0 0 0.00000000000000000e0 0 1 0
|
||||
[/base-doc]
|
||||
# femm42 simulation metadata
|
||||
# track node indices reference the doc loaded above; editing the doc may invalidate them.
|
||||
|
||||
[source]
|
||||
fem = /Volumes/External/Repositories/femm42src/examples/guitar_strings.fem
|
||||
|
||||
[sim]
|
||||
dt = 82.410us
|
||||
interval = 16.667ms
|
||||
subdivisions = 20
|
||||
buffer_size = 148
|
||||
match_cycle = true
|
||||
loop_playback = true
|
||||
fundamental_hz = 82.41
|
||||
|
||||
[track]
|
||||
label = track 1
|
||||
anchor_a = 17
|
||||
anchor_b = 30
|
||||
axis = +y
|
||||
expression = series(s, t, 82.41, 5)
|
||||
members = 18 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
|
||||
|
||||
[track]
|
||||
label = track 2
|
||||
anchor_a = 15
|
||||
anchor_b = 28
|
||||
axis = +y
|
||||
expression = series(s, t, 103.83, 5)
|
||||
members = 16 29 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
|
||||
|
||||
[track]
|
||||
label = track 3
|
||||
anchor_a = 13
|
||||
anchor_b = 26
|
||||
axis = +y
|
||||
expression = series(s, t, 123.47, 5)
|
||||
members = 14 27 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
|
||||
|
||||
[track]
|
||||
label = track 4
|
||||
anchor_a = 11
|
||||
anchor_b = 24
|
||||
axis = +y
|
||||
expression = series(s, t, 164.81, 5)
|
||||
members = 12 25 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
|
||||
|
||||
[track]
|
||||
label = track 5
|
||||
anchor_a = 9
|
||||
anchor_b = 22
|
||||
axis = +y
|
||||
expression = series(s, t, 246.94, 5)
|
||||
members = 10 23 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
|
||||
|
||||
[track]
|
||||
label = track 6
|
||||
anchor_a = 7
|
||||
anchor_b = 20
|
||||
axis = +y
|
||||
expression = series(s, t, 329.63, 5)
|
||||
members = 8 21 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
|
||||
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
[LengthUnits] = millimeters
|
||||
[ProblemType] = planar
|
||||
[Coordinates] = cartesian
|
||||
[Comment] = "single low-E steel string (1.168 mm gauge, 25.5-inch scale). NdFeB bar magnets touch each end of the string with N faces facing inward. Flux is injected from both ends, the two flows cancel at the midpoint, producing a null point in the centre of the rod."
|
||||
[Comment] = "single low-E steel string (1.168 mm gauge, 12.75-inch half-scale). NdFeB bar magnets touch each end of the string with the same polarity along +x: the west magnet's N face contacts the west string end, the east magnet's S face contacts the east string end. Flux runs the full length of the rod in one direction and returns through air, producing a strong axial field everywhere along the wire."
|
||||
[PointProps] = 0
|
||||
[BdryProps] = 1
|
||||
<BeginBdry>
|
||||
|
|
@ -83,22 +83,22 @@
|
|||
<EndBlock>
|
||||
[CircuitProps] = 0
|
||||
[NumPoints] = 16
|
||||
-100 -30 0 0
|
||||
750 -30 0 0
|
||||
750 30 0 0
|
||||
-100 30 0 0
|
||||
-100 -120 0 0
|
||||
400 -120 0 0
|
||||
400 120 0 0
|
||||
-100 120 0 0
|
||||
-30 -5 0 0
|
||||
-30 5 0 0
|
||||
0 -5 0 0
|
||||
0 -0.584 0 0
|
||||
0 0.584 0 0
|
||||
0 5 0 0
|
||||
647.7 -5 0 0
|
||||
647.7 -0.584 0 0
|
||||
647.7 0.584 0 0
|
||||
647.7 5 0 0
|
||||
677.7 5 0 0
|
||||
677.7 -5 0 0
|
||||
323.85 -5 0 0
|
||||
323.85 -0.584 0 0
|
||||
323.85 0.584 0 0
|
||||
323.85 5 0 0
|
||||
353.85 5 0 0
|
||||
353.85 -5 0 0
|
||||
[NumSegments] = 18
|
||||
0 1 -1 1 0 0
|
||||
1 2 -1 1 0 0
|
||||
|
|
@ -121,7 +121,7 @@
|
|||
[NumArcSegments] = 0
|
||||
[NumHoles] = 0
|
||||
[NumBlockLabels] = 4
|
||||
325 20 1 -1 0 0 0 1 2
|
||||
162 90 1 -1 0 0 0 1 2
|
||||
-15 0 2 5 0 0 0 1 0
|
||||
662.7 0 2 5 0 180 0 1 0
|
||||
325 0 3 1 0 0 0 1 0
|
||||
338.85 0 2 5 0 0 0 1 0
|
||||
162 0 3 1 0 0 0 1 0
|
||||
|
|
|
|||
|
|
@ -0,0 +1,117 @@
|
|||
[Format] = 4.0
|
||||
[Frequency] = 0
|
||||
[Precision] = 1e-008
|
||||
[MinAngle] = 25
|
||||
[Depth] = 1
|
||||
[LengthUnits] = millimeters
|
||||
[ProblemType] = axisymmetric
|
||||
[Coordinates] = cartesian
|
||||
[Comment] = "axisymmetric meridional cross-section: a steel wire on the z-axis passes through the hole of an axially-magnetised NdFeB ring (M along +z). top end of the wire emerges past the ring's N face, bottom end past the S face. revolved around r = 0 the ring stays a continuous body."
|
||||
[PointProps] = 0
|
||||
[BdryProps] = 1
|
||||
<BeginBdry>
|
||||
<BdryName> = "A=0"
|
||||
<BdryType> = 0
|
||||
<A_0> = 0
|
||||
<A_1> = 0
|
||||
<A_2> = 0
|
||||
<Phi> = 0
|
||||
<c0> = 0
|
||||
<c0i> = 0
|
||||
<c1> = 0
|
||||
<c1i> = 0
|
||||
<Mu_ssd> = 0
|
||||
<Sigma_ssd> = 0
|
||||
<EndBdry>
|
||||
[BlockProps] = 3
|
||||
<BeginBlock>
|
||||
<BlockName> = "Air"
|
||||
<Mu_x> = 1
|
||||
<Mu_y> = 1
|
||||
<H_c> = 0
|
||||
<H_cAngle> = 0
|
||||
<J_re> = 0
|
||||
<J_im> = 0
|
||||
<Sigma> = 0
|
||||
<d_lam> = 0
|
||||
<Phi_h> = 0
|
||||
<Phi_hx> = 0
|
||||
<Phi_hy> = 0
|
||||
<LamType> = 0
|
||||
<LamFill> = 1
|
||||
<NStrands> = 0
|
||||
<WireD> = 0
|
||||
<BHPoints> = 0
|
||||
<EndBlock>
|
||||
<BeginBlock>
|
||||
<BlockName> = "NdFeB"
|
||||
<Mu_x> = 1.05
|
||||
<Mu_y> = 1.05
|
||||
<H_c> = 915000
|
||||
<H_cAngle> = 0
|
||||
<J_re> = 0
|
||||
<J_im> = 0
|
||||
<Sigma> = 0.667
|
||||
<d_lam> = 0
|
||||
<Phi_h> = 0
|
||||
<Phi_hx> = 0
|
||||
<Phi_hy> = 0
|
||||
<LamType> = 0
|
||||
<LamFill> = 1
|
||||
<NStrands> = 0
|
||||
<WireD> = 0
|
||||
<BHPoints> = 0
|
||||
<EndBlock>
|
||||
<BeginBlock>
|
||||
<BlockName> = "Steel"
|
||||
<Mu_x> = 2500
|
||||
<Mu_y> = 2500
|
||||
<H_c> = 0
|
||||
<H_cAngle> = 0
|
||||
<J_re> = 0
|
||||
<J_im> = 0
|
||||
<Sigma> = 5.8
|
||||
<d_lam> = 0
|
||||
<Phi_h> = 0
|
||||
<Phi_hx> = 0
|
||||
<Phi_hy> = 0
|
||||
<LamType> = 0
|
||||
<LamFill> = 1
|
||||
<NStrands> = 0
|
||||
<WireD> = 0
|
||||
<BHPoints> = 0
|
||||
<EndBlock>
|
||||
[CircuitProps] = 0
|
||||
[NumPoints] = 12
|
||||
0 -100 0 0
|
||||
50 -100 0 0
|
||||
50 100 0 0
|
||||
0 100 0 0
|
||||
0 -80 0 0
|
||||
0.584 -80 0 0
|
||||
0.584 80 0 0
|
||||
0 80 0 0
|
||||
3 -10 0 0
|
||||
8 -10 0 0
|
||||
8 10 0 0
|
||||
3 10 0 0
|
||||
[NumSegments] = 13
|
||||
0 1 -1 1 0 0
|
||||
1 2 -1 1 0 0
|
||||
2 3 -1 1 0 0
|
||||
0 4 -1 0 0 0
|
||||
4 7 -1 0 0 0
|
||||
7 3 -1 0 0 0
|
||||
4 5 -1 0 0 0
|
||||
5 6 -1 0 0 0
|
||||
6 7 -1 0 0 0
|
||||
8 9 -1 0 0 0
|
||||
9 10 -1 0 0 0
|
||||
10 11 -1 0 0 0
|
||||
11 8 -1 0 0 0
|
||||
[NumArcSegments] = 0
|
||||
[NumHoles] = 0
|
||||
[NumBlockLabels] = 3
|
||||
25 50 1 -1 0 0 0 1 2
|
||||
5.5 0 2 1 0 90 0 1 0
|
||||
0.1 0 3 0.2 0 0 0 1 0
|
||||
|
|
@ -10,6 +10,7 @@ case "$(uname -s)" in
|
|||
esac
|
||||
|
||||
export MACOSX_DEPLOYMENT_TARGET=11.0
|
||||
export PROFILE=release
|
||||
|
||||
BUILD="$ROOT/build"
|
||||
APP="$BUILD/bin/femm.app"
|
||||
|
|
|
|||
|
|
@ -9,7 +9,11 @@ BUILD=${BUILD:-"$ROOT/build/ffi"}
|
|||
CXX=${CXX:-clang++}
|
||||
LD=${LD:-ld}
|
||||
AR=${AR:-ar}
|
||||
CXXFLAGS=${CXXFLAGS:-"-std=c++17 -fno-exceptions -fno-rtti -O2 -w"}
|
||||
case "${PROFILE:-release}" in
|
||||
release) DEFAULT_CXXFLAGS="-std=c++17 -fno-exceptions -fno-rtti -O3 -DNDEBUG -w" ;;
|
||||
*) DEFAULT_CXXFLAGS="-std=c++17 -fno-exceptions -fno-rtti -O0 -g -w" ;;
|
||||
esac
|
||||
CXXFLAGS=${CXXFLAGS:-$DEFAULT_CXXFLAGS}
|
||||
|
||||
mkdir -p "$BUILD"/{fkn,liblua,belasolv,csolv,hsolv,ffi}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,11 @@ BUILD="${BUILD:-$ROOT/build/triangle}"
|
|||
mkdir -p "$BUILD"
|
||||
|
||||
CC=${CC:-clang}
|
||||
CFLAGS=${CFLAGS:-"-O2 -w"}
|
||||
case "${PROFILE:-release}" in
|
||||
release) DEFAULT_CFLAGS="-O3 -DNDEBUG -w" ;;
|
||||
*) DEFAULT_CFLAGS="-O0 -g -w" ;;
|
||||
esac
|
||||
CFLAGS=${CFLAGS:-$DEFAULT_CFLAGS}
|
||||
|
||||
$CC $CFLAGS -o "$BUILD/triangle" "$ROOT/triangle/triangle.c" -lm
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue