diff --git a/src/constants.rs b/src/constants.rs index aec0ef2..c1c4901 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -5,7 +5,7 @@ const IN2MM: f32 = 25.4; pub const CHORD: f32 = 7.0 * IN2MM; pub const WING_TAPER: f32 = 0.6; pub const WINGSPAN: f32 = 45.0 * IN2MM; -pub const LENGTH: f32 = 30.0*IN2MM; +pub const LENGTH: f32 = 30.0 * IN2MM; /// strut count in the main wing pub const STRUTS: usize = 12; pub const CARDBOARD_WIDTH: f32 = 3.5; @@ -14,13 +14,13 @@ pub const SPAR_SIDE_WIDTH: f32 = 0.75 * IN2MM; pub const FUSELAGE_GAP: f32 = 0.4 * IN2MM; /// a very large number, used to place unknown objects such that they do not intersect pub const INF: f32 = 1000.0; -pub const RUDDER_HEIGHT: f32 = 5.0 *IN2MM; -pub const RUDDER_CHORD: f32 = 3.0 *IN2MM; +pub const RUDDER_HEIGHT: f32 = 5.0 * IN2MM; +pub const RUDDER_CHORD: f32 = 3.0 * IN2MM; pub const RUDDER_TAPER: f32 = 0.7; pub const RUDDER_STRUTS: usize = 3; -pub const ELEVATOR_HEIGHT: f32 = 8.0 *IN2MM; -pub const ELEVATOR_CHORD: f32 = 5.0 *IN2MM; +pub const ELEVATOR_HEIGHT: f32 = 8.0 * IN2MM; +pub const ELEVATOR_CHORD: f32 = 5.0 * IN2MM; pub const ELEVATOR_TAPER: f32 = 0.7; pub const ELEVATOR_STRUTS: usize = 4; -pub const STOCK_HEIGHT: f32 = 25.0 *IN2MM; -pub const STOCK_WIDTH: f32 = 37.0 *IN2MM; +pub const STOCK_HEIGHT: f32 = 25.0 * IN2MM; +pub const STOCK_WIDTH: f32 = 37.0 * IN2MM; diff --git a/src/main.rs b/src/main.rs index b9c01a2..612e855 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,12 @@ use std::ops::Deref; use std::sync::Mutex; +use constants::*; use nalgebra::Vector3; use scad::*; -use constants::*; -use selig::Span; -use selig::SeligFile; use selig::Point; +use selig::SeligFile; +use selig::Span; use std::ops::Range; mod constants; mod selig; @@ -21,26 +21,49 @@ fn main() { // cambered airfoil, used in the wing let wing_airfoil: SeligFile = SeligFile::parse(include_str!("../ag24.dat")); - println!("name {}, perim {}", wing_airfoil.get_name(), wing_airfoil.get_points().length()); - println!("desc {}", wing_airfoil.get_description().clone().unwrap_or("no desc".to_string())); + println!( + "name {}, perim {}", + wing_airfoil.get_name(), + wing_airfoil.get_points().length() + ); + println!( + "desc {}", + wing_airfoil + .get_description() + .clone() + .unwrap_or("no desc".to_string()) + ); //println!("span {:?}", points_in_range(wing_airfoil.get_points()[0..80], 0.0..1.0)); let points = wing_airfoil.get_points()[0..80].to_vec(); let perimeter = points.length(); - let span = points.to_vec().points_in_range(perimeter * 0.4.. perimeter); + let span = points.to_vec().points_in_range(perimeter * 0.4..perimeter); let points = scad::PolygonParameters::new(span); register_part(scad!(Polygon(points))); // symetric airfoil, used in the control surfaces let control_airfoil: SeligFile = SeligFile::parse(include_str!("../edgevertical.dat")); - let mut wing_transform = scad!(Translate(vec3(0.0,0.0,0.0))); + let mut wing_transform = scad!(Translate(vec3(0.0, 0.0, 0.0))); // struts let mut symetric_spar = scad!(Union); - for port in [true,false] { - let mut wing = wing(&wing_airfoil, STRUTS/2, WINGSPAN/2.0, CHORD, CHORD * WING_TAPER, SparType::Top); - let top_spar = topwing_spar(&wing_airfoil, STRUTS/2, WINGSPAN/2.0, CHORD, CHORD * WING_TAPER); + for port in [true, false] { + let mut wing = wing( + &wing_airfoil, + STRUTS / 2, + WINGSPAN / 2.0, + CHORD, + CHORD * WING_TAPER, + SparType::Top, + ); + let top_spar = topwing_spar( + &wing_airfoil, + STRUTS / 2, + WINGSPAN / 2.0, + CHORD, + CHORD * WING_TAPER, + ); let mut top_spar_neg = scad!(Difference; top_spar); top_spar_neg.add_child(wing.clone()); @@ -57,32 +80,52 @@ fn main() { } // fuselage affixment point - symetric_spar.add_child(centered_cube(vec3(CHORD, FUSELAGE_GAP*2.0, 1.0), (false,true,false))); + symetric_spar.add_child(centered_cube( + vec3(CHORD, FUSELAGE_GAP * 2.0, 1.0), + (false, true, false), + )); wing_transform.add_child(symetric_spar.clone()); register_part(scad!(Projection(false); symetric_spar)); - - // spars // "fuselage" scad_file.add_object(scad!(Rotate(-90.0, vec3(0.0, 0.0, 1.0)); - spar(LENGTH, false)) - ); + spar(LENGTH, false))); // rudder let mut rudder = scad!(Rotate(90.0, vec3(1.0, 0.0, 0.0))); - rudder.add_child(wing(&control_airfoil, RUDDER_STRUTS, RUDDER_HEIGHT, RUDDER_CHORD, RUDDER_CHORD * RUDDER_TAPER, SparType::Center)); + rudder.add_child(wing( + &control_airfoil, + RUDDER_STRUTS, + RUDDER_HEIGHT, + RUDDER_CHORD, + RUDDER_CHORD * RUDDER_TAPER, + SparType::Center, + )); rudder.add_child(spar(RUDDER_HEIGHT, false)); rudder = scad!(Translate(vec3(LENGTH-RUDDER_CHORD, 0.0, 0.0)); rudder); scad_file.add_object(rudder); // elevator - let mut elevator = scad!(Translate(vec3(LENGTH-ELEVATOR_CHORD, 0.0,0.0))); + let mut elevator = scad!(Translate(vec3(LENGTH - ELEVATOR_CHORD, 0.0, 0.0))); let mut symetric_spar = scad!(Union); - for port in [true,false] { - let mut wing = wing(&wing_airfoil, ELEVATOR_STRUTS, ELEVATOR_HEIGHT, ELEVATOR_CHORD, ELEVATOR_CHORD * ELEVATOR_TAPER, SparType::Top); - let top_spar = topwing_spar(&wing_airfoil, ELEVATOR_STRUTS, ELEVATOR_HEIGHT, ELEVATOR_CHORD, ELEVATOR_CHORD * ELEVATOR_TAPER); + for port in [true, false] { + let mut wing = wing( + &wing_airfoil, + ELEVATOR_STRUTS, + ELEVATOR_HEIGHT, + ELEVATOR_CHORD, + ELEVATOR_CHORD * ELEVATOR_TAPER, + SparType::Top, + ); + let top_spar = topwing_spar( + &wing_airfoil, + ELEVATOR_STRUTS, + ELEVATOR_HEIGHT, + ELEVATOR_CHORD, + ELEVATOR_CHORD * ELEVATOR_TAPER, + ); let mut top_spar_neg = scad!(Difference; top_spar); top_spar_neg.add_child(wing.clone()); symetric_spar.add_child(top_spar_neg.clone()); @@ -94,17 +137,19 @@ fn main() { } scad_file.add_object(elevator); - symetric_spar.add_child(centered_cube(vec3(CHORD, FUSELAGE_GAP*2.0, 1.0), (false,true,false))); + symetric_spar.add_child(centered_cube( + vec3(CHORD, FUSELAGE_GAP * 2.0, 1.0), + (false, true, false), + )); register_part(scad!(Projection(false); symetric_spar)); - let cardboard = vec3(0.38, 0.26, 0.26); scad_file.add_object(scad!(Color(cardboard); wing_transform)); scad_file.write_to_file(String::from("build/assembly.scad")); for (idx, part) in PARTS.lock().unwrap().clone().into_iter().enumerate() { - let mut file = ScadFile::new(); + let mut file = ScadFile::new(); file.set_detail(50); file.add_object(part); file.write_to_file(format!("build/part{idx}.scad")); @@ -134,7 +179,7 @@ fn strut(airfoil: &SeligFile, chord: f32, width: f32, spar: &SparType) -> ScadOb SparType::Top => topspar_negative(airfoil, chord, 0.1..0.6), SparType::Center => { scad!(Union) - }, + } SparType::None => { scad!(Union) } @@ -144,7 +189,13 @@ fn strut(airfoil: &SeligFile, chord: f32, width: f32, spar: &SparType) -> ScadOb extrude_strut(shape, strut_hole, width, chord, true) } -fn extrude_strut(shape: ScadObject, strut_hole: ScadObject, width: f32, chord: f32, register:bool) -> ScadObject { +fn extrude_strut( + shape: ScadObject, + strut_hole: ScadObject, + width: f32, + chord: f32, + register: bool, +) -> ScadObject { let mut strut_shape = scad!(Difference); strut_shape.add_child(shape); strut_shape.add_child(strut_hole); @@ -169,16 +220,18 @@ fn extrude_strut(shape: ScadObject, strut_hole: ScadObject, width: f32, chord: f fn topspar_negative(airfoil: &SeligFile, chord: f32, range: Range) -> ScadObject { let points = airfoil.get_points(); - let points = &points[0.. points.len() / 2]; + let points = &points[0..points.len() / 2]; let perimeter = span_length(points); - let span = points.to_vec().points_in_range(perimeter * (1.0-range.end) .. perimeter * (1.0-range.start)); + let span = points + .to_vec() + .points_in_range(perimeter * (1.0 - range.end)..perimeter * (1.0 - range.start)); let mut mask = scad!(Union); let mut last: Option = None; for point in span { - let mut current = scad!(Square(vec2(0.001/ chord, CARDBOARD_WIDTH / chord))); + let mut current = scad!(Square(vec2(0.001 / chord, CARDBOARD_WIDTH / chord))); current = scad!(Translate2d(point.clone() - vec2(0.0, CARDBOARD_WIDTH/chord)); current); if let Some(last_mask) = last { let mut hull = scad!(Hull); @@ -202,7 +255,7 @@ fn spar(length: f32, center: bool) -> ScadObject { if center { panel = scad!(Translate(vec3(0.0, -length/2.0, 0.0)); panel) } - let rot = 120.0 * i as f32; + let rot = 120.0 * i as f32; spar.add_child(scad!( Rotate(rot, vec3(0.0, 1.0, 0.0)); panel )); @@ -215,23 +268,29 @@ fn register_part(part: ScadObject) { PARTS.lock().unwrap().push(part); } -fn lerp(a: f32, b:f32, x:f32) -> f32 { - a*(1.0-x) + b*x +fn lerp(a: f32, b: f32, x: f32) -> f32 { + a * (1.0 - x) + b * x } -fn wing(aerofoil: &SeligFile, struts: usize, length: f32, chord: f32, taper: f32, spar: SparType) -> ScadObject { - let mut wing = scad!(Translate(vec3(0.0,0.0,0.0))); +fn wing( + aerofoil: &SeligFile, + struts: usize, + length: f32, + chord: f32, + taper: f32, + spar: SparType, +) -> ScadObject { + let mut wing = scad!(Translate(vec3(0.0, 0.0, 0.0))); // struts for strut_idx in 0..struts + 1 { - - let gap = length/struts as f32; + let gap = length / struts as f32; let chord = lerp(chord, taper, strut_idx as f32 / struts as f32); let spacing = strut_idx as f32 * gap; - let mut transform = scad!(Translate(vec3(0.0, spacing ,0.0))); + let mut transform = scad!(Translate(vec3(0.0, spacing, 0.0))); let strut = strut(&aerofoil, chord, CARDBOARD_WIDTH, &spar); transform.add_child(strut); @@ -241,16 +300,21 @@ fn wing(aerofoil: &SeligFile, struts: usize, length: f32, chord: f32, taper: f32 wing } -fn topwing_spar(aerofoil: &SeligFile, struts: usize, length: f32, chord: f32, taper: f32) -> ScadObject { +fn topwing_spar( + aerofoil: &SeligFile, + struts: usize, + length: f32, + chord: f32, + taper: f32, +) -> ScadObject { let mut wing = scad!(Hull); - let mut last_segment : Option = None; + let mut last_segment: Option = None; let mut pre_vis = scad!(Union); // struts for strut_idx in 0..struts + 1 { - - let gap = length/struts as f32; + let gap = length / struts as f32; let chord = lerp(chord, taper, strut_idx as f32 / struts as f32); @@ -259,10 +323,9 @@ fn topwing_spar(aerofoil: &SeligFile, struts: usize, length: f32, chord: f32, ta let shape = topspar_negative(&aerofoil, chord, 0.1..0.6); let extruded = extrude_strut(shape.clone(), scad!(Union), CARDBOARD_WIDTH, chord, false); - let mut transform = scad!(Translate(vec3(0.0, spacing ,0.0))); + let mut transform = scad!(Translate(vec3(0.0, spacing, 0.0))); transform.add_child(extruded); - // in betweens if let Some(last) = last_segment { let mut hull = scad!(Hull); @@ -272,7 +335,7 @@ fn topwing_spar(aerofoil: &SeligFile, struts: usize, length: f32, chord: f32, ta ..Default::default() }; - let mut last_t = scad!(Translate(vec3(0.0, -gap ,0.0))); + let mut last_t = scad!(Translate(vec3(0.0, -gap, 0.0))); last_t.add_child(scad!(LinearExtrude(extrude.clone()); last)); //hull.add_child(last_t); //hull.add_child(scad!(LinearExtrude(extrude); shape.clone())); diff --git a/src/selig.rs b/src/selig.rs index 42634a8..f67dfd7 100644 --- a/src/selig.rs +++ b/src/selig.rs @@ -1,7 +1,4 @@ use std::ops::Range; -use std::ops::RangeFull; -use std::ops::Deref; -use std::ops::Index; use nalgebra::Vector2; @@ -19,20 +16,24 @@ impl SeligFile { let mut points = Vec::new(); let mut lines = file.lines(); - let mut header = lines.next().expect("selig .dat is empty").split(" | ").map(|x| x.trim()); + let mut header = lines + .next() + .expect("selig .dat is empty") + .split(" | ") + .map(|x| x.trim()); let name = header.next().unwrap().to_string(); let description = header.next().map(|d| d.to_string()); for line in lines { - let mut numbers =line.split(" ").filter(|n| !n.trim().is_empty()); + let mut numbers = line.split(" ").filter(|n| !n.trim().is_empty()); let x = numbers.next().unwrap().parse().unwrap(); let y = numbers.next().unwrap().parse().unwrap(); - points.push(Point::new(x,y)) + points.push(Point::new(x, y)) } SeligFile { name, description, - data: points + data: points, } } @@ -47,7 +48,6 @@ impl SeligFile { pub fn get_description(&self) -> &Option { &self.description } - } pub trait Span { @@ -79,21 +79,27 @@ impl Span for Airfoil { for point in self { if let Some(last) = last_point { let span: f32 = (last - point).magnitude(); - match (range.contains(&distance), range.contains(&(distance+span))) { - (true, true) => { // fully within span + match ( + range.contains(&distance), + range.contains(&(distance + span)), + ) { + (true, true) => { + // fully within span points.push(point.clone()); - }, - (false, true) => { // entering span + } + (false, true) => { + // entering span let undershoot = distance - range.start; let part_out = (span - undershoot) / span; points.push(last.lerp(&point, part_out)); - }, - (true, false) => { // exiting span + } + (true, false) => { + // exiting span let overshoot = range.end - distance; let part_in = (span - overshoot) / span; points.push(last.lerp(&point, part_in)); - }, - _ => {}, + } + _ => {} } distance += span; } @@ -105,7 +111,7 @@ impl Span for Airfoil { #[cfg(test)] mod tests { - use super::{Span, SeligFile}; + use super::{SeligFile, Span}; #[test] fn full_perimiter() {