Compare commits
10 commits
935a0e43d2
...
6fd328ba4a
Author | SHA1 | Date | |
---|---|---|---|
6fd328ba4a | |||
038394f2eb | |||
ee07ec1b2c | |||
873eaaf82c | |||
c1a3553898 | |||
2746379238 | |||
04dd82cbd7 | |||
f59aeb6594 | |||
c536047a43 | |||
a80a6d89dd |
3 changed files with 264 additions and 103 deletions
48
Makefile
48
Makefile
|
@ -1,3 +1,8 @@
|
||||||
|
MAIN_AIRFOIL = ag41d
|
||||||
|
ELEVATOR_AIRFOIL = b540ols-il
|
||||||
|
RUDDER_AIRFOIL = ht13
|
||||||
|
TAIL = t-tail
|
||||||
|
|
||||||
UNAME = $(shell uname)
|
UNAME = $(shell uname)
|
||||||
ifeq ($(UNAME), Linux)
|
ifeq ($(UNAME), Linux)
|
||||||
SCAD = openscad
|
SCAD = openscad
|
||||||
|
@ -6,14 +11,24 @@ ifeq ($(UNAME), Darwin)
|
||||||
SCAD = OpenSCAD
|
SCAD = OpenSCAD
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
AIRFOILS = $(MAIN_AIRFOIL).dat $(ELEVATOR_AIRFOIL).dat $(RUDDER_AIRFOIL).dat
|
||||||
|
|
||||||
b: build/allparts.svg
|
b: build/allparts.svg
|
||||||
|
|
||||||
build:
|
build:
|
||||||
mkdir -p build
|
mkdir -p build
|
||||||
|
|
||||||
build/%.scad: src/*.rs ag24.dat m1-il.dat
|
build/assembly.scad: src/*.rs $(AIRFOILS)
|
||||||
mkdir -p build
|
mkdir -p build
|
||||||
cargo run
|
cargo run $(AIRFOILS) $(TAIL) assembly
|
||||||
|
|
||||||
|
build/part%.scad: src/*.rs $(AIRFOILS)
|
||||||
|
mkdir -p build
|
||||||
|
cargo run $(AIRFOILS) parts $(TAIL)
|
||||||
|
|
||||||
|
build/allparts.scad: src/*.rs $(AIRFOILS)
|
||||||
|
mkdir -p build
|
||||||
|
cargo run $(AIRFOILS) sheet $(TAIL)
|
||||||
|
|
||||||
e393.dat:
|
e393.dat:
|
||||||
curl http://airfoiltools.com/airfoil/seligdatfile?airfoil=e393-il -o e393.dat
|
curl http://airfoiltools.com/airfoil/seligdatfile?airfoil=e393-il -o e393.dat
|
||||||
|
@ -21,6 +36,9 @@ e393.dat:
|
||||||
ag24.dat:
|
ag24.dat:
|
||||||
curl https://m-selig.ae.illinois.edu/ads/coord/ag24.dat -o ag24.dat
|
curl https://m-selig.ae.illinois.edu/ads/coord/ag24.dat -o ag24.dat
|
||||||
|
|
||||||
|
ag41d.dat:
|
||||||
|
curl https://web.mit.edu/drela/Public/web/avl/runs/ag41d.dat -o $@
|
||||||
|
|
||||||
sd8020.dat:
|
sd8020.dat:
|
||||||
curl http://airfoiltools.com/airfoil/seligdatfile?airfoil=sd8020-il -o $@
|
curl http://airfoiltools.com/airfoil/seligdatfile?airfoil=sd8020-il -o $@
|
||||||
|
|
||||||
|
@ -35,7 +53,7 @@ edgevertical.dat: edgevertical.zip
|
||||||
unzip -p edgevertical.zip Edge-Vertical.dat > edgevertical.dat
|
unzip -p edgevertical.zip Edge-Vertical.dat > edgevertical.dat
|
||||||
|
|
||||||
build/%.png: build/%.scad
|
build/%.png: build/%.scad
|
||||||
$(SCAD) --colorscheme Metallic -o $@ $<
|
$(SCAD) --imgsize 1920,1080 --colorscheme Metallic -o $@ $<
|
||||||
|
|
||||||
build/%.svg: build/%.scad
|
build/%.svg: build/%.scad
|
||||||
$(SCAD) -o $@ $<
|
$(SCAD) -o $@ $<
|
||||||
|
@ -49,11 +67,11 @@ build/%.dxf: build/%.scad
|
||||||
build/%.dwg: build/%.svg
|
build/%.dwg: build/%.svg
|
||||||
svg2dwg -no-gui -no-dock-icon -o $@ $<
|
svg2dwg -no-gui -no-dock-icon -o $@ $<
|
||||||
|
|
||||||
.PHONY: svgs dxfs clean b
|
.PHONY: svgs dxfs clean b v-tail
|
||||||
svgs: build/%.scad
|
svgs: build/part%.scad
|
||||||
ls build/part*.scad | sed 's/scad/svg/' | xargs make
|
ls build/part*.scad | sed 's/scad/svg/' | xargs make
|
||||||
|
|
||||||
dxfs: build/%.scad
|
dxfs: build/part%.scad
|
||||||
ls build/part*.scad | sed 's/scad/dxf/' | xargs make
|
ls build/part*.scad | sed 's/scad/dxf/' | xargs make
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
@ -62,14 +80,20 @@ clean:
|
||||||
cargo clean
|
cargo clean
|
||||||
|
|
||||||
vis: build/assembly-clear.scad
|
vis: build/assembly-clear.scad
|
||||||
openscad build/assembly-clear.scad
|
$(SCAD) build/assembly-clear.scad
|
||||||
|
|
||||||
|
v-tail: src/*.rs $(AIRFOILS)
|
||||||
|
cargo run $(AIRFOILS) v-tail assembly sheet parts
|
||||||
|
|
||||||
|
t-tail: src/*.rs $(AIRFOILS)
|
||||||
|
cargo run $(AIRFOILS) t-tail assembly sheet parts
|
||||||
|
|
||||||
build/testwing-clear.scad: build/testwing.scad
|
build/testwing-clear.scad: build/testwing.scad
|
||||||
sed 's/color("clear-red")/color([1.0,1.0,0.0,0.9])/g' $< > $@
|
sed 's/color("clear-red")/color([1.0,1.0,0.0,0.9])/g' $< > $@
|
||||||
sed 's/color("clear-blue")/color([0.0,1.0,1.0,1.0])/g' -i $@
|
sed 's/color("clear-blue")/color([0.0,1.0,1.0,1.0])/g' -i $@
|
||||||
|
|
||||||
build/assembly-clear.scad: build/assembly.scad
|
build/assembly-clear.scad: src/*.rs $(AIRFOILS) build
|
||||||
cargo run --features film
|
cargo run $(AIRFOILS) assembly $(TAIL) film stdout |\
|
||||||
sed 's/color("clear-red")/color([1.0,0.0,0.0,0.5])/g' $< > $@
|
sed 's/color("clear-red")/color([1.0,0.0,0.0,0.5])/g' |\
|
||||||
sed 's/color("clear-blue")/color([0.0,1.0,0.0,0.9])/g' -i $@
|
sed 's/color("clear-blue")/color([0.0,1.0,0.0,0.9])/g' |\
|
||||||
sed 's/color("clear-blue")/color([0.0,0.0,1.0,0.9])/g' -i $@
|
sed 's/color("clear-blue")/color([0.0,0.0,1.0,0.9])/g' > $@
|
||||||
|
|
310
src/main.rs
310
src/main.rs
|
@ -3,26 +3,59 @@ use nalgebra::Vector3;
|
||||||
use scad::*;
|
use scad::*;
|
||||||
use selig::SeligFile;
|
use selig::SeligFile;
|
||||||
use selig::Span;
|
use selig::Span;
|
||||||
|
use std::env::args;
|
||||||
|
use std::path::Path;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
mod constants;
|
mod constants;
|
||||||
mod selig;
|
mod selig;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
let mut args = args().skip(1);
|
||||||
|
let main_airfoil_path = args.next().unwrap();
|
||||||
|
let main_airfoil_path = Path::new(&main_airfoil_path);
|
||||||
|
let elevator_airfoil_path = args.next().unwrap();
|
||||||
|
let elevator_airfoil_path = Path::new(&elevator_airfoil_path);
|
||||||
|
let rudder_airfoil_path = args.next().unwrap();
|
||||||
|
let rudder_airfoil_path = Path::new(&rudder_airfoil_path);
|
||||||
|
let args: Vec<String> = args.collect();
|
||||||
|
let gen_film = args.contains(&"film".to_string());
|
||||||
|
let gen_v_tail = args.contains(&"v-tail".to_string());
|
||||||
|
let gen_t_tail = args.contains(&"t-tail".to_string());
|
||||||
|
let gen_parts = args.contains(&"parts".to_string());
|
||||||
|
let gen_allparts = args.contains(&"sheet".to_string());
|
||||||
|
let gen_assembly = args.contains(&"assembly".to_string());
|
||||||
|
let gen_test = args.contains(&"test".to_string());
|
||||||
|
let stdout = args.contains(&"stdout".to_string());
|
||||||
|
|
||||||
let mut scad_file = ScadFile::new();
|
let mut scad_file = ScadFile::new();
|
||||||
|
|
||||||
let mut parts = Vec::new();
|
let mut parts = Vec::new();
|
||||||
scad_file.set_detail(50);
|
scad_file.set_detail(50);
|
||||||
|
|
||||||
// cambered airfoil, used in the wing
|
// cambered airfoil, used in the wing
|
||||||
let wing_airfoil: SeligFile = SeligFile::parse(include_str!("../ag24.dat"));
|
let wing_airfoil: SeligFile = SeligFile::load(main_airfoil_path).unwrap();
|
||||||
|
eprintln!("Main airfoil: {}", wing_airfoil.get_name());
|
||||||
|
if let Some(desc) = wing_airfoil.get_description() {
|
||||||
|
eprintln!("{}", desc);
|
||||||
|
}
|
||||||
|
|
||||||
// symmetric airfoil, used in the control surfaces
|
// symmetric airfoil
|
||||||
let control_airfoil: SeligFile = SeligFile::parse(include_str!("../m1-il.dat"));
|
let rudder_airfoil: SeligFile = SeligFile::load(rudder_airfoil_path).unwrap();
|
||||||
|
eprintln!("Rudder airfoil: {}", rudder_airfoil.get_name());
|
||||||
|
if let Some(desc) = rudder_airfoil.get_description() {
|
||||||
|
eprintln!("{}", desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
let elevator_airfoil: SeligFile = SeligFile::load(elevator_airfoil_path).unwrap();
|
||||||
|
eprintln!("Elevator airfoil: {}", elevator_airfoil.get_name());
|
||||||
|
if let Some(desc) = elevator_airfoil.get_description() {
|
||||||
|
eprintln!("{}", desc);
|
||||||
|
}
|
||||||
|
|
||||||
// main wing
|
// main wing
|
||||||
let mut wing_transform = mirrored_wing(&wing_airfoil, &WING);
|
let mut wing_transform = mirrored_wing(&wing_airfoil, &WING);
|
||||||
|
|
||||||
#[cfg(feature = "film")] {
|
if gen_film {
|
||||||
let mut wrapped = scad!(Hull; wing_transform.visualization.clone());
|
let mut wrapped = scad!(Hull; wing_transform.visualization.clone());
|
||||||
wrapped = scad!(Minkowski; wrapped);
|
wrapped = scad!(Minkowski; wrapped);
|
||||||
wrapped.add_child(scad!(Sphere(Radius(1.0))));
|
wrapped.add_child(scad!(Sphere(Radius(1.0))));
|
||||||
|
@ -33,23 +66,100 @@ fn main() {
|
||||||
parts.append(wing_transform.parts.as_mut());
|
parts.append(wing_transform.parts.as_mut());
|
||||||
|
|
||||||
// "fuselage"
|
// "fuselage"
|
||||||
|
let mut fuselage = spar(LENGTH);
|
||||||
scad_file.add_object(scad!(Rotate(-90.0, vec3(0.0, 0.0, 1.0));
|
scad_file.add_object(scad!(Rotate(-90.0, vec3(0.0, 0.0, 1.0));
|
||||||
spar(LENGTH, false)));
|
fuselage.visualization));
|
||||||
|
parts.append(fuselage.parts.as_mut());
|
||||||
|
|
||||||
|
if gen_t_tail {
|
||||||
|
t_tail(
|
||||||
|
&rudder_airfoil,
|
||||||
|
&mut parts,
|
||||||
|
&mut scad_file,
|
||||||
|
&elevator_airfoil,
|
||||||
|
gen_film,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if gen_v_tail {
|
||||||
|
v_tail(
|
||||||
|
&rudder_airfoil,
|
||||||
|
&mut parts,
|
||||||
|
&mut scad_file,
|
||||||
|
&elevator_airfoil,
|
||||||
|
gen_film,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if gen_assembly {
|
||||||
|
// every part visualized together
|
||||||
|
if stdout {
|
||||||
|
print!("{}",scad_file.get_code());
|
||||||
|
} else {
|
||||||
|
scad_file.write_to_file(String::from("build/assembly.scad"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if gen_parts {
|
||||||
|
// every part in seperate files
|
||||||
|
for (idx, part) in parts.clone().into_iter().enumerate() {
|
||||||
|
let mut file = ScadFile::new();
|
||||||
|
file.set_detail(50);
|
||||||
|
file.add_object(part);
|
||||||
|
if stdout {
|
||||||
|
print!("{}",file.get_code());
|
||||||
|
} else {
|
||||||
|
file.write_to_file(format!("build/part{idx:0>3}.scad"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if gen_allparts {
|
||||||
|
// all parts in one file
|
||||||
|
let mut allparts = ScadFile::new();
|
||||||
|
parts.push(scad!(Square(vec2(STOCK_WIDTH, STOCK_HEIGHT))));
|
||||||
|
allparts.set_detail(50);
|
||||||
|
for (idx, part) in parts.into_iter().enumerate() {
|
||||||
|
allparts.add_object(scad!(Translate2d(vec2(0.0, INF * idx as f32)); part));
|
||||||
|
}
|
||||||
|
if stdout {
|
||||||
|
print!("{}",scad_file.get_code());
|
||||||
|
} else {
|
||||||
|
allparts.write_to_file("build/allparts.scad".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if gen_test {
|
||||||
|
// test airfoil
|
||||||
|
let mut test = ScadFile::new();
|
||||||
|
let test_airfoil = SeligFile::parse(include_str!("../m1-il.dat"));
|
||||||
|
let wing = wing(&test_airfoil, &RUDDER, SparType::Top);
|
||||||
|
test.add_object(scad!(NamedColor("clear-blue".to_string()); wing.0.visualization));
|
||||||
|
test.add_object(scad!(NamedColor("clear-red".to_string()); wing.1.visualization));
|
||||||
|
if stdout {
|
||||||
|
print!("{}",test.get_code());
|
||||||
|
} else {
|
||||||
|
test.write_to_file(String::from("build/testwing.scad"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn t_tail(
|
||||||
|
control_airfoil: &SeligFile,
|
||||||
|
parts: &mut Vec<ScadObject>,
|
||||||
|
scad_file: &mut ScadFile,
|
||||||
|
wing_airfoil: &SeligFile,
|
||||||
|
gen_film: bool,
|
||||||
|
) {
|
||||||
// rudder
|
// rudder
|
||||||
let mut rudder = scad!(Rotate(90.0, vec3(1.0, 0.0, 0.0)));
|
let mut rudder = scad!(Rotate(90.0, vec3(1.0, 0.0, 0.0)));
|
||||||
let (mut struts,mut spar) = wing(
|
let (mut struts, mut spar) = wing(&control_airfoil, &RUDDER, SparType::Center);
|
||||||
&control_airfoil,
|
|
||||||
&RUDDER,
|
|
||||||
SparType::Center,
|
|
||||||
);
|
|
||||||
rudder.add_child(struts.visualization);
|
rudder.add_child(struts.visualization);
|
||||||
rudder.add_child(spar.visualization);
|
rudder.add_child(spar.visualization);
|
||||||
parts.append(struts.parts.as_mut());
|
parts.append(struts.parts.as_mut());
|
||||||
parts.append(spar.parts.as_mut());
|
parts.append(spar.parts.as_mut());
|
||||||
rudder = scad!(Translate(vec3(LENGTH-RUDDER_CHORD, 0.0, 0.0)); rudder);
|
rudder = scad!(Translate(vec3(LENGTH-RUDDER_CHORD, 0.0, 0.0)); rudder);
|
||||||
|
|
||||||
#[cfg(feature = "film")] {
|
if gen_film {
|
||||||
let mut wrapped = scad!(Hull; rudder.clone());
|
let mut wrapped = scad!(Hull; rudder.clone());
|
||||||
wrapped = scad!(Minkowski; wrapped);
|
wrapped = scad!(Minkowski; wrapped);
|
||||||
wrapped.add_child(scad!(Sphere(Radius(1.0))));
|
wrapped.add_child(scad!(Sphere(Radius(1.0))));
|
||||||
|
@ -61,7 +171,7 @@ fn main() {
|
||||||
// elevator
|
// elevator
|
||||||
let mut elevator = mirrored_wing(&wing_airfoil, &ELEVATOR);
|
let mut elevator = mirrored_wing(&wing_airfoil, &ELEVATOR);
|
||||||
|
|
||||||
#[cfg(feature = "film")] {
|
if gen_film {
|
||||||
let elevator = scad!(Translate(vec3(LENGTH - ELEVATOR_CHORD, 0.0, 0.0)); elevator.visualization.clone());
|
let elevator = scad!(Translate(vec3(LENGTH - ELEVATOR_CHORD, 0.0, 0.0)); elevator.visualization.clone());
|
||||||
let mut wrapped = scad!(Hull; elevator);
|
let mut wrapped = scad!(Hull; elevator);
|
||||||
wrapped = scad!(Minkowski; wrapped);
|
wrapped = scad!(Minkowski; wrapped);
|
||||||
|
@ -69,36 +179,44 @@ fn main() {
|
||||||
scad_file.add_object(scad!(NamedColor("clear-red".to_string()); wrapped));
|
scad_file.add_object(scad!(NamedColor("clear-red".to_string()); wrapped));
|
||||||
}
|
}
|
||||||
|
|
||||||
scad_file.add_object(scad!(Translate(vec3(LENGTH - ELEVATOR_CHORD, 0.0, 0.0)); elevator.visualization));
|
scad_file.add_object(
|
||||||
|
scad!(Translate(vec3(LENGTH - ELEVATOR_CHORD, 0.0, 0.0)); elevator.visualization),
|
||||||
|
);
|
||||||
parts.append(&mut elevator.parts);
|
parts.append(&mut elevator.parts);
|
||||||
|
}
|
||||||
|
|
||||||
// every part visualized together
|
fn v_tail(
|
||||||
scad_file.write_to_file(String::from("build/assembly.scad"));
|
_control_airfoil: &SeligFile,
|
||||||
|
parts: &mut Vec<ScadObject>,
|
||||||
|
scad_file: &mut ScadFile,
|
||||||
|
wing_airfoil: &SeligFile,
|
||||||
|
gen_film: bool,
|
||||||
|
) {
|
||||||
|
// rudder
|
||||||
|
let mut ruddervators = scad!(Union);
|
||||||
|
let mut ruddervator = scad!(Union);
|
||||||
|
ruddervator = scad!(Rotate(60.0, vec3(1.0, 0.0, 0.0)); ruddervator);
|
||||||
|
let (mut struts, mut spar) = wing(&wing_airfoil, &RUDDER, SparType::Top);
|
||||||
|
ruddervator.add_child(struts.visualization);
|
||||||
|
ruddervator.add_child(spar.visualization);
|
||||||
|
ruddervator = scad!(Translate(vec3(0.0, FUSELAGE_GAP, 0.0)); ruddervator);
|
||||||
|
parts.append(struts.parts.clone().as_mut());
|
||||||
|
parts.append(spar.parts.clone().as_mut());
|
||||||
|
parts.append(struts.parts.as_mut());
|
||||||
|
parts.append(spar.parts.as_mut());
|
||||||
|
ruddervators.add_child(scad!(Mirror(vec3(0.0, 1.0, 0.0)); ruddervator.clone()));
|
||||||
|
ruddervators.add_child(ruddervator);
|
||||||
|
ruddervators = scad!(Translate(vec3(LENGTH-RUDDER_CHORD, 0.0, 0.0)); ruddervators);
|
||||||
|
|
||||||
// every part in seperate files
|
if gen_film {
|
||||||
for (idx, part) in parts.clone().into_iter().enumerate() {
|
// this won't look right
|
||||||
let mut file = ScadFile::new();
|
let mut wrapped = scad!(Hull; ruddervators.clone());
|
||||||
file.set_detail(50);
|
wrapped = scad!(Minkowski; wrapped);
|
||||||
file.add_object(part);
|
wrapped.add_child(scad!(Sphere(Radius(1.0))));
|
||||||
file.write_to_file(format!("build/part{idx:0>3}.scad"));
|
scad_file.add_object(scad!(NamedColor("clear-red".to_string()); wrapped));
|
||||||
}
|
}
|
||||||
|
|
||||||
// all parts in one file
|
scad_file.add_object(ruddervators);
|
||||||
let mut allparts = ScadFile::new();
|
|
||||||
parts.push(scad!(Square(vec2(STOCK_WIDTH, STOCK_HEIGHT))));
|
|
||||||
allparts.set_detail(50);
|
|
||||||
for (idx, part) in parts.into_iter().enumerate() {
|
|
||||||
allparts.add_object(scad!(Translate2d(vec2(0.0, INF * idx as f32)); part));
|
|
||||||
}
|
|
||||||
allparts.write_to_file("build/allparts.scad".to_string());
|
|
||||||
|
|
||||||
// test airfoil
|
|
||||||
let mut test = ScadFile::new();
|
|
||||||
let test_airfoil = SeligFile::parse(include_str!("../m1-il.dat"));
|
|
||||||
let wing = wing(&test_airfoil, &RUDDER, SparType::Top);
|
|
||||||
test.add_object(scad!(NamedColor("clear-blue".to_string()); wing.0.visualization));
|
|
||||||
test.add_object(scad!(NamedColor("clear-red".to_string()); wing.1.visualization));
|
|
||||||
test.write_to_file(String::from("build/testwing.scad"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A physical object
|
/// A physical object
|
||||||
|
@ -116,11 +234,6 @@ impl Construct {
|
||||||
parts,
|
parts,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A construct with no parts
|
|
||||||
fn cosmetic(visualization: ScadObject) -> Self {
|
|
||||||
Construct { visualization, parts: Vec::new() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert to tuple
|
/// Convert to tuple
|
||||||
/// (visualization, parts)
|
/// (visualization, parts)
|
||||||
|
@ -142,7 +255,7 @@ fn mirrored_wing(wing_airfoil: &SeligFile, wing_config: &WingConfig) -> Construc
|
||||||
|
|
||||||
parts.append(strut.parts.as_mut());
|
parts.append(strut.parts.as_mut());
|
||||||
|
|
||||||
let transform = Translate(vec3(0.0, FUSELAGE_GAP,0.0));
|
let transform = Translate(vec3(0.0, FUSELAGE_GAP, 0.0));
|
||||||
|
|
||||||
wing = scad!(transform.clone(); wing);
|
wing = scad!(transform.clone(); wing);
|
||||||
top_spar_neg = scad!(transform; top_spar_neg);
|
top_spar_neg = scad!(transform; top_spar_neg);
|
||||||
|
@ -164,46 +277,53 @@ fn mirrored_wing(wing_airfoil: &SeligFile, wing_config: &WingConfig) -> Construc
|
||||||
(false, true, false),
|
(false, true, false),
|
||||||
));
|
));
|
||||||
parts.push(scad!(Projection(false); symetric_spar.clone()));
|
parts.push(scad!(Projection(false); symetric_spar.clone()));
|
||||||
Construct::new(
|
Construct::new(wing_transform.clone(), parts)
|
||||||
wing_transform.clone()
|
|
||||||
, parts)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns (struts, spar)
|
/// Returns (struts, spar)
|
||||||
fn wing(wing_airfoil: &SeligFile, wing_config: &WingConfig, spar: SparType) -> (Construct, Construct) {
|
fn wing(
|
||||||
let (wing, wing_parts) = wing_struts(
|
wing_airfoil: &SeligFile,
|
||||||
wing_airfoil,
|
wing_config: &WingConfig,
|
||||||
wing_config,
|
spar: SparType,
|
||||||
&spar,
|
) -> (Construct, Construct) {
|
||||||
).tup();
|
let (wing, wing_parts) = wing_struts(wing_airfoil, wing_config, &spar).tup();
|
||||||
// TODO: other spar types
|
// TODO: other spar types
|
||||||
let spar = match spar {
|
let spar = match spar {
|
||||||
SparType::None => {
|
SparType::None => {
|
||||||
|
|
||||||
scad!(Union)
|
scad!(Union)
|
||||||
},
|
}
|
||||||
SparType::Top => {
|
SparType::Top => {
|
||||||
let top_spar = topwing_spar(
|
let top_spar = topwing_spar(wing_airfoil, wing_config);
|
||||||
wing_airfoil,
|
|
||||||
wing_config,
|
|
||||||
);
|
|
||||||
let mut spar = scad!(Difference; top_spar);
|
let mut spar = scad!(Difference; top_spar);
|
||||||
spar.add_child(wing.clone());
|
spar.add_child(wing.clone());
|
||||||
spar
|
spar
|
||||||
},
|
}
|
||||||
SparType::Center => {
|
SparType::Center => {
|
||||||
// TODO: taper
|
// TODO: taper
|
||||||
let mut mid_spar = scad!(Hull);
|
let mut mid_spar = scad!(Hull);
|
||||||
let bottom = centered_cube(vec3(wing_config.chord * (MIDSPAR_RANGE.end - MIDSPAR_RANGE.start) , CARDBOARD_WIDTH, CARDBOARD_WIDTH), (false, false, true));
|
let bottom = centered_cube(
|
||||||
let mut top = scad!(Translate(vec3(wing_config.taper * MIDSPAR_RANGE.start * wing_config.chord - MIDSPAR_RANGE.start * wing_config.chord, wing_config.length, 0.0)));
|
vec3(
|
||||||
|
wing_config.chord * (MIDSPAR_RANGE.end - MIDSPAR_RANGE.start),
|
||||||
|
CARDBOARD_WIDTH,
|
||||||
|
CARDBOARD_WIDTH,
|
||||||
|
),
|
||||||
|
(false, false, true),
|
||||||
|
);
|
||||||
|
let mut top = scad!(Translate(vec3(
|
||||||
|
wing_config.taper * MIDSPAR_RANGE.start * wing_config.chord
|
||||||
|
- MIDSPAR_RANGE.start * wing_config.chord,
|
||||||
|
wing_config.length,
|
||||||
|
0.0
|
||||||
|
)));
|
||||||
top.add_child(scad!(Scale(vec3(wing_config.taper, 1.0, 1.0)); bottom.clone()));
|
top.add_child(scad!(Scale(vec3(wing_config.taper, 1.0, 1.0)); bottom.clone()));
|
||||||
mid_spar.add_child(top);
|
mid_spar.add_child(top);
|
||||||
mid_spar.add_child(bottom);
|
mid_spar.add_child(bottom);
|
||||||
mid_spar = scad!(Translate(vec3(MIDSPAR_RANGE.start * wing_config.chord, 0.0, 0.0)); mid_spar);
|
mid_spar =
|
||||||
|
scad!(Translate(vec3(MIDSPAR_RANGE.start * wing_config.chord, 0.0, 0.0)); mid_spar);
|
||||||
let mut spar = scad!(Difference; mid_spar);
|
let mut spar = scad!(Difference; mid_spar);
|
||||||
spar.add_child(wing.clone());
|
spar.add_child(wing.clone());
|
||||||
spar
|
spar
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let spar_flat = scad!(Projection(false); spar.clone());
|
let spar_flat = scad!(Projection(false); spar.clone());
|
||||||
|
@ -215,6 +335,7 @@ fn wing(wing_airfoil: &SeligFile, wing_config: &WingConfig, spar: SparType) -> (
|
||||||
|
|
||||||
enum SparType {
|
enum SparType {
|
||||||
/// No spar, just struts
|
/// No spar, just struts
|
||||||
|
#[allow(unused)]
|
||||||
None,
|
None,
|
||||||
/// Spar along the top of the wing
|
/// Spar along the top of the wing
|
||||||
Top,
|
Top,
|
||||||
|
@ -232,7 +353,7 @@ fn strut(airfoil: &SeligFile, chord: f32, width: f32, spar: &SparType) -> Constr
|
||||||
match spar {
|
match spar {
|
||||||
SparType::Top => topspar_negative(airfoil, chord, TOPSPAR_RANGE),
|
SparType::Top => topspar_negative(airfoil, chord, TOPSPAR_RANGE),
|
||||||
SparType::Center => {
|
SparType::Center => {
|
||||||
scad!(Translate2d(vec2(MIDSPAR_RANGE.start, 0.0));
|
scad!(Translate2d(vec2(MIDSPAR_RANGE.start, 0.0));
|
||||||
centered_square(vec2(MIDSPAR_RANGE.end - MIDSPAR_RANGE.start, CARDBOARD_WIDTH/ chord), (false,true))
|
centered_square(vec2(MIDSPAR_RANGE.end - MIDSPAR_RANGE.start, CARDBOARD_WIDTH/ chord), (false,true))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -267,12 +388,7 @@ fn strut(airfoil: &SeligFile, chord: f32, width: f32, spar: &SparType) -> Constr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extrude_strut(
|
fn extrude_strut(shape: ScadObject, strut_hole: ScadObject, width: f32, chord: f32) -> ScadObject {
|
||||||
shape: ScadObject,
|
|
||||||
strut_hole: ScadObject,
|
|
||||||
width: f32,
|
|
||||||
chord: f32,
|
|
||||||
) -> ScadObject {
|
|
||||||
let mut strut_shape = scad!(Difference);
|
let mut strut_shape = scad!(Difference);
|
||||||
strut_shape.add_child(shape);
|
strut_shape.add_child(shape);
|
||||||
strut_shape.add_child(strut_hole);
|
strut_shape.add_child(strut_hole);
|
||||||
|
@ -319,14 +435,19 @@ fn topspar_negative(airfoil: &SeligFile, chord: f32, range: Range<f32>) -> ScadO
|
||||||
mask
|
mask
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: shift to trangle design
|
fn spar(length: f32) -> Construct {
|
||||||
fn spar(length: f32, center: bool) -> ScadObject {
|
|
||||||
let mut spar = scad!(Union);
|
let mut spar = scad!(Union);
|
||||||
|
|
||||||
//register_part(scad!(Square(vec2(length, SPAR_SIDE_WIDTH * 3.0))));
|
let parts = vec![scad!(Square(vec2(length, SPAR_SIDE_WIDTH * 3.0)))];
|
||||||
|
|
||||||
let top = centered_cube(vec3(SPAR_SIDE_WIDTH, length, CARDBOARD_WIDTH), (true,false,false));
|
let top = centered_cube(
|
||||||
let mut left = centered_cube(vec3(SPAR_SIDE_WIDTH, length, CARDBOARD_WIDTH), (false,false,false));
|
vec3(SPAR_SIDE_WIDTH, length, CARDBOARD_WIDTH),
|
||||||
|
(true, false, false),
|
||||||
|
);
|
||||||
|
let mut left = centered_cube(
|
||||||
|
vec3(SPAR_SIDE_WIDTH, length, CARDBOARD_WIDTH),
|
||||||
|
(false, false, false),
|
||||||
|
);
|
||||||
left = scad!(Rotate(120.0, vec3(0.0, 1.0, 0.0)); left);
|
left = scad!(Rotate(120.0, vec3(0.0, 1.0, 0.0)); left);
|
||||||
left = scad!(Translate(vec3(SPAR_SIDE_WIDTH/2.0, 1.0, 0.0)); left);
|
left = scad!(Translate(vec3(SPAR_SIDE_WIDTH/2.0, 1.0, 0.0)); left);
|
||||||
let right = scad!(Mirror(vec3(1.0, 0.0, 0.0)); left.clone());
|
let right = scad!(Mirror(vec3(1.0, 0.0, 0.0)); left.clone());
|
||||||
|
@ -335,18 +456,17 @@ fn spar(length: f32, center: bool) -> ScadObject {
|
||||||
spar.add_child(right);
|
spar.add_child(right);
|
||||||
spar.add_child(top);
|
spar.add_child(top);
|
||||||
|
|
||||||
spar
|
Construct {
|
||||||
|
visualization: spar,
|
||||||
|
parts,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lerp(a: f32, b: f32, x: f32) -> f32 {
|
fn lerp(a: f32, b: f32, x: f32) -> f32 {
|
||||||
a * (1.0 - x) + b * x
|
a * (1.0 - x) + b * x
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wing_struts(
|
fn wing_struts(aerofoil: &SeligFile, config: &WingConfig, spar: &SparType) -> Construct {
|
||||||
aerofoil: &SeligFile,
|
|
||||||
config: &WingConfig,
|
|
||||||
spar: &SparType,
|
|
||||||
) -> Construct {
|
|
||||||
let mut wing = scad!(Translate(vec3(0.0, 0.0, 0.0)));
|
let mut wing = scad!(Translate(vec3(0.0, 0.0, 0.0)));
|
||||||
let mut parts = Vec::new();
|
let mut parts = Vec::new();
|
||||||
|
|
||||||
|
@ -354,7 +474,11 @@ fn wing_struts(
|
||||||
for strut_idx in 0..config.struts + 1 {
|
for strut_idx in 0..config.struts + 1 {
|
||||||
let gap = config.length / config.struts as f32;
|
let gap = config.length / config.struts as f32;
|
||||||
|
|
||||||
let chord = lerp(config.chord, config.chord * config.taper, strut_idx as f32 / config.struts as f32);
|
let chord = lerp(
|
||||||
|
config.chord,
|
||||||
|
config.chord * config.taper,
|
||||||
|
strut_idx as f32 / config.struts as f32,
|
||||||
|
);
|
||||||
|
|
||||||
let spacing = strut_idx as f32 * gap;
|
let spacing = strut_idx as f32 * gap;
|
||||||
|
|
||||||
|
@ -366,13 +490,13 @@ fn wing_struts(
|
||||||
wing.add_child(transform);
|
wing.add_child(transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
Construct { visualization: wing, parts }
|
Construct {
|
||||||
|
visualization: wing,
|
||||||
|
parts,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn topwing_spar(
|
fn topwing_spar(aerofoil: &SeligFile, config: &WingConfig) -> ScadObject {
|
||||||
aerofoil: &SeligFile,
|
|
||||||
config: &WingConfig
|
|
||||||
) -> ScadObject {
|
|
||||||
let mut wing = scad!(Hull);
|
let mut wing = scad!(Hull);
|
||||||
|
|
||||||
let mut pre_vis = scad!(Union);
|
let mut pre_vis = scad!(Union);
|
||||||
|
@ -381,7 +505,11 @@ fn topwing_spar(
|
||||||
for strut_idx in 0..config.struts + 1 {
|
for strut_idx in 0..config.struts + 1 {
|
||||||
let gap = config.length / config.struts as f32;
|
let gap = config.length / config.struts as f32;
|
||||||
|
|
||||||
let chord = lerp(config.chord, config.chord * config.taper, strut_idx as f32 / config.struts as f32);
|
let chord = lerp(
|
||||||
|
config.chord,
|
||||||
|
config.chord * config.taper,
|
||||||
|
strut_idx as f32 / config.struts as f32,
|
||||||
|
);
|
||||||
|
|
||||||
let spacing = strut_idx as f32 * gap;
|
let spacing = strut_idx as f32 * gap;
|
||||||
|
|
||||||
|
@ -402,8 +530,10 @@ fn topwing_spar(
|
||||||
/// Parameters for what would be half of a symmetrical (port/starboard) wing
|
/// Parameters for what would be half of a symmetrical (port/starboard) wing
|
||||||
/// All distance units are millimeters
|
/// All distance units are millimeters
|
||||||
pub struct WingConfig {
|
pub struct WingConfig {
|
||||||
pub length: f32, /// half of the wingspan
|
pub length: f32,
|
||||||
|
/// half of the wingspan
|
||||||
pub chord: f32,
|
pub chord: f32,
|
||||||
pub taper: f32, /// chord at wingtip in relation to the chord at the root
|
pub taper: f32,
|
||||||
|
/// chord at wingtip in relation to the chord at the root
|
||||||
pub struts: usize,
|
pub struts: usize,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use std::ops::Range;
|
use std::{ops::Range, path::Path, fs::File, io::Read};
|
||||||
|
use std::io;
|
||||||
|
|
||||||
use nalgebra::Vector2;
|
use nalgebra::Vector2;
|
||||||
|
|
||||||
|
@ -37,6 +38,12 @@ impl SeligFile {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn load(path: &Path) -> io::Result<Self> {
|
||||||
|
let mut buf = String::new();
|
||||||
|
File::open(path)?.read_to_string(&mut buf)?;
|
||||||
|
io::Result::Ok(Self::parse(&buf))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_points(&self) -> &Airfoil {
|
pub fn get_points(&self) -> &Airfoil {
|
||||||
&self.data
|
&self.data
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue