169 lines
9.8 KiB
OpenSCAD
169 lines
9.8 KiB
OpenSCAD
// Zhestokost fortress (boss) "Nakovalnya" (anvil). Built from the Zhestokost kit.
|
|
// part = lang | brief | anchors | lines | primary | secondary | accent | recess | marking | emit:<id> | rad:<id>
|
|
// Frame: Ship Root, metres, nose -Y, starboard -X, up +Z. Anchor yaw: 0 nose, 90 port, 180 aft, 270 starboard.
|
|
include <zhestokost.scad>
|
|
part = "primary";
|
|
|
|
HULL_FACTION = "zhestokost";
|
|
HULL_DONOR = "";
|
|
HULL_ROLE = "fortress";
|
|
HULL_LENGTH = [45, 60];
|
|
HULL_TRIANGLES = [6000, 12000];
|
|
HULL_READS = ["three long thick heavy barrels from fixed armoured casemates built into the stepped prow, all pointing straight forward",
|
|
"a ring of ten small squat flak turrets on raised rings evenly spaced around the upper edge",
|
|
"huge low broad stepped mass, wider at the bow, tapering to a squared stern; narrow cockpit slit high on the prow",
|
|
"round feed collar with guide lugs and an oxide ammunition door on each flank, magazine hatches in rows on the dorsal plating; six turning blocks and two broad stern drives"];
|
|
if (part == "brief") echo(brief = [HULL_FACTION, HULL_DONOR, HULL_ROLE, HULL_LENGTH, HULL_TRIANGLES, HULL_READS]);
|
|
|
|
// ---- masses: three steps (hull, mid-deck, spine) with the casemates set into the prow's lowest step -----------------
|
|
// M: the hull, broadest at the bow, tapering aft to a squared stern with a cast lip. Its bow face is set back so the
|
|
// casemates sit inside the bow line.
|
|
M = [zh_s(-16, 32, 5.0, -2.25), zh_s(-15, 34, 5.5, -2.5), zh_s(5, 33, 5.5, -2.5), zh_s(14, 27, 5.5, -2.5),
|
|
zh_s(19.6, 23, 5.5, -2.5), zh_s(20, 22.4, 5.2, -2.35)];
|
|
DECK = 3.0;
|
|
// T1: the mid-deck, ~60% of the hull's width, 3 m up; the outer band is the flak ring's gallery.
|
|
T1 = zh_tier([zh_sec(M, -15), zh_sec(M, 5), zh_sec(M, 11)], 5.4, 3.0, 4.0);
|
|
// T2: the spine, 2 m above that, carrying the cab and its slit at the prow's top step.
|
|
T2 = zh_tier(T1, 4.0, 2.0, 2.5);
|
|
// the spine runs on aft at the same height to the stern shoulders
|
|
SPINE = zh_block(12.0, 12, 8, 5.05, DECK - 0.05, 0.8, 0, 0.5);
|
|
// the belly steps down too: a keel block under the centre
|
|
KEEL = zh_block(0.5, 30, 18, 1.6, -4.0, 0, 0.6, 0.8);
|
|
// The prow's lowest step: a glacis apron, and three casemates whose tops are flush with the hull deck.
|
|
APRON = zh_block(-18.5, 8, 26, 2.6, -2.7, 0.6, 0.5, 0.4);
|
|
CX = [-9.6, 0, 9.6];
|
|
CAS = zh_block(-18.0, 10, 7.2, 5.7, -2.7, 1.3, 0.9, 0.6);
|
|
GY = -23.0; // casemate face
|
|
GZ = 0.1; // barrel axis height
|
|
BL = 11.0; BR = 0.9; // barrel length, radius
|
|
// stern shoulders: radiator troughs either side of the spine
|
|
RAD_Y = 12.4; RAD_U = 0.55; RAD_W = 3.0; RAD_L = 2.4;
|
|
// stern drives
|
|
DX = 6.0; DRV = zh_block(20.6, 4, 8, 4.6, -2.3, 0.9, 0.7, 0.4);
|
|
DRIVE_Y = 22.6; DZ = -2.3 + 2.3;
|
|
|
|
zh_mass(M);
|
|
zh_mass(T1);
|
|
zh_mass(T2);
|
|
zh_mass(APRON);
|
|
for (x = CX) lp_at(t=[x, 0, 0]) zh_mass(CAS);
|
|
zh_mass(SPINE);
|
|
zh_mass(KEEL, lines=false); // under the hull: no top view sees it
|
|
for (s = [-1, 1]) lp_at(t=[s*DX, 0, 0]) zh_mass(DRV, "secondary");
|
|
|
|
// ---- the casemate guns: lighter cast-steel barrels out of charcoal mantlets -----------------------------------------
|
|
MANTLET = [4.0, 1.2, 3.4];
|
|
module casemate_gun() {
|
|
// mantlet frame: its fitting outline is drawn in the frame the mantlet is
|
|
lp_at(t=[0, GY - 0.3, GZ]) {
|
|
zh_z("secondary") cbox(MANTLET, 0.2);
|
|
lp_line("fitting", zh_rect(MANTLET.x, MANTLET.y, MANTLET.z/2), true);
|
|
}
|
|
// barrel frame: local +Y runs forward from the collar; the barrel's top line is drawn in the frame the barrel is
|
|
lp_at(t=[0, GY - 0.7, GZ], r=[0, 0, 180]) {
|
|
zh_z("secondary") tube(BR*1.55, 1.8, 8); // barrel collar
|
|
zh_z("primary") tube(BR, BL, 8);
|
|
zh_z("secondary") translate([0, BL, 0]) cbox([BR*2.7, 1.6, BR*2.7], 0.12); // muzzle block
|
|
zh_z("recess") translate([0, BL + 0.81, 0]) cube([BR*1.2, 0.05, BR*1.2], center=true);
|
|
lp_line("fitting", [[0, 0, BR], [0, BL, BR]]);
|
|
}
|
|
}
|
|
for (x = CX) lp_at(t=[x, 0, 0]) casemate_gun();
|
|
for (x = CX) lp_at(t=[x, 0, 0]) zh_on_top(CAS, -21.9, 0) zh_stripes(3.6, 0.6, 4);
|
|
// black reveals between the casemates, on the apron
|
|
for (u = [-0.33, 0.33]) zh_on_top(APRON, -19.5, u) zh_reveal(2.3, 7.0);
|
|
|
|
// ---- the flak ring: ten turrets on raised drums, equal arc-length around the hull's deck rim ----------------------
|
|
// [x, y, outward yaw]; F1 at the bow-port quarter, numbered clockwise seen from above; equal-arc-length stations, baked.
|
|
FLAK = [[5.43, -13.40, 21], [-5.43, -13.40, 339], [-13.77, -10.93, 311], [-13.50, -0.06, 274], [-11.53, 10.51, 230],
|
|
[-5.43, 17.20, 199], [5.43, 17.20, 161], [11.53, 10.51, 130], [13.50, -0.06, 86], [13.77, -10.93, 49]];
|
|
FR = 2.2; FH = 1.7; // raised drum
|
|
FG = [2, 0.27, 3.0, 3.2]; // zh_gun n, cal, len, base
|
|
module flak(yaw) {
|
|
zh_z("secondary") cylinder(r=FR, h=FH + ZH_SINK, $fn=8);
|
|
zh_z("accent") translate([0, 0, FH*0.35]) cylinder(r=FR + 0.08, h=0.35, $fn=8); // oxide band round the drum
|
|
zh_z("recess") translate([0, 0, FH - 0.06]) cylinder(r=FR*0.84, h=0.1, $fn=8);
|
|
lp_line("fitting", zh_oct(FR, FH), true);
|
|
lp_at(t=[0, 0, FH - 0.02], r=[0, 0, yaw]) zh_gun(FG[0], FG[1], FG[2], FG[3], "primary");
|
|
}
|
|
for (f = FLAK) lp_at(t=[f[0], f[1], DECK - ZH_SINK]) flak(f[2]);
|
|
function rz(a, p) = [p[0]*cos(a) - p[1]*sin(a), p[0]*sin(a) + p[1]*cos(a), p[2]];
|
|
|
|
// ---- bow: cab with the slit high on the spine's front step ---------------------------------------------------------
|
|
zh_on_top(T2, -6.4, 0) zh_cab(6.0, 3.4, 3.0, "primary");
|
|
zh_on_top(T2, -3.6, 0) zh_serial(0.7, 4);
|
|
|
|
// ---- dorsal magazine hatches: two rows of four on the mid-deck, clear of the rim -----------------------------------
|
|
for (y = [-8.0, -3.4, 1.2, 5.6], u = [-0.78, 0.78]) zh_on_top(T1, y, u) zh_hatch(2.8, 3.0, dogs=false);
|
|
PLATE_SEAM = [8.4, 0.5, 0.2];
|
|
for (y = [0.5, 4.5]) zh_on_top(T2, y, 0) lp_at(t=[0, 0, 0.06]) { // plate seams
|
|
zh_z("secondary") cube(PLATE_SEAM, center=true);
|
|
lp_line("seam", [[-PLATE_SEAM.x/2, 0, PLATE_SEAM.z/2], [PLATE_SEAM.x/2, 0, PLATE_SEAM.z/2]]);
|
|
}
|
|
|
|
// Feed collar for an Oboz: a lighter cast-steel flange ring standing proud of the flank round the kit collar.
|
|
module feed_collar() {
|
|
zh_z("primary") difference() {
|
|
cylinder(r=3.3, h=0.9 + ZH_SINK, $fn=8); translate([0, 0, -0.1]) cylinder(r=2.1, h=2, $fn=8); }
|
|
zh_collar(2.2, 1.5, 4);
|
|
}
|
|
// ---- flanks: feed collar, ammunition door, unit serial -------------------------------------------------------------
|
|
for (s = [-1, 1]) {
|
|
zh_on_belt(M, 1.5, s, 0.5) lp_at(r=[0, 0, 22.5]) feed_collar();
|
|
zh_on_wall(M, 7.0, s, 0.45) zh_hatch(2.6, 3.0);
|
|
zh_on_wall(M, -9.0, s, 0.5) zh_serial(0.8, 5);
|
|
}
|
|
|
|
// ---- six square turning blocks on the belt, a small exhaust slot in each -------------------------------------------
|
|
TB = [2.8, 2.6, 1.8]; // along Y, down the wall, outward
|
|
TY = [-11.5, 13.6];
|
|
module turn_block() {
|
|
zh_mass(zh_block(0, TB[0], TB[1], TB[2], 0, 0.45, 0.45, 0.35), "secondary");
|
|
zh_z("recess") translate([0, 0, TB[2] + 0.02]) cube([0.6, 1.4, 0.14], center=true);
|
|
}
|
|
for (y = TY, s = [-1, 1]) zh_on_belt(M, y, s, 0.5) turn_block();
|
|
// the forward pair face ahead from the bow corners, outboard of the casemates
|
|
NX = 14.6;
|
|
for (s = [-1, 1]) zh_on_nose(M, s*NX, 0.55) turn_block();
|
|
function nb_at(s) = zh_nose_pt(M, s*NX, 0.55) + [0, ZH_SINK - TB[2] - 0.1, 0];
|
|
function tb_at(y, s) = zh_belt(M, y, s, 0.5) + [s*(TB[2] + 0.1 - ZH_SINK), 0, 0];
|
|
|
|
// ---- stern: drives, radiator troughs on the shoulders, stern plate ------------------------------------------------
|
|
module drive_mouth() {
|
|
zh_z("secondary") translate([0, DRIVE_Y + 0.12, 0]) difference() {
|
|
cbox([7.0, 0.4, 3.6], 0.1); cube([5.4, 1, 2.4], center=true); }
|
|
zh_z("recess") translate([0, DRIVE_Y - 0.02, 0]) cube([5.5, 0.3, 2.5], center=true);
|
|
}
|
|
for (s = [-1, 1]) translate([s*DX, 0, DZ]) drive_mouth();
|
|
for (s = [-1, 1]) zh_on_top(M, RAD_Y, s*RAD_U) zh_rad_trough(RAD_W, RAD_L, 0.45);
|
|
zh_on_stern(M, 0, 0.62) zh_plate(4.0, 1.4, 0.3);
|
|
zh_on_stern(M, 0, 0.62) translate([0, 0, 0.3]) zh_stripes(3.4, 1.0, 4);
|
|
|
|
// ---- anchor meshes ------------------------------------------------------------------------------------------------
|
|
for (s = [-1, 1]) if (part == (s > 0 ? "rad:Ra.L" : "rad:Ra.R"))
|
|
zh_on_top(M, RAD_Y, s*RAD_U) for (h = [-1, 1]) zh_rad_vanes(RAD_W, RAD_L, h, 4, 1.0);
|
|
for (s = [-1, 1]) if (part == (s > 0 ? "emit:Th.L" : "emit:Th.R"))
|
|
translate([s*DX, DRIVE_Y + 0.15, DZ]) cube([5.2, 0.05, 2.3], center=true);
|
|
TID = [["Th.T3", TY[0], 1], ["Th.T4", TY[0], -1], ["Th.T5", TY[1], 1], ["Th.T6", TY[1], -1]];
|
|
for (t = TID) if (part == str("emit:", t[0])) translate(tb_at(t[1], t[2])) cube([0.05, 1.2, 0.4], center=true);
|
|
for (s = [-1, 1]) if (part == (s > 0 ? "emit:Th.T1" : "emit:Th.T2")) translate(nb_at(s)) cube([1.2, 0.05, 0.4], center=true);
|
|
|
|
if (part == "anchors") {
|
|
for (i = [0:2]) {
|
|
id = str("Gun.H", i + 1);
|
|
echo(anchor=[id, "weapon-mount", [CX[i], GY, GZ], 0, ""]);
|
|
echo(anchor=[str(id, ".muzzle"), "weapon-muzzle", [CX[i], GY - 0.7 - BL - 0.85, GZ], 0, id]);
|
|
}
|
|
for (i = [0:9]) {
|
|
f = FLAK[i]; id = str("Gun.F", i + 1); base = [f[0], f[1], DECK - ZH_SINK + FH - 0.02];
|
|
echo(anchor=[id, "weapon-mount", base + [0, 0, 0.8], f[2], ""]);
|
|
echo(anchor=[str(id, ".muzzle"), "weapon-muzzle", base + rz(f[2], zh_gun_muzzle(FG[0], FG[1], FG[2], FG[3])), f[2], id]);
|
|
}
|
|
echo(anchor=["Th.T1", "thruster-emitter", nb_at(1), 0, ""]);
|
|
echo(anchor=["Th.T2", "thruster-emitter", nb_at(-1), 0, ""]);
|
|
for (t = TID) echo(anchor=[t[0], "thruster-emitter", tb_at(t[1], t[2]), t[2] > 0 ? 90 : 270, ""]);
|
|
echo(anchor=["Th.L", "thruster-emitter", [DX, DRIVE_Y + 0.15, DZ], 180, ""]);
|
|
echo(anchor=["Th.R", "thruster-emitter", [-DX, DRIVE_Y + 0.15, DZ], 180, ""]);
|
|
echo(anchor=["Ra.L", "radiator-mesh", zh_top_at(M, RAD_Y, RAD_U, [0, 0, 0.5]), 0, ""]);
|
|
echo(anchor=["Ra.R", "radiator-mesh", zh_top_at(M, RAD_Y, -RAD_U, [0, 0, 0.5]), 0, ""]);
|
|
}
|