1358 lines
No EOL
37 KiB
OpenSCAD
1358 lines
No EOL
37 KiB
OpenSCAD
/*
|
|
===============================================================================
|
|
UNIVERSAL SIGN GENERATOR - OpenSCAD Nightly
|
|
Version 1.0 - 2026-09
|
|
-------------------------------------------------------------------------------
|
|
Selvstændig parametrisk skiltegenerator til:
|
|
- 3D-print
|
|
- CNC-fræsning / 2.5D-gravering
|
|
- Laserskæring / lasergravering
|
|
|
|
Designmål:
|
|
* Flere tekstlinjer med individuel font, størrelse, spacing og finjustering.
|
|
* Nightly textmetrics() + fontmetrics() til reel fontmåling og auto-sizing.
|
|
* Moderne em-baseret fontstørrelse kan vælges.
|
|
* Mange pladeformer, konstruktioner og monteringsformer kan kombineres.
|
|
* Raised / engraved / cut-through / inlay tekst.
|
|
* Raised / engraved / cut-through kant.
|
|
* 2D outputs til SVG/DXF og 3D outputs til STL/3MF.
|
|
* Ingen eksterne libraries.
|
|
|
|
NIGHTLY:
|
|
textmetrics() / fontmetrics() er fortsat en udviklingsfunktion i aktuelle
|
|
snapshots. I GUI: Preferences -> Features -> textmetrics.
|
|
CLI: openscad-nightly --enable=textmetrics ...
|
|
|
|
FALLBACK:
|
|
Sæt Use_Nightly_Metrics=false hvis du vil åbne filen i en ældre OpenSCAD.
|
|
Auto-sizing bliver da en tilnærmelse og em-mode bør ikke anvendes.
|
|
|
|
FONT-FILER:
|
|
Projektspecifikke .ttf/.otf kan registreres med en literal use<>-linje,
|
|
eksempel:
|
|
use <fonts/MyFont.ttf>
|
|
Derefter anvendes fontens logiske navn i Line_*_Font.
|
|
|
|
OBS:
|
|
OpenSCAD arbejder uden fysiske enheder; værdierne er tænkt som millimeter.
|
|
===============================================================================
|
|
*/
|
|
|
|
|
|
/* [Output] */
|
|
|
|
// Primært output
|
|
Output_Mode = "3D"; // ["3D","2D Cut Outline","2D Engrave Text","2D Text/Inlay","2D Border","2D Fold Score"]
|
|
|
|
// Grundkonstruktion
|
|
Construction = "Flat Plate"; // ["Flat Plate","Desk L-Stand","Desk Wedge","Desk Slot Stand","A-Frame Tent","Foldable Sheet Blank","Stake Sign"]
|
|
|
|
// Hvilken del vises/eksporteres i 3D
|
|
Part_Selection = "Assembly"; // ["Assembly","Face only","Support only","Text insert only"]
|
|
|
|
// Brug præcis Nightly fontmåling
|
|
Use_Nightly_Metrics = true;
|
|
|
|
|
|
/* [Skiltform] */
|
|
|
|
Plate_Shape = "Rounded Rectangle"; // ["Rectangle","Square","Rounded Rectangle","Capsule","Circle","Oval","Triangle","Diamond","Hexagon","Octagon","Shield","Arch","Tag","Arrow Right","Pennant"]
|
|
|
|
Auto_Size_Plate = true;
|
|
|
|
// Bruges hvis Auto_Size_Plate=false
|
|
Manual_Width = 160; // [20:1:600]
|
|
Manual_Height = 70; // [20:1:400]
|
|
|
|
// Minimum ved auto-size
|
|
Minimum_Width = 70; // [10:1:600]
|
|
Minimum_Height = 35; // [10:1:400]
|
|
|
|
// Luft omkring tekstblokken
|
|
Padding_X = 14; // [0:0.5:80]
|
|
Padding_Y = 10; // [0:0.5:80]
|
|
|
|
// Ekstra konservativ sikkerhed for former med smalle hjørner
|
|
Shape_Text_Safety = 1.00; // [0.5:0.01:1.2]
|
|
|
|
// Hjørner / formdetaljer
|
|
Corner_Radius = 8; // [0:0.5:60]
|
|
Tag_Cut = 10; // [1:0.5:60]
|
|
Arrow_Head_Fraction = 0.30; // [0.15:0.01:0.50]
|
|
Arch_Radius_Fraction = 0.40; // [0.20:0.01:0.50]
|
|
|
|
// Pladetykkelse
|
|
Plate_Thickness = 3.0; // [0.4:0.1:30]
|
|
|
|
// Kantprofil
|
|
Edge_Profile = "Straight"; // ["Straight","Chamfered"]
|
|
Bevel_Size = 0.8; // [0:0.1:5]
|
|
|
|
|
|
/* [Tekst - generelt] */
|
|
|
|
Font_Sizing_Mode = "size"; // ["size","em"]
|
|
|
|
Text_Mode = "Raised"; // ["Raised","Engraved","Cut-through","Inlay","None"]
|
|
|
|
Text_HAlign = "center"; // ["left","center","right"]
|
|
Text_Direction = "ltr"; // ["ltr","rtl","ttb","btt"]
|
|
Text_Language = "da";
|
|
Text_Script = "latin";
|
|
|
|
// Afstand mellem line-boxes ud over fontens interline
|
|
Line_Gap = 2.0; // [-5:0.25:30]
|
|
Leading_Scale = 1.00; // [0.5:0.01:2.0]
|
|
|
|
// Global flytning af tekstblokken
|
|
Text_Offset_X = 0; // [-100:0.5:100]
|
|
Text_Offset_Y = 0; // [-100:0.5:100]
|
|
|
|
// Ved fast pladestørrelse: nedskaler automatisk hvis teksten ikke kan være der
|
|
Scale_Text_Down_To_Fit = true;
|
|
Text_Global_Scale = 1.0; // [0.1:0.01:3]
|
|
|
|
// Relief / gravering
|
|
Text_Raised_Height = 1.0; // [0.1:0.1:10]
|
|
Text_Engrave_Depth = 0.8; // [0.1:0.1:20]
|
|
|
|
// Inlay
|
|
Inlay_Depth = 1.2; // [0.2:0.1:20]
|
|
Inlay_XY_Clearance = 0.15; // [0:0.01:1]
|
|
Inlay_Z_Clearance = 0.10; // [0:0.01:1]
|
|
|
|
|
|
/* [Tekstlinje 1] */
|
|
Line_1_Text = "MAKERSPACE";
|
|
Line_1_Size = 18; // [1:0.5:150]
|
|
Line_1_Font = "Liberation Sans:style=Bold";
|
|
Line_1_Spacing = 1.0; // [0.5:0.01:2]
|
|
Line_1_X = 0; // [-100:0.5:100]
|
|
Line_1_Y = 0; // [-100:0.5:100]
|
|
|
|
/* [Tekstlinje 2] */
|
|
Line_2_Text = "NÆSTVED";
|
|
Line_2_Size = 11; // [1:0.5:150]
|
|
Line_2_Font = "Liberation Sans";
|
|
Line_2_Spacing = 1.0; // [0.5:0.01:2]
|
|
Line_2_X = 0; // [-100:0.5:100]
|
|
Line_2_Y = 0; // [-100:0.5:100]
|
|
|
|
/* [Tekstlinje 3] */
|
|
Line_3_Text = "";
|
|
Line_3_Size = 9; // [1:0.5:150]
|
|
Line_3_Font = "Liberation Sans";
|
|
Line_3_Spacing = 1.0; // [0.5:0.01:2]
|
|
Line_3_X = 0; // [-100:0.5:100]
|
|
Line_3_Y = 0; // [-100:0.5:100]
|
|
|
|
/* [Tekstlinje 4] */
|
|
Line_4_Text = "";
|
|
Line_4_Size = 8; // [1:0.5:150]
|
|
Line_4_Font = "Liberation Sans";
|
|
Line_4_Spacing = 1.0; // [0.5:0.01:2]
|
|
Line_4_X = 0; // [-100:0.5:100]
|
|
Line_4_Y = 0; // [-100:0.5:100]
|
|
|
|
/* [Tekstlinje 5] */
|
|
Line_5_Text = "";
|
|
Line_5_Size = 8; // [1:0.5:150]
|
|
Line_5_Font = "Liberation Sans";
|
|
Line_5_Spacing = 1.0; // [0.5:0.01:2]
|
|
Line_5_X = 0; // [-100:0.5:100]
|
|
Line_5_Y = 0; // [-100:0.5:100]
|
|
|
|
/* [Tekstlinje 6] */
|
|
Line_6_Text = "";
|
|
Line_6_Size = 8; // [1:0.5:150]
|
|
Line_6_Font = "Liberation Sans";
|
|
Line_6_Spacing = 1.0; // [0.5:0.01:2]
|
|
Line_6_X = 0; // [-100:0.5:100]
|
|
Line_6_Y = 0; // [-100:0.5:100]
|
|
|
|
/* [Tekstlinje 7] */
|
|
Line_7_Text = "";
|
|
Line_7_Size = 8; // [1:0.5:150]
|
|
Line_7_Font = "Liberation Sans";
|
|
Line_7_Spacing = 1.0; // [0.5:0.01:2]
|
|
Line_7_X = 0; // [-100:0.5:100]
|
|
Line_7_Y = 0; // [-100:0.5:100]
|
|
|
|
/* [Tekstlinje 8] */
|
|
Line_8_Text = "";
|
|
Line_8_Size = 8; // [1:0.5:150]
|
|
Line_8_Font = "Liberation Sans";
|
|
Line_8_Spacing = 1.0; // [0.5:0.01:2]
|
|
Line_8_X = 0; // [-100:0.5:100]
|
|
Line_8_Y = 0; // [-100:0.5:100]
|
|
|
|
|
|
/* [Kant / border] */
|
|
|
|
Border_Style = "None"; // ["None","Raised","Engraved","Cut-through"]
|
|
Border_Width = 2.0; // [0.3:0.1:20]
|
|
Border_Inset = 3.0; // [0:0.1:30]
|
|
Border_Raised_Height = 0.8; // [0.1:0.1:10]
|
|
Border_Engrave_Depth = 0.6; // [0.1:0.1:20]
|
|
|
|
|
|
/* [Montering] */
|
|
|
|
Mounting_Style = "None"; // ["None","2 Hanging Holes","2 Side Holes","4 Corner Holes","2 Countersunk","4 Countersunk","Lanyard Hole","Zip-tie Slots","Keyhole Pair","Magnet Pair","Magnet Quad"]
|
|
|
|
Mount_Edge = 10; // [2:0.5:80]
|
|
Hole_Diameter = 4.0; // [1:0.1:30]
|
|
|
|
Countersink_Diameter = 8.0; // [2:0.1:40]
|
|
Countersink_Depth = 1.8; // [0.2:0.1:10]
|
|
|
|
// Magnetlommer skæres fra bagsiden
|
|
Magnet_Diameter = 10.2; // [2:0.1:40]
|
|
Magnet_Depth = 2.2; // [0.2:0.1:20]
|
|
Magnet_Spacing = 70; // [5:1:400]
|
|
|
|
// Nøglehulslommer skæres fra bagsiden
|
|
Keyhole_Head_Diameter = 8.5; // [2:0.1:30]
|
|
Keyhole_Neck_Width = 4.0; // [1:0.1:20]
|
|
Keyhole_Length = 14; // [4:0.5:50]
|
|
Keyhole_Depth = 2.0; // [0.2:0.1:20]
|
|
|
|
// Kabelbinder-slots
|
|
Zip_Slot_Length = 12; // [3:0.5:50]
|
|
Zip_Slot_Width = 3.5; // [1:0.1:20]
|
|
|
|
|
|
/* [Bordskilte / standere] */
|
|
|
|
Desk_Angle = 72; // [45:1:90]
|
|
|
|
Base_Thickness = 4; // [1:0.5:30]
|
|
Base_Depth = 45; // [10:1:150]
|
|
Base_Width_Extra = 16; // [0:1:100]
|
|
Base_Front_Lip = 8; // [0:1:50]
|
|
|
|
// Giver fysisk overlap ved monolitiske standere
|
|
Base_Embed = 1.5; // [0.2:0.1:10]
|
|
|
|
// Slotstander
|
|
Slot_Clearance = 0.30; // [0:0.05:2]
|
|
Slot_Depth = 2.5; // [0.5:0.1:20]
|
|
|
|
// Wedge
|
|
Wedge_Depth = 35; // [5:1:150]
|
|
Wedge_Height_Fraction = 0.65; // [0.2:0.01:0.95]
|
|
Wedge_Width_Fraction = 0.70; // [0.2:0.01:1]
|
|
|
|
// A-frame
|
|
Tent_Angle = 70; // [45:1:85]
|
|
Tent_Top_Bar = true;
|
|
Tent_Top_Bar_Diameter = 5; // [1:0.5:20]
|
|
|
|
|
|
/* [Foldbart plademateriale] */
|
|
|
|
// Foldable Sheet Blank er lavet til fx karton, PP, tynd plast, finer mv.
|
|
Fold_Hinge_Band = 4; // [0:0.5:30]
|
|
Fold_Score_Width = 0.35; // [0.1:0.05:3]
|
|
Fold_Corner_Radius = 4; // [0:0.5:30]
|
|
Fold_Sheet_Thickness = 1.0; // [0.2:0.1:10]
|
|
Fold_Text_On_Both_Panels = true;
|
|
|
|
|
|
/* [Stake sign] */
|
|
|
|
Stake_Width = 18; // [5:1:80]
|
|
Stake_Length = 90; // [20:1:400]
|
|
Stake_Tip_Length = 25; // [5:1:100]
|
|
|
|
|
|
/* [Kvalitet] */
|
|
|
|
Curve_FN = 96; // [12:4:360]
|
|
Text_FN = 32; // [8:2:128]
|
|
Arch_Segments = 48; // [12:2:180]
|
|
|
|
EPS = 0.02;
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
DATA + FONT METRICS
|
|
--------------------------------------------------------------------------- */
|
|
|
|
raw_lines = [
|
|
[Line_1_Text, Line_1_Size, Line_1_Font, Line_1_Spacing, Line_1_X, Line_1_Y],
|
|
[Line_2_Text, Line_2_Size, Line_2_Font, Line_2_Spacing, Line_2_X, Line_2_Y],
|
|
[Line_3_Text, Line_3_Size, Line_3_Font, Line_3_Spacing, Line_3_X, Line_3_Y],
|
|
[Line_4_Text, Line_4_Size, Line_4_Font, Line_4_Spacing, Line_4_X, Line_4_Y],
|
|
[Line_5_Text, Line_5_Size, Line_5_Font, Line_5_Spacing, Line_5_X, Line_5_Y],
|
|
[Line_6_Text, Line_6_Size, Line_6_Font, Line_6_Spacing, Line_6_X, Line_6_Y],
|
|
[Line_7_Text, Line_7_Size, Line_7_Font, Line_7_Spacing, Line_7_X, Line_7_Y],
|
|
[Line_8_Text, Line_8_Size, Line_8_Font, Line_8_Spacing, Line_8_X, Line_8_Y]
|
|
];
|
|
|
|
active_lines = [for (l = raw_lines) if (len(l[0]) > 0) l];
|
|
|
|
function sum_vec(v, i=0) =
|
|
i >= len(v) ? 0 : v[i] + sum_vec(v, i+1);
|
|
|
|
function sum_first(v, count, i=0) =
|
|
i >= count ? 0 : v[i] + sum_first(v, count, i+1);
|
|
|
|
function line_tm(l) =
|
|
Font_Sizing_Mode == "em"
|
|
? textmetrics(
|
|
text=l[0], em=l[1], font=l[2],
|
|
direction=Text_Direction, language=Text_Language, script=Text_Script,
|
|
halign="center", valign="center", spacing=l[3])
|
|
: textmetrics(
|
|
text=l[0], size=l[1], font=l[2],
|
|
direction=Text_Direction, language=Text_Language, script=Text_Script,
|
|
halign="center", valign="center", spacing=l[3]);
|
|
|
|
function line_fm(l) =
|
|
Font_Sizing_Mode == "em"
|
|
? fontmetrics(em=l[1], font=l[2])
|
|
: fontmetrics(size=l[1], font=l[2]);
|
|
|
|
function fallback_width(l) =
|
|
max([0.01, len(l[0]) * l[1] * 0.62 * l[3]]);
|
|
|
|
function fallback_height(l) =
|
|
max([0.01, l[1]]);
|
|
|
|
function line_width(l) =
|
|
Use_Nightly_Metrics ? max([0.01, line_tm(l).size[0]]) : fallback_width(l);
|
|
|
|
function line_height(l) =
|
|
Use_Nightly_Metrics ? max([0.01, line_tm(l).size[1]]) : fallback_height(l);
|
|
|
|
function line_interline(l) =
|
|
Use_Nightly_Metrics
|
|
? max([0.01, line_fm(l).interline])
|
|
: l[1] * 1.22;
|
|
|
|
line_boxes = [
|
|
for (l = active_lines)
|
|
max([line_height(l), line_interline(l) * Leading_Scale])
|
|
];
|
|
|
|
text_block_width =
|
|
len(active_lines) > 0
|
|
? max([for (l = active_lines) line_width(l)])
|
|
: 0.01;
|
|
|
|
text_block_height =
|
|
len(active_lines) > 0
|
|
? sum_vec(line_boxes) + max([0, len(active_lines)-1]) * Line_Gap
|
|
: 0.01;
|
|
|
|
function line_center_y(i) =
|
|
text_block_height/2
|
|
- sum_first(line_boxes, i)
|
|
- i * Line_Gap
|
|
- line_boxes[i]/2;
|
|
|
|
function text_anchor_x() =
|
|
Text_HAlign == "left" ? -text_block_width/2 :
|
|
Text_HAlign == "right" ? text_block_width/2 :
|
|
0;
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
SHAPE MATH
|
|
--------------------------------------------------------------------------- */
|
|
|
|
// Faktor for den nogenlunde rektangulære "safe text area" inde i formen.
|
|
function native_shape_safe(shape) =
|
|
shape == "Circle" ? [0.80, 0.80] :
|
|
shape == "Oval" ? [0.84, 0.78] :
|
|
shape == "Triangle" ? [0.62, 0.54] :
|
|
shape == "Diamond" ? [0.65, 0.65] :
|
|
shape == "Hexagon" ? [0.82, 0.82] :
|
|
shape == "Octagon" ? [0.88, 0.88] :
|
|
shape == "Shield" ? [0.76, 0.74] :
|
|
shape == "Arch" ? [0.88, 0.82] :
|
|
shape == "Arrow Right" ? [0.66, 0.82] :
|
|
shape == "Pennant" ? [0.70, 0.82] :
|
|
[1.0, 1.0];
|
|
|
|
safe_factor = native_shape_safe(Plate_Shape) * Shape_Text_Safety;
|
|
|
|
mount_feature_d =
|
|
Mounting_Style == "Magnet Pair" || Mounting_Style == "Magnet Quad"
|
|
? Magnet_Diameter
|
|
: Mounting_Style == "Keyhole Pair"
|
|
? Keyhole_Head_Diameter
|
|
: max([Hole_Diameter, Countersink_Diameter]);
|
|
|
|
mount_min_w =
|
|
Mounting_Style == "None" || Mounting_Style == "Lanyard Hole"
|
|
? 0
|
|
: 2 * (Mount_Edge + mount_feature_d/2 + 2);
|
|
|
|
mount_min_h =
|
|
Mounting_Style == "None"
|
|
? 0
|
|
: 2 * (Mount_Edge + mount_feature_d/2 + 2);
|
|
|
|
auto_width_raw =
|
|
max([
|
|
Minimum_Width,
|
|
(text_block_width + 2*Padding_X) / max([0.1, safe_factor[0]]),
|
|
mount_min_w
|
|
]);
|
|
|
|
auto_height_raw =
|
|
max([
|
|
Minimum_Height,
|
|
(text_block_height + 2*Padding_Y) / max([0.1, safe_factor[1]]),
|
|
mount_min_h
|
|
]);
|
|
|
|
pre_w = Auto_Size_Plate ? auto_width_raw : Manual_Width;
|
|
pre_h = Auto_Size_Plate ? auto_height_raw : Manual_Height;
|
|
|
|
square_side = max([pre_w, pre_h]);
|
|
|
|
plate_w =
|
|
Plate_Shape == "Square" || Plate_Shape == "Circle"
|
|
? square_side
|
|
: pre_w;
|
|
|
|
plate_h =
|
|
Plate_Shape == "Square" || Plate_Shape == "Circle"
|
|
? square_side
|
|
: pre_h;
|
|
|
|
usable_text_w =
|
|
max([0.1, (plate_w - 2*Padding_X) * safe_factor[0]]);
|
|
|
|
usable_text_h =
|
|
max([0.1, (plate_h - 2*Padding_Y) * safe_factor[1]]);
|
|
|
|
auto_fit_scale =
|
|
Scale_Text_Down_To_Fit
|
|
? min([
|
|
1,
|
|
usable_text_w / max([0.01, text_block_width]),
|
|
usable_text_h / max([0.01, text_block_height])
|
|
])
|
|
: 1;
|
|
|
|
final_text_scale = auto_fit_scale * Text_Global_Scale;
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
VALIDATION + DIAGNOSTICS
|
|
--------------------------------------------------------------------------- */
|
|
|
|
module validate_parameters() {
|
|
assert(plate_w > 0 && plate_h > 0, "Plate dimensions must be positive.");
|
|
assert(Plate_Thickness > 0, "Plate_Thickness must be > 0.");
|
|
assert(Corner_Radius >= 0, "Corner_Radius cannot be negative.");
|
|
assert(Bevel_Size >= 0, "Bevel_Size cannot be negative.");
|
|
assert(final_text_scale > 0, "Text scale must be > 0.");
|
|
|
|
if (Text_Mode == "Engraved")
|
|
assert(Text_Engrave_Depth <= Plate_Thickness + EPS,
|
|
"Text_Engrave_Depth is larger than Plate_Thickness.");
|
|
|
|
if (Text_Mode == "Inlay") {
|
|
assert(Inlay_Depth <= Plate_Thickness + EPS,
|
|
"Inlay_Depth is larger than Plate_Thickness.");
|
|
assert(Inlay_Depth > Inlay_Z_Clearance,
|
|
"Inlay_Z_Clearance must be smaller than Inlay_Depth.");
|
|
}
|
|
|
|
if (Border_Style == "Engraved")
|
|
assert(Border_Engrave_Depth <= Plate_Thickness + EPS,
|
|
"Border_Engrave_Depth is larger than Plate_Thickness.");
|
|
|
|
if (Mounting_Style == "Magnet Pair" || Mounting_Style == "Magnet Quad")
|
|
assert(Magnet_Depth < Plate_Thickness,
|
|
"Magnet_Depth must be smaller than Plate_Thickness.");
|
|
|
|
if (Mounting_Style == "Keyhole Pair")
|
|
assert(Keyhole_Depth < Plate_Thickness,
|
|
"Keyhole_Depth must be smaller than Plate_Thickness.");
|
|
|
|
if (Construction == "Desk Slot Stand")
|
|
assert(Slot_Depth < Base_Thickness,
|
|
"Slot_Depth must be smaller than Base_Thickness.");
|
|
|
|
children();
|
|
}
|
|
|
|
echo("=== Universal Sign Generator / Nightly ===");
|
|
echo("OpenSCAD version", version());
|
|
echo("Use_Nightly_Metrics", Use_Nightly_Metrics);
|
|
echo("Plate shape", Plate_Shape);
|
|
echo("Construction", Construction);
|
|
echo("Plate size", [plate_w, plate_h, Plate_Thickness]);
|
|
echo("Text block raw", [text_block_width, text_block_height]);
|
|
echo("Final text scale", final_text_scale);
|
|
echo("Mounting", Mounting_Style);
|
|
|
|
if (!Use_Nightly_Metrics)
|
|
echo("WARNING: Nightly metrics disabled: text auto-sizing is approximate.");
|
|
|
|
if (final_text_scale < 0.65)
|
|
echo("WARNING: Text has been scaled below 65%. Consider a larger sign or smaller text.");
|
|
|
|
if (Text_Mode == "Raised" && Construction == "Foldable Sheet Blank")
|
|
echo("NOTE: Foldable Sheet Blank is primarily intended for 2D cut/engrave workflows.");
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
2D SHAPES
|
|
--------------------------------------------------------------------------- */
|
|
|
|
module rounded_rect_2d(w, h, r) {
|
|
rr = min([max([0, r]), w/2, h/2]);
|
|
|
|
if (rr <= EPS)
|
|
square([w, h], center=true);
|
|
else
|
|
offset(r=rr, $fn=Curve_FN)
|
|
square([max([EPS, w-2*rr]), max([EPS, h-2*rr])], center=true);
|
|
}
|
|
|
|
module capsule_2d(w, h) {
|
|
if (w >= h) {
|
|
r = h/2;
|
|
hull() {
|
|
translate([-(w-h)/2, 0]) circle(r=r, $fn=Curve_FN);
|
|
translate([ +(w-h)/2, 0]) circle(r=r, $fn=Curve_FN);
|
|
}
|
|
} else {
|
|
r = w/2;
|
|
hull() {
|
|
translate([0, -(h-w)/2]) circle(r=r, $fn=Curve_FN);
|
|
translate([0, +(h-w)/2]) circle(r=r, $fn=Curve_FN);
|
|
}
|
|
}
|
|
}
|
|
|
|
module ellipse_2d(w, h) {
|
|
scale([w/2, h/2])
|
|
circle(r=1, $fn=Curve_FN);
|
|
}
|
|
|
|
module regular_polygon_2d(w, h, n, rot=90) {
|
|
polygon(points=[
|
|
for (i=[0:n-1])
|
|
[w/2*cos(rot + i*360/n), h/2*sin(rot + i*360/n)]
|
|
]);
|
|
}
|
|
|
|
module arch_2d(w, h) {
|
|
r = min([w/2, h * Arch_Radius_Fraction]);
|
|
cy = h/2 - r;
|
|
|
|
pts = concat(
|
|
[[-w/2, -h/2], [w/2, -h/2], [w/2, cy]],
|
|
[for (i=[0:Arch_Segments])
|
|
let(a = i * 180 / Arch_Segments)
|
|
[r*cos(a), cy + r*sin(a)]
|
|
]
|
|
);
|
|
|
|
polygon(points=pts);
|
|
}
|
|
|
|
module shield_2d(w, h) {
|
|
polygon(points=[
|
|
[-w/2, h*0.34],
|
|
[-w*0.34, h/2],
|
|
[ w*0.34, h/2],
|
|
[ w/2, h*0.34],
|
|
[ w*0.43, -h*0.18],
|
|
[ 0, -h/2],
|
|
[-w*0.43, -h*0.18]
|
|
]);
|
|
}
|
|
|
|
module tag_2d(w, h) {
|
|
c = min([Tag_Cut, w/3, h/2]);
|
|
polygon(points=[
|
|
[-w/2+c, -h/2],
|
|
[ w/2-c, -h/2],
|
|
[ w/2, -h/2+c],
|
|
[ w/2, h/2-c],
|
|
[ w/2-c, h/2],
|
|
[-w/2+c, h/2],
|
|
[-w/2, h/2-c],
|
|
[-w/2, -h/2+c]
|
|
]);
|
|
}
|
|
|
|
module arrow_right_2d(w, h) {
|
|
ah = w * Arrow_Head_Fraction;
|
|
polygon(points=[
|
|
[-w/2, -h/2],
|
|
[ w/2-ah, -h/2],
|
|
[ w/2-ah, -h/2],
|
|
[ w/2, 0],
|
|
[ w/2-ah, h/2],
|
|
[-w/2, h/2]
|
|
]);
|
|
}
|
|
|
|
module pennant_2d(w, h) {
|
|
polygon(points=[
|
|
[-w/2, -h/2],
|
|
[ w*0.20, -h/2],
|
|
[ w/2, 0],
|
|
[ w*0.20, h/2],
|
|
[-w/2, h/2]
|
|
]);
|
|
}
|
|
|
|
module face_outline_2d() {
|
|
if (Plate_Shape == "Rectangle")
|
|
square([plate_w, plate_h], center=true);
|
|
|
|
else if (Plate_Shape == "Square")
|
|
square([plate_w, plate_h], center=true);
|
|
|
|
else if (Plate_Shape == "Rounded Rectangle")
|
|
rounded_rect_2d(plate_w, plate_h, Corner_Radius);
|
|
|
|
else if (Plate_Shape == "Capsule")
|
|
capsule_2d(plate_w, plate_h);
|
|
|
|
else if (Plate_Shape == "Circle")
|
|
circle(d=plate_w, $fn=Curve_FN);
|
|
|
|
else if (Plate_Shape == "Oval")
|
|
ellipse_2d(plate_w, plate_h);
|
|
|
|
else if (Plate_Shape == "Triangle")
|
|
polygon(points=[
|
|
[-plate_w/2, -plate_h/2],
|
|
[ plate_w/2, -plate_h/2],
|
|
[0, plate_h/2]
|
|
]);
|
|
|
|
else if (Plate_Shape == "Diamond")
|
|
polygon(points=[
|
|
[0, plate_h/2],
|
|
[plate_w/2, 0],
|
|
[0, -plate_h/2],
|
|
[-plate_w/2, 0]
|
|
]);
|
|
|
|
else if (Plate_Shape == "Hexagon")
|
|
regular_polygon_2d(plate_w, plate_h, 6, 0);
|
|
|
|
else if (Plate_Shape == "Octagon")
|
|
regular_polygon_2d(plate_w, plate_h, 8, 22.5);
|
|
|
|
else if (Plate_Shape == "Shield")
|
|
shield_2d(plate_w, plate_h);
|
|
|
|
else if (Plate_Shape == "Arch")
|
|
arch_2d(plate_w, plate_h);
|
|
|
|
else if (Plate_Shape == "Tag")
|
|
tag_2d(plate_w, plate_h);
|
|
|
|
else if (Plate_Shape == "Arrow Right")
|
|
arrow_right_2d(plate_w, plate_h);
|
|
|
|
else if (Plate_Shape == "Pennant")
|
|
pennant_2d(plate_w, plate_h);
|
|
|
|
else
|
|
rounded_rect_2d(plate_w, plate_h, Corner_Radius);
|
|
}
|
|
|
|
module inset_outline_2d(d) {
|
|
offset(delta=-d, $fn=Curve_FN)
|
|
face_outline_2d();
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
TEXT GEOMETRY
|
|
--------------------------------------------------------------------------- */
|
|
|
|
module one_line_text_2d(l) {
|
|
if (Font_Sizing_Mode == "em")
|
|
text(
|
|
text=l[0],
|
|
em=l[1],
|
|
font=l[2],
|
|
direction=Text_Direction,
|
|
language=Text_Language,
|
|
script=Text_Script,
|
|
halign=Text_HAlign,
|
|
valign="center",
|
|
spacing=l[3],
|
|
$fn=Text_FN
|
|
);
|
|
else
|
|
text(
|
|
text=l[0],
|
|
size=l[1],
|
|
font=l[2],
|
|
direction=Text_Direction,
|
|
language=Text_Language,
|
|
script=Text_Script,
|
|
halign=Text_HAlign,
|
|
valign="center",
|
|
spacing=l[3],
|
|
$fn=Text_FN
|
|
);
|
|
}
|
|
|
|
module text_block_2d() {
|
|
translate([Text_Offset_X, Text_Offset_Y])
|
|
scale([final_text_scale, final_text_scale])
|
|
if (len(active_lines) > 0)
|
|
for (i=[0:len(active_lines)-1]) {
|
|
l = active_lines[i];
|
|
|
|
translate([
|
|
text_anchor_x() + l[4],
|
|
line_center_y(i) + l[5]
|
|
])
|
|
one_line_text_2d(l);
|
|
}
|
|
}
|
|
|
|
module text_recess_shape_2d() {
|
|
if (Text_Mode == "Inlay")
|
|
offset(delta=Inlay_XY_Clearance, $fn=Curve_FN)
|
|
text_block_2d();
|
|
else
|
|
text_block_2d();
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
BORDER
|
|
--------------------------------------------------------------------------- */
|
|
|
|
module border_ring_2d() {
|
|
if (Border_Width > EPS)
|
|
difference() {
|
|
inset_outline_2d(Border_Inset);
|
|
inset_outline_2d(Border_Inset + Border_Width);
|
|
}
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
MOUNTING - 2D CUT SHAPES
|
|
--------------------------------------------------------------------------- */
|
|
|
|
function x_edge_pos() = max([0, plate_w/2 - Mount_Edge]);
|
|
function y_edge_pos() = max([0, plate_h/2 - Mount_Edge]);
|
|
|
|
module round_hole_2d(x, y, d=Hole_Diameter) {
|
|
translate([x,y])
|
|
circle(d=d, $fn=Curve_FN);
|
|
}
|
|
|
|
module slot_2d(x, y, length, width, vertical=true) {
|
|
translate([x,y])
|
|
hull() {
|
|
if (vertical) {
|
|
translate([0, -(length-width)/2]) circle(d=width, $fn=Curve_FN);
|
|
translate([0, +(length-width)/2]) circle(d=width, $fn=Curve_FN);
|
|
} else {
|
|
translate([-(length-width)/2, 0]) circle(d=width, $fn=Curve_FN);
|
|
translate([+(length-width)/2, 0]) circle(d=width, $fn=Curve_FN);
|
|
}
|
|
}
|
|
}
|
|
|
|
module through_mounts_2d() {
|
|
xe = x_edge_pos();
|
|
ye = y_edge_pos();
|
|
|
|
if (Mounting_Style == "2 Hanging Holes") {
|
|
round_hole_2d(-xe, ye);
|
|
round_hole_2d( xe, ye);
|
|
}
|
|
|
|
else if (Mounting_Style == "2 Side Holes") {
|
|
round_hole_2d(-xe, 0);
|
|
round_hole_2d( xe, 0);
|
|
}
|
|
|
|
else if (Mounting_Style == "4 Corner Holes" ||
|
|
Mounting_Style == "4 Countersunk") {
|
|
for (x=[-xe,xe], y=[-ye,ye])
|
|
round_hole_2d(x,y);
|
|
}
|
|
|
|
else if (Mounting_Style == "2 Countersunk") {
|
|
round_hole_2d(-xe, ye);
|
|
round_hole_2d( xe, ye);
|
|
}
|
|
|
|
else if (Mounting_Style == "Lanyard Hole") {
|
|
round_hole_2d(0, ye);
|
|
}
|
|
|
|
else if (Mounting_Style == "Zip-tie Slots") {
|
|
slot_2d(-xe, 0, Zip_Slot_Length, Zip_Slot_Width, true);
|
|
slot_2d( xe, 0, Zip_Slot_Length, Zip_Slot_Width, true);
|
|
}
|
|
}
|
|
|
|
module keyhole_2d() {
|
|
// Stort indføringshul nederst, smal kanal opad.
|
|
union() {
|
|
translate([0, -Keyhole_Length/2 + Keyhole_Head_Diameter/2])
|
|
circle(d=Keyhole_Head_Diameter, $fn=Curve_FN);
|
|
|
|
translate([0, Keyhole_Length/4])
|
|
square([Keyhole_Neck_Width, Keyhole_Length], center=true);
|
|
}
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
3D BASE PLATE
|
|
--------------------------------------------------------------------------- */
|
|
|
|
module plate_solid_3d() {
|
|
b = min([
|
|
max([0, Bevel_Size]),
|
|
max([0, Plate_Thickness/2 - EPS])
|
|
]);
|
|
|
|
if (Edge_Profile == "Straight" || b <= EPS) {
|
|
linear_extrude(height=Plate_Thickness)
|
|
face_outline_2d();
|
|
} else {
|
|
// Chamfer begge sider uden Minkowski: hurtigere og robust på mange former.
|
|
union() {
|
|
hull() {
|
|
translate([0,0,0])
|
|
linear_extrude(height=EPS)
|
|
inset_outline_2d(b);
|
|
|
|
translate([0,0,b])
|
|
linear_extrude(height=EPS)
|
|
face_outline_2d();
|
|
}
|
|
|
|
if (Plate_Thickness > 2*b)
|
|
translate([0,0,b])
|
|
linear_extrude(height=Plate_Thickness-2*b)
|
|
face_outline_2d();
|
|
|
|
hull() {
|
|
translate([0,0,Plate_Thickness-b])
|
|
linear_extrude(height=EPS)
|
|
face_outline_2d();
|
|
|
|
translate([0,0,Plate_Thickness-EPS])
|
|
linear_extrude(height=EPS)
|
|
inset_outline_2d(b);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
module through_mounts_3d() {
|
|
translate([0,0,-EPS])
|
|
linear_extrude(height=Plate_Thickness + 2*EPS)
|
|
through_mounts_2d();
|
|
}
|
|
|
|
module countersinks_3d() {
|
|
xe = x_edge_pos();
|
|
ye = y_edge_pos();
|
|
|
|
positions =
|
|
Mounting_Style == "2 Countersunk"
|
|
? [[-xe,ye],[xe,ye]]
|
|
: Mounting_Style == "4 Countersunk"
|
|
? [[-xe,-ye],[-xe,ye],[xe,-ye],[xe,ye]]
|
|
: [];
|
|
|
|
if (len(positions) > 0)
|
|
for (p = positions)
|
|
translate([p[0], p[1], Plate_Thickness-Countersink_Depth])
|
|
cylinder(
|
|
h=Countersink_Depth + EPS,
|
|
d1=Hole_Diameter,
|
|
d2=Countersink_Diameter,
|
|
$fn=Curve_FN
|
|
);
|
|
}
|
|
|
|
module backside_mount_recesses_3d() {
|
|
xe = x_edge_pos();
|
|
ye = y_edge_pos();
|
|
|
|
if (Mounting_Style == "Magnet Pair") {
|
|
mx = min([Magnet_Spacing/2, xe]);
|
|
|
|
for (x=[-mx,mx])
|
|
translate([x,0,-EPS])
|
|
cylinder(h=Magnet_Depth+EPS, d=Magnet_Diameter, $fn=Curve_FN);
|
|
}
|
|
|
|
else if (Mounting_Style == "Magnet Quad") {
|
|
for (x=[-xe,xe], y=[-ye,ye])
|
|
translate([x,y,-EPS])
|
|
cylinder(h=Magnet_Depth+EPS, d=Magnet_Diameter, $fn=Curve_FN);
|
|
}
|
|
|
|
else if (Mounting_Style == "Keyhole Pair") {
|
|
for (x=[-xe,xe])
|
|
translate([x, ye - Keyhole_Length/2, -EPS])
|
|
linear_extrude(height=Keyhole_Depth+EPS)
|
|
keyhole_2d();
|
|
}
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
FACE: BASE + SUBTRACTIONS + RELIEF
|
|
--------------------------------------------------------------------------- */
|
|
|
|
module face_core_3d() {
|
|
difference() {
|
|
plate_solid_3d();
|
|
|
|
// Gennemgående monteringshuller / slots
|
|
through_mounts_3d();
|
|
|
|
// Forsænkning på forsiden
|
|
countersinks_3d();
|
|
|
|
// Magneter / keyholes fra bagsiden
|
|
backside_mount_recesses_3d();
|
|
|
|
// Tekst
|
|
if (Text_Mode == "Engraved")
|
|
translate([0,0,Plate_Thickness-Text_Engrave_Depth])
|
|
linear_extrude(height=Text_Engrave_Depth+EPS)
|
|
text_block_2d();
|
|
|
|
else if (Text_Mode == "Cut-through")
|
|
translate([0,0,-EPS])
|
|
linear_extrude(height=Plate_Thickness+2*EPS)
|
|
text_block_2d();
|
|
|
|
else if (Text_Mode == "Inlay")
|
|
translate([0,0,Plate_Thickness-Inlay_Depth])
|
|
linear_extrude(height=Inlay_Depth+EPS)
|
|
text_recess_shape_2d();
|
|
|
|
// Border
|
|
if (Border_Style == "Engraved")
|
|
translate([0,0,Plate_Thickness-Border_Engrave_Depth])
|
|
linear_extrude(height=Border_Engrave_Depth+EPS)
|
|
border_ring_2d();
|
|
|
|
else if (Border_Style == "Cut-through")
|
|
translate([0,0,-EPS])
|
|
linear_extrude(height=Plate_Thickness+2*EPS)
|
|
border_ring_2d();
|
|
}
|
|
}
|
|
|
|
module face_relief_3d() {
|
|
if (Text_Mode == "Raised")
|
|
translate([0,0,Plate_Thickness-EPS])
|
|
linear_extrude(height=Text_Raised_Height+EPS)
|
|
text_block_2d();
|
|
|
|
if (Border_Style == "Raised")
|
|
translate([0,0,Plate_Thickness-EPS])
|
|
linear_extrude(height=Border_Raised_Height+EPS)
|
|
border_ring_2d();
|
|
}
|
|
|
|
module sign_face_3d() {
|
|
union() {
|
|
face_core_3d();
|
|
face_relief_3d();
|
|
}
|
|
}
|
|
|
|
module inlay_insert_3d(on_face=false) {
|
|
insert_h = max([EPS, Inlay_Depth-Inlay_Z_Clearance]);
|
|
|
|
z0 =
|
|
on_face
|
|
? Plate_Thickness-Inlay_Depth+Inlay_Z_Clearance
|
|
: 0;
|
|
|
|
translate([0,0,z0])
|
|
linear_extrude(height=insert_h)
|
|
text_block_2d();
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
DESK / SUPPORT GEOMETRY
|
|
--------------------------------------------------------------------------- */
|
|
|
|
module desk_base_3d(with_slot=false) {
|
|
bw = plate_w + Base_Width_Extra;
|
|
|
|
difference() {
|
|
translate([0, (Base_Depth-Base_Front_Lip)/2, Base_Thickness/2])
|
|
cube([bw, Base_Depth+Base_Front_Lip, Base_Thickness], center=true);
|
|
|
|
if (with_slot)
|
|
translate([
|
|
0,
|
|
0,
|
|
Base_Thickness - Slot_Depth/2 + EPS
|
|
])
|
|
cube([
|
|
plate_w + 2*Slot_Clearance,
|
|
Plate_Thickness + 2*Slot_Clearance,
|
|
Slot_Depth + 2*EPS
|
|
], center=true);
|
|
}
|
|
}
|
|
|
|
// Sign_face er modelleret i XY. Denne transform drejer bundkanten ned på bordet.
|
|
module posed_face(angle=90, y0=0, z0=0) {
|
|
translate([0,y0,z0])
|
|
rotate([angle,0,0])
|
|
translate([0,plate_h/2,0])
|
|
children();
|
|
}
|
|
|
|
// Ekstrudér en polygon i YZ langs X.
|
|
module prism_x(width, yz_points) {
|
|
rotate([0,90,0])
|
|
linear_extrude(height=width, center=true, convexity=10)
|
|
polygon(points=[
|
|
for (p=yz_points) [-p[1], p[0]]
|
|
]);
|
|
}
|
|
|
|
module l_stand_support_3d() {
|
|
desk_base_3d(false);
|
|
}
|
|
|
|
module desk_l_stand_3d(include_face=true, include_support=true) {
|
|
if (include_support)
|
|
l_stand_support_3d();
|
|
|
|
if (include_face)
|
|
posed_face(90, 0, Base_Thickness-Base_Embed)
|
|
sign_face_3d();
|
|
}
|
|
|
|
module wedge_support_3d() {
|
|
support_w = plate_w * Wedge_Width_Fraction;
|
|
rise_len = plate_h * Wedge_Height_Fraction;
|
|
tip_y = rise_len * cos(Desk_Angle);
|
|
tip_z = Base_Thickness + rise_len * sin(Desk_Angle) - Base_Embed;
|
|
|
|
desk_base_3d(false);
|
|
|
|
prism_x(
|
|
support_w,
|
|
[
|
|
[0, Base_Thickness-EPS],
|
|
[Wedge_Depth, Base_Thickness-EPS],
|
|
[tip_y, tip_z]
|
|
]
|
|
);
|
|
}
|
|
|
|
module desk_wedge_3d(include_face=true, include_support=true) {
|
|
if (include_support)
|
|
wedge_support_3d();
|
|
|
|
if (include_face)
|
|
posed_face(Desk_Angle, 0, Base_Thickness-Base_Embed)
|
|
sign_face_3d();
|
|
}
|
|
|
|
module slot_support_3d() {
|
|
desk_base_3d(true);
|
|
}
|
|
|
|
module desk_slot_stand_3d(include_face=true, include_support=true) {
|
|
if (include_support)
|
|
slot_support_3d();
|
|
|
|
if (include_face)
|
|
posed_face(
|
|
90,
|
|
0,
|
|
Base_Thickness-Slot_Depth
|
|
)
|
|
sign_face_3d();
|
|
}
|
|
|
|
module tent_face_pair_3d() {
|
|
a = Tent_Angle;
|
|
half_spread = plate_h * cos(a);
|
|
top_z = plate_h * sin(a);
|
|
|
|
// Forside
|
|
posed_face(a, -half_spread, 0)
|
|
sign_face_3d();
|
|
|
|
// Bagside: spejling over Y giver læsbar tekst fra modsatte side.
|
|
mirror([0,1,0])
|
|
posed_face(a, -half_spread, 0)
|
|
sign_face_3d();
|
|
|
|
if (Tent_Top_Bar)
|
|
translate([0,0,top_z])
|
|
rotate([0,90,0])
|
|
cylinder(
|
|
h=plate_w,
|
|
d=Tent_Top_Bar_Diameter,
|
|
center=true,
|
|
$fn=Curve_FN
|
|
);
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
FOLDABLE SHEET BLANK
|
|
--------------------------------------------------------------------------- */
|
|
|
|
fold_blank_h = 2*plate_h + Fold_Hinge_Band;
|
|
|
|
module fold_blank_outline_2d() {
|
|
rounded_rect_2d(
|
|
plate_w,
|
|
fold_blank_h,
|
|
min([Fold_Corner_Radius, plate_w/2, fold_blank_h/2])
|
|
);
|
|
}
|
|
|
|
module fold_score_2d() {
|
|
// To fold-linjer skaber en lille hinge-band. Ved band=0 bliver de samme linje.
|
|
y1 = -Fold_Hinge_Band/2;
|
|
y2 = Fold_Hinge_Band/2;
|
|
|
|
translate([0,y1])
|
|
square([plate_w-2*Fold_Corner_Radius, Fold_Score_Width], center=true);
|
|
|
|
if (Fold_Hinge_Band > EPS)
|
|
translate([0,y2])
|
|
square([plate_w-2*Fold_Corner_Radius, Fold_Score_Width], center=true);
|
|
}
|
|
|
|
module fold_text_2d() {
|
|
panel_offset = (plate_h + Fold_Hinge_Band)/2;
|
|
|
|
translate([0, panel_offset])
|
|
text_block_2d();
|
|
|
|
if (Fold_Text_On_Both_Panels)
|
|
translate([0,-panel_offset])
|
|
rotate(180)
|
|
text_block_2d();
|
|
}
|
|
|
|
module fold_blank_3d() {
|
|
linear_extrude(height=Fold_Sheet_Thickness)
|
|
difference() {
|
|
fold_blank_outline_2d();
|
|
|
|
if (Text_Mode == "Cut-through")
|
|
fold_text_2d();
|
|
}
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
STAKE SIGN
|
|
--------------------------------------------------------------------------- */
|
|
|
|
module stake_2d() {
|
|
y_top = -plate_h/2 + EPS;
|
|
y_shoulder = -plate_h/2 - (Stake_Length-Stake_Tip_Length);
|
|
y_tip = -plate_h/2 - Stake_Length;
|
|
|
|
polygon(points=[
|
|
[-Stake_Width/2, y_top],
|
|
[ Stake_Width/2, y_top],
|
|
[ Stake_Width/2, y_shoulder],
|
|
[0, y_tip],
|
|
[-Stake_Width/2, y_shoulder]
|
|
]);
|
|
}
|
|
|
|
module stake_body_3d() {
|
|
union() {
|
|
sign_face_3d();
|
|
|
|
linear_extrude(height=Plate_Thickness)
|
|
stake_2d();
|
|
}
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
2D OUTPUTS
|
|
--------------------------------------------------------------------------- */
|
|
|
|
module face_cut_outline_2d() {
|
|
difference() {
|
|
face_outline_2d();
|
|
|
|
// Kun features der faktisk går igennem pladen.
|
|
through_mounts_2d();
|
|
|
|
if (Text_Mode == "Cut-through")
|
|
text_block_2d();
|
|
|
|
if (Border_Style == "Cut-through")
|
|
border_ring_2d();
|
|
}
|
|
}
|
|
|
|
module stake_cut_outline_2d() {
|
|
difference() {
|
|
union() {
|
|
face_outline_2d();
|
|
stake_2d();
|
|
}
|
|
|
|
through_mounts_2d();
|
|
|
|
if (Text_Mode == "Cut-through")
|
|
text_block_2d();
|
|
|
|
if (Border_Style == "Cut-through")
|
|
border_ring_2d();
|
|
}
|
|
}
|
|
|
|
module output_2d_cut() {
|
|
if (Construction == "Foldable Sheet Blank")
|
|
difference() {
|
|
fold_blank_outline_2d();
|
|
if (Text_Mode == "Cut-through")
|
|
fold_text_2d();
|
|
}
|
|
|
|
else if (Construction == "Stake Sign")
|
|
stake_cut_outline_2d();
|
|
|
|
else
|
|
face_cut_outline_2d();
|
|
}
|
|
|
|
module output_2d_text() {
|
|
if (Construction == "Foldable Sheet Blank")
|
|
fold_text_2d();
|
|
else
|
|
text_block_2d();
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
3D CONSTRUCTION DISPATCH
|
|
--------------------------------------------------------------------------- */
|
|
|
|
module output_3d() {
|
|
want_face =
|
|
Part_Selection == "Assembly" ||
|
|
Part_Selection == "Face only";
|
|
|
|
want_support =
|
|
Part_Selection == "Assembly" ||
|
|
Part_Selection == "Support only";
|
|
|
|
if (Part_Selection == "Text insert only") {
|
|
inlay_insert_3d(false);
|
|
}
|
|
|
|
else if (Construction == "Flat Plate") {
|
|
if (want_face)
|
|
if (Text_Mode == "Inlay" && Part_Selection == "Assembly") {
|
|
sign_face_3d();
|
|
inlay_insert_3d(true);
|
|
} else {
|
|
sign_face_3d();
|
|
}
|
|
}
|
|
|
|
else if (Construction == "Desk L-Stand") {
|
|
desk_l_stand_3d(want_face, want_support);
|
|
}
|
|
|
|
else if (Construction == "Desk Wedge") {
|
|
desk_wedge_3d(want_face, want_support);
|
|
}
|
|
|
|
else if (Construction == "Desk Slot Stand") {
|
|
desk_slot_stand_3d(want_face, want_support);
|
|
}
|
|
|
|
else if (Construction == "A-Frame Tent") {
|
|
if (Part_Selection == "Support only") {
|
|
if (Tent_Top_Bar) {
|
|
top_z = plate_h * sin(Tent_Angle);
|
|
|
|
translate([0,0,top_z])
|
|
rotate([0,90,0])
|
|
cylinder(
|
|
h=plate_w,
|
|
d=Tent_Top_Bar_Diameter,
|
|
center=true,
|
|
$fn=Curve_FN
|
|
);
|
|
}
|
|
} else {
|
|
tent_face_pair_3d();
|
|
}
|
|
}
|
|
|
|
else if (Construction == "Foldable Sheet Blank") {
|
|
if (want_face)
|
|
fold_blank_3d();
|
|
}
|
|
|
|
else if (Construction == "Stake Sign") {
|
|
if (want_face)
|
|
stake_body_3d();
|
|
}
|
|
|
|
else {
|
|
sign_face_3d();
|
|
}
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
MAIN
|
|
--------------------------------------------------------------------------- */
|
|
|
|
validate_parameters() {
|
|
if (Output_Mode == "3D")
|
|
output_3d();
|
|
|
|
else if (Output_Mode == "2D Cut Outline")
|
|
output_2d_cut();
|
|
|
|
else if (Output_Mode == "2D Engrave Text")
|
|
output_2d_text();
|
|
|
|
else if (Output_Mode == "2D Text/Inlay")
|
|
output_2d_text();
|
|
|
|
else if (Output_Mode == "2D Border")
|
|
border_ring_2d();
|
|
|
|
else if (Output_Mode == "2D Fold Score")
|
|
if (Construction == "Foldable Sheet Blank")
|
|
fold_score_2d();
|
|
|
|
else
|
|
output_3d();
|
|
} |