> Canonical HTML: https://kerfscript.mylasertools.com/models/xenospora/

# Xenospora seed pod

Xenospora: a seed pod from another planet. Twelve flat crescent fins stand around a crossed core between a base and a crown. The fins only look twisted: each one is flat, set at a tangent to the circle.

This design has 16 parts on 3 sheets. Customize it in the [playground](https://kerfscript.mylasertools.com/playground/?model=xenospora).

## Downloads

- [KerfScript source](https://kerfscript.mylasertools.com/models/xenospora/xenospora.kerf)
- [xenospora-sheet-1.svg](https://kerfscript.mylasertools.com/models/xenospora/xenospora-sheet-1.svg)
- [xenospora-sheet-1.dxf](https://kerfscript.mylasertools.com/models/xenospora/xenospora-sheet-1.dxf)
- [xenospora-sheet-2.svg](https://kerfscript.mylasertools.com/models/xenospora/xenospora-sheet-2.svg)
- [xenospora-sheet-2.dxf](https://kerfscript.mylasertools.com/models/xenospora/xenospora-sheet-2.dxf)
- [xenospora-sheet-3.svg](https://kerfscript.mylasertools.com/models/xenospora/xenospora-sheet-3.svg)
- [xenospora-sheet-3.dxf](https://kerfscript.mylasertools.com/models/xenospora/xenospora-sheet-3.dxf)

## Parts

| Part | Quantity | Size |
| --- | ---: | --- |
| Fin 1 | 1 | 85.1 × 226 mm |
| Fin 2 | 1 | 85.1 × 226 mm |
| Fin 3 | 1 | 85.1 × 226 mm |
| Fin 4 | 1 | 85.1 × 226 mm |
| Fin 5 | 1 | 85.1 × 226 mm |
| Fin 6 | 1 | 85.1 × 226 mm |
| Fin 7 | 1 | 85.1 × 226 mm |
| Fin 8 | 1 | 85.1 × 226 mm |
| Fin 9 | 1 | 85.1 × 226 mm |
| Fin 10 | 1 | 85.1 × 226 mm |
| Fin 11 | 1 | 85.1 × 226 mm |
| Fin 12 | 1 | 85.1 × 226 mm |
| Root | 1 | 120 × 120 mm |
| Crown | 1 | 86 × 86 mm |
| Core A | 1 | 42 × 226 mm |
| Core B | 1 | 42 × 226 mm |

## Settings

| Name | Default | Range or choices |
| --- | --- | --- |
| `height` | `220` | 210 to 250 |
| `fullness` | `60` | 54 to 66 |
| `output` | `"all"` | All parts, Fins 1 to 4, Fins 5 to 8, Fins 9 to 12, Base crown and core, Fit test |
| `score_detail` | `true` | on or off |
| `$thickness` | `3` | 2.8 to 3.2 |
| `clearance` | `0.15` | 0.1 to 0.3 |
| `$kerf` | `0.15` | 0 to 0.25 |

## Source

```kerf
// Xenospora: a seed pod from another planet. Twelve flat crescent fins stand
// around a crossed core between a base and a crown. The fins only look
// twisted: each one is flat, set at a tangent to the circle.

/* [Specimen] */
// Height between base and crown
height = 220; // [210:5:250]
// How far the crescents bow out
fullness = 60; // [54:2:66]
// All parts, one group at a time, or two pieces to test the slot fit
output = "all"; // [all:All parts, fins_a:Fins 1 to 4, fins_b:Fins 5 to 8, fins_c:Fins 9 to 12, structure:Base crown and core, fit:Fit test]
// Scored veins and assembly numbers
score_detail = true;

/* [Material] */
// Sheet thickness
$thickness = 3; // [2.8:0.1:3.2]
// Added to the slot width
clearance = 0.15; // [0.1:0.05:0.3]
// Beam width
$kerf = 0.15; // [0:0.01:0.25]

/* [Hidden] */
$material = "plywood";
$sheet = [600, 400];
$gap = 6;
$fn = 144;
t = $thickness;
s = height / 220;
R = 30 * s;
sw = t + clearance;
fin_tab = 8;
core_tab = 12;

// Centre line and width of a fin, and width of the core, at height v (0 to 1)
function cc(v) = s * (6 + 12 * v + fullness * pow(sin(180 * v), 1.3));
function ww(v) = s * (12 + 14 * pow(sin(180 * v), 1.2));
function cp(v) = [cc(v), height * v];
function polar(r, a) = [r * cos(a), r * sin(a)];
function core_w(v) = s * (8 + 13 * pow(sin(180 * clamp((v - 0.18) / 0.64, 0, 1)), 1.4));

module fin_blank() polygon(concat(
  [for (j = [0:160]) let (v = j / 160) [cc(v) - ww(v) / 2, height * v]],
  [for (j = [160:-1:0]) let (v = j / 160) [cc(v) + ww(v) / 2, height * v]]
));

function pore_normal(v) = let (
  dx = s * (12 + fullness * 1.3 * PI * pow(sin(180 * v), 0.3) * cos(180 * v)),
  dy = height,
  n = sqrt(dx * dx + dy * dy)
) [-dy / n, dx / n];

// A slot that follows the centre line of the fin from a to b, with round ends
module pore(a, b, w) {
  r = w * s / 2;
  na = pore_normal(a);
  nb = pore_normal(b);
  aa = atan2(na[1], na[0]);
  ab = atan2(nb[1], nb[0]);
  polygon(concat(
    [for (j = [0:72]) let (v = a + (b - a) * j / 72) cp(v) + pore_normal(v) * r],
    [for (j = [1:36]) cp(b) + polar(r, ab - j * 180 / 36)],
    [for (j = [1:72]) let (v = b - (b - a) * j / 72) cp(v) - pore_normal(v) * r],
    [for (j = [1:36]) cp(a) + polar(r, aa - 180 - j * 180 / 36)]
  ));
}

module fin(i) {
  difference() {
    union() {
      fin_blank();
      translate([6 * s - fin_tab / 2, -t]) rect([fin_tab, t + 0.3]);
      translate([18 * s - fin_tab / 2, height - 0.3]) rect([fin_tab, t + 0.3]);
    }
    pore(0.16, 0.31, 4);
    pore(0.37, 0.58, 6);
    pore(0.64, 0.82, 4);
  }
  if (score_detail) score() {
    for (side = [-1, 1]) polyline([for (j = [6:154]) let (v = j / 160) [cc(v) + side * (ww(v) / 2 - 2.3 * s), height * v]]);
    for (v = [0.2:0.06:0.8])
      line([cc(v) + 4 * s, height * v], [cc(v + 0.014) + ww(v + 0.014) / 2 - 2.3 * s, height * (v + 0.014)]);
    translate([cc(0.055), height * 0.055]) text(str(i), size = 3.5 * s, halign = "center", valign = "center");
    translate([cc(0.95), height * 0.95]) text(str(i), size = 3 * s, halign = "center", valign = "center");
  }
}

module cross_socket() {
  rect([core_tab + clearance, sw], center = true);
  rect([sw, core_tab + clearance], center = true);
}

module sockets(top = false) {
  for (i = [0:11]) rotate(i * 30) translate([R, top ? 18 * s : 6 * s]) rect([sw, fin_tab + clearance], center = true);
}

// The base (root) and the crown
module disc(top = false) {
  difference() {
    circle(r = (top ? 43 : 60) * s);
    sockets(top);
    cross_socket();
  }
  if (score_detail) score() {
    outline() circle(r = (top ? 40 : 57) * s);
    for (i = [0:11]) rotate(i * 30) {
      translate([top ? 33 * s : 40 * s, top ? 18 * s : 6 * s]) text(str(i + 1), size = 2.8 * s, halign = "center", valign = "center");
      line([R - 5 * s, top ? 18 * s : 6 * s], [R - 8 * s, top ? 18 * s : 6 * s]);
    }
    translate([0, -15 * s]) text(top ? "CROWN" : "ROOT", size = 3 * s, halign = "center", valign = "center");
    if (!top) {
      translate([0, -48 * s]) text("X E N O S P O R A", size = 4 * s, halign = "center", valign = "center");
      translate([0, -54 * s]) text("SPECIMEN 03 / EXOBOTANICA", size = 2.3 * s, halign = "center", valign = "center");
      // Core A lines up with this mark
      line([-18 * s, 0], [-10 * s, 0]);
      translate([-21 * s, 0]) text("A", size = 3 * s, halign = "center", valign = "center");
    }
  }
}

module core_blank() polygon(concat(
  [for (j = [0:160]) let (v = j / 160) [-core_w(v), height * v]],
  [for (j = [160:-1:0]) let (v = j / 160) [core_w(v), height * v]]
));

// Two core halves that cross: A is slotted from the top, B from the bottom
module core(a = true) {
  difference() {
    union() {
      core_blank();
      translate([-core_tab / 2, -t]) rect([core_tab, t + 0.3]);
      translate([-core_tab / 2, height - 0.3]) rect([core_tab, t + 0.3]);
    }
    translate([-sw / 2, a ? height / 2 : -t - 1]) rect([sw, height / 2 + t + 1]);
    for (side = [-1, 1], v = [0.36, 0.48, 0.60])
      translate([side * (core_w(v) - 6 * s), height * v]) ellipse([2.2 * s, 5 * s]);
  }
  if (score_detail) score() {
    for (side = [-1, 1]) polyline([for (j = [35:125]) let (v = j / 160) [side * (core_w(v) - 2 * s), height * v]]);
    translate([-4.6 * s, height * 0.1]) text(a ? "A" : "B", size = 3 * s, halign = "center", valign = "center");
    translate([-4.6 * s, height * 0.9]) text(a ? "A" : "B", size = 3 * s, halign = "center", valign = "center");
    for (side = [-1, 1], v = [0.29:0.06:0.71])
      line([side * 3 * s, height * v], [side * (core_w(v + 0.025) - 2 * s), height * (v + 0.025)]);
  }
}

module fit_test() {
  part("Fit test, sockets") {
    difference() {
      rect([66, 32], r = 2);
      translate([17, 16]) rect([sw, fin_tab + clearance], center = true);
      translate([45, 16]) cross_socket();
    }
    if (score_detail) score() {
      translate([17, 5]) text("FIN", size = 3, halign = "center");
      translate([45, 5]) text("CORE", size = 3, halign = "center");
    }
  }
  for (a = [true, false]) part(a ? "Fit test, A" : "Fit test, B") {
    difference() {
      union() {
        rect([24, 32]);
        translate([6, 32]) rect([12, t]);
        translate([8, -t]) rect([8, t + 0.2]);
      }
      translate([12 - sw / 2, a ? 16 : -t - 1]) rect([sw, a ? 20 + t : 17 + t]);
    }
    if (score_detail) score() translate([4, 25]) text(a ? "A" : "B", size = 3);
  }
}

if (output == "fit") fit_test();
// Assembled: the two cores cross in the root, the twelve fins stand round
// them every 30 degrees, and the crown caps them
function fin_at(i) = concat([(R - t / 2) * cos(30 * (i - 1)), (R - t / 2) * sin(30 * (i - 1))], [0]);
module placed_fin(i) step("Set the twelve fins into their numbered sockets, all bowing the same way") part(str("Fin ", i), at = fin_at(i), rotate = [90, 0, 90 + 30 * (i - 1)]) fin(i);
// Steps are numbered in the order they first run, so name them here in build order
step("Cross the two cores and glue them into the root") {}
step("Set the twelve fins into their numbered sockets, all bowing the same way") {}
step("Lower the crown over the top tabs and glue it") {}
if (output == "all" || output == "fins_a") for (i = [1:4]) placed_fin(i);
if (output == "all" || output == "fins_b") for (i = [5:8]) placed_fin(i);
if (output == "all" || output == "fins_c") for (i = [9:12]) placed_fin(i);
if (output == "all" || output == "structure") {
  step("Cross the two cores and glue them into the root")
    part("Root", at = [0, 0, -t]) disc(false);
  step("Lower the crown over the top tabs and glue it")
    part("Crown", at = [0, 0, height]) disc(true);
  step("Cross the two cores and glue them into the root") {
    part("Core A", at = [0, t / 2, 0], rotate = [90, 0, 0]) core(true);
    part("Core B", at = [-t / 2, 0, 0], rotate = [90, 0, 90]) core(false);
  }
}

// Assembly:
// 1. Slide cores A and B together at right angles and glue them into the
//    cross socket in the root, with core A on the scored A mark.
// 2. Set fins 1 to 12 into the numbered sockets, all bowing the same way.
// 3. Lower the crown over the top tabs, numbers matching the root, and glue.
// Paint the core a contrasting colour after cutting if you like, keeping
// the joints bare.
```

Set power, speed and cut order in your laser software. See the [laser software guide](https://kerfscript.mylasertools.com/docs/compatibility/index.md).
