1
Fork 0
glider/src/main.rs
2023-11-01 14:15:47 -05:00

208 lines
6.8 KiB
Rust

use std::sync::Mutex;
use nalgebra::Vector3;
use scad::*;
use constants::*;
use selig::Airfoil;
use selig::Point;
use std::ops::Range;
mod constants;
mod selig;
static PARTS: Mutex<Vec<ScadObject>> = Mutex::new(Vec::new());
fn main() {
let mut scad_file = ScadFile::new();
//let mut parts: Vec<ScadObject> = Vec::new();
scad_file.set_detail(50);
// cambered airfoil, used in the wing
let wing_airfoil: Airfoil = Airfoil::parse(include_str!("../ag24.dat"));
println!("name {}, perim {}", wing_airfoil.get_name(), wing_airfoil.perimiter());
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];
let perimeter = span_length(points);
let span = points_in_range(&Vec::from(points), 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: Airfoil = Airfoil::parse(include_str!("../edgevertical.dat"));
let mut wing_transform = scad!(Translate(vec3(0.0,0.0,0.0)));
// struts
for port in [true,false] {
let mut wing = wing(&wing_airfoil, STRUTS/2, WINGSPAN/2.0, CHORD, CHORD * WING_TAPER);
wing = scad!(Translate(vec3(0.0, FUSELAGE_GAP,0.0)); wing);
if port {
wing = scad!(Mirror(vec3(0.0, 1.0, 0.0)); wing);
}
wing_transform.add_child(wing);
}
// spars
// main wing
scad_file.add_object(spar(WINGSPAN+FUSELAGE_GAP*2.0, true));
// "fuselage"
scad_file.add_object(scad!(Rotate(-90.0, vec3(0.0, 0.0, 1.0));
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));
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)));
for port in [true,false] {
let mut wing = wing(&control_airfoil, ELEVATOR_STRUTS, ELEVATOR_HEIGHT, ELEVATOR_CHORD, ELEVATOR_CHORD * ELEVATOR_TAPER);
wing = scad!(Translate(vec3(0.0, FUSELAGE_GAP,0.0)); wing);
if port {
wing = scad!(Mirror(vec3(0.0, 1.0, 0.0)); wing);
}
elevator.add_child(wing);
}
elevator.add_child(spar(ELEVATOR_HEIGHT*2.0+FUSELAGE_GAP*2.0, true));
scad_file.add_object(elevator);
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();
file.set_detail(50);
file.add_object(part);
file.write_to_file(format!("build/part{idx}.scad"));
}
let mut allparts = ScadFile::new();
allparts.set_detail(50);
for (idx, part) in PARTS.lock().unwrap().to_owned().into_iter().enumerate() {
allparts.add_object(scad!(Translate2d(vec2(0.0, INF * idx as f32)); part));
}
allparts.write_to_file(format!("build/allparts.scad"));
}
/// returns a extruded airfoil with the given dimensions
fn strut(airfoil: &Airfoil, chord: f32, width: f32) -> ScadObject {
let aerofoil = scad::PolygonParameters::new(airfoil.get_points().clone());
let shape = scad!(Polygon(aerofoil));
let extrude = LinExtrudeParams {
height: width,
center: true,
..Default::default()
};
let unit: Vector3<f32> = Vector3::new(1.0, 1.0, 1.0);
let scaled = scad!(Scale(unit * chord); shape);
register_part(scaled.clone());
let strut = scad!(LinearExtrude(extrude); scaled);
let rotated = scad!(Rotate(90.0, vec3(1.0, 0.0, 0.0)); strut);
rotated
}
fn spar(length: f32, center: bool) -> ScadObject {
let mut spar = scad!(Union);
register_part(scad!(Square(vec2(length, SPAR_SIDE_WIDTH * 3.0))));
for i in 0..3 {
let mut panel = scad!(Cube(vec3(SPAR_SIDE_WIDTH, length, CARDBOARD_WIDTH)));
if center {
panel = scad!(Translate(vec3(0.0, -length/2.0, 0.0)); panel)
}
let rot = 120.0 * i as f32;
spar.add_child(scad!(
Rotate(rot, vec3(0.0, 1.0, 0.0)); panel
));
}
spar
}
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 wing(aerofoil: &Airfoil, struts: usize, length: f32, chord: f32, taper: f32) -> 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 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 strut = strut(&aerofoil, chord, CARDBOARD_WIDTH);
transform.add_child(strut);
wing.add_child(transform);
}
wing
}
pub fn points_in_range(input: &Vec<Point>, range: Range<f32>) -> Vec<Point> {
let mut last_point: Option<Point> = None;
let mut distance: f32 = 0.0;
let mut points: Vec<Point> = Vec::new();
for point in input {
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
points.push(point.clone());
println!("in {span}");
},
(false, true) => { // entering span
let undershoot = distance - range.start;
let part_out = (span - undershoot) / span;
points.push(last.lerp(&point, part_out));
println!("enter");
},
(true, false) => { // exiting span
let overshoot = range.end - distance;
let part_in = (span - overshoot) / span;
points.push(last.lerp(&point, part_in));
},
_ => {},
}
distance += span;
}
last_point = Some(point.clone());
}
points
}
fn span_length(points: &[Point]) -> f32 {
let mut last_point: Option<Point> = None;
let mut distance: f32 = 0.0;
for point in points {
if let Some(last) = last_point {
distance += (last - point).magnitude();
}
last_point = Some(point.clone());
}
distance
}