Der untere p5.js-Sketch ermöglicht die schnelle Abschätzung grundlegender Fräsparameter für Hobby- und Labor-CNCs. Anstatt Vorschub, Zustelltiefe und Spindeldrehzahl nur aus Tabellenwerten zu übernehmen, kombiniert das Tool Materialauswahl, Materialdicke, Fräserdurchmesser und Fräser-Typ zu konsistenten Startwerten.

Für ein gewähltes Material (zum Beispiel Birkensperrholz, MDF, Kunststoffe oder Aluminium) und eine angegebene Materialdicke in Millimetern berechnet der Sketch unter anderem Vorschubgeschwindigkeit (Feed), Eintauchgeschwindigkeit (Plunge), Zustelltiefe pro Durchgang (Depth per pass), empfohlene Spindeldrehzahl sowie den Span pro Zahn (Chip Load). Zusätzlich wird zwischen Spiralnut-Aufwärtsfräsern (Upcut) und Fräsern mit gerader Schneide unterschieden, sodass die Hinweise praxisnahe Empfehlungen für typische Laborsituationen liefern. Das Tool ist bewusst konservativ ausgelegt und dient als Ausgangspunkt für eigene Testläufe an Reststücken.

https://ct-lab.info/cnc-helper/

P5.JS

// CNC feed suggestion helper with adjustable bit diameter
// Includes spindle speed and chip load calculation
// Version 1.01 – angepasst auf DeWalt D26200 Drehzahlstufen
 
let materialSelect;
let thicknessInput;
let bitDiameterInput;
let bitTypeSelect;
let calcButton;
let resultDiv;
 
function applyCustomStyles() {
  // Styles werden im HTML gesetzt; hier nur für Konsistenz.
}
 
function setup() {
  applyCustomStyles();
 
  createCanvas(1, 1);
  noLoop();
  select('canvas').style('display', 'none');
 
  const container = select('#calculator-container');
 
  const title = createDiv('CNC Feed & Chip-Load Rechner V. 1.01');
  title.addClass('app-title');
  title.parent(container);
 
  const subtitle = createDiv(
    'Konservative Startwerte für Hobby-CNC-Router mit DeWalt D26200. Vor Serienfertigung immer an Reststücken testen.'
  );
  subtitle.addClass('app-subtitle');
  subtitle.parent(container);
 
  const layout = createDiv();
  layout.addClass('layout');
  layout.parent(container);
 
  const inputPanel = createDiv();
  inputPanel.addClass('panel');
  inputPanel.parent(layout);
 
  const inputTitle = createDiv('Eingaben');
  inputTitle.addClass('panel-title');
  inputTitle.parent(inputPanel);
 
  const matGroup = createDiv();
  matGroup.addClass('field-group');
  matGroup.parent(inputPanel);
 
  const matLabel = createSpan('Material (Material):');
  matLabel.addClass('field-label');
  matLabel.parent(matGroup);
 
  materialSelect = createSelect();
  materialSelect.parent(matGroup);
  materialSelect.option('Birkensperrholz (Birch Plywood)');
  materialSelect.option('Kiefer (Pine)');
  materialSelect.option('Bambus (Bamboo)');
  materialSelect.option('MDF (MDF)');
  materialSelect.option('Kirschsperrholz (Cherry Plywood)');
  materialSelect.option('Harter Ahorn (Hard Maple)');
  materialSelect.option('Bubinga (Bubinga)');
  materialSelect.option('HDPE (HDPE)');
  materialSelect.option('2-Farb-HDPE (2-Color HDPE)');
  materialSelect.option('Geschäumtes PVC (Expanded PVC)');
  materialSelect.option('ABS (ABS)');
  materialSelect.option('Gussacryl (Cast Acrylic)');
  materialSelect.option('2-Farb-Acryl (2-Color Acrylic)');
  materialSelect.option('Corian (Corian)');
  materialSelect.option('Linoleum (Linoleum)');
  materialSelect.option('Leiterplattenmaterial PCB (PCB)');
  materialSelect.option('Aluminium (Aluminum)');
 
  const thickGroup = createDiv();
  thickGroup.addClass('field-group');
  thickGroup.parent(inputPanel);
 
  const thickLabel = createSpan('Materialdicke [mm] (Material thickness):');
  thickLabel.addClass('field-label');
  thickLabel.parent(thickGroup);
 
  thicknessInput = createInput('');
  thicknessInput.attribute('type', 'number');
  thicknessInput.attribute('min', '0.1');
  thicknessInput.attribute('step', '0.1');
  thicknessInput.parent(thickGroup);
 
  const thickHint = createDiv('Typische Platten: 3 / 6 / 9 / 12 / 18 mm.');
  thickHint.addClass('hint');
  thickHint.parent(thickGroup);
 
  const toolRow = createDiv();
  toolRow.addClass('field-row');
  toolRow.parent(inputPanel);
 
  const bitGroup = createDiv();
  bitGroup.addClass('field-group');
  bitGroup.parent(toolRow);
 
  const bitLabel = createSpan('Fräserdurchmesser [mm] (Bit diameter):');
  bitLabel.addClass('field-label');
  bitLabel.parent(bitGroup);
 
  bitDiameterInput = createInput('2.5');
  bitDiameterInput.attribute('type', 'number');
  bitDiameterInput.attribute('min', '0.5');
  bitDiameterInput.attribute('step', '0.1');
  bitDiameterInput.parent(bitGroup);
 
  const typeGroup = createDiv();
  typeGroup.addClass('field-group');
  typeGroup.parent(toolRow);
 
  const typeLabel = createSpan('Fräser-Typ (End mill type):');
  typeLabel.addClass('field-label');
  typeLabel.parent(typeGroup);
 
  bitTypeSelect = createSelect();
  bitTypeSelect.parent(typeGroup);
  bitTypeSelect.option('Spiralnut Aufwärts (Upcut end mill)');
  bitTypeSelect.option('Gerade Schneide (Straight-cut end mill)');
 
  const typeHint = createDiv(
    'Upcut: starker Spanabtransport, mögliche Ausrisse oben. Gerade Schneide: ruhigere Kante, weniger Spanabtransport.'
  );
  typeHint.addClass('hint');
  typeHint.parent(inputPanel);
 
  calcButton = createButton('Vorschläge berechnen (Get suggestions)');
  calcButton.parent(inputPanel);
  calcButton.mousePressed(updateSettings);
 
  const btnHint = createDiv(
    'Alle Werte sind Startpunkte. Bei Geräusch, Vibration oder Brandspuren Parameter anpassen.'
  );
  btnHint.addClass('hint');
  btnHint.parent(inputPanel);
 
  const outputPanel = createDiv();
  outputPanel.addClass('panel');
  outputPanel.parent(container);
 
  const outputTitle = createDiv('Ergebnis');
  outputTitle.addClass('panel-title');
  outputTitle.parent(outputPanel);
 
  resultDiv = createDiv('Noch keine Berechnung durchgeführt.');
  resultDiv.parent(outputPanel);
 
  const licenseDiv = createDiv(
    'Creative Technologies Lab, FH Münster – Lizenz: Dieses Tool steht unter der Creative-Commons-Lizenz ' +
      '<a href="https://creativecommons.org/licenses/by-sa/3.0/" target="_blank" rel="noopener noreferrer">CC BY-SA 3.0</a>. ' +
      'Es darf kopiert, geteilt und angepasst werden, solange die Urheberschaft genannt und abgeleitete Versionen unter denselben Bedingungen weitergegeben werden; ' +
      'zusätzliche Einschränkungen oder abweichende Lizenzbedingungen dürfen nicht hinzugefügt werden.'
  );
  licenseDiv.addClass('license-note');
  licenseDiv.parent(container);
}
 
function updateSettings() {
  const mat = materialSelect.value();
  const t = parseFloat(thicknessInput.value());
  const bitD = parseFloat(bitDiameterInput.value());
  const bitType = bitTypeSelect.value();
 
  if (isNaN(t) || t <= 0) {
    resultDiv.html(
      '<div class="error-message">Bitte eine gültige Materialdicke in Millimetern eingeben.</div>'
    );
    return;
  }
  if (isNaN(bitD) || bitD <= 0) {
    resultDiv.html(
      '<div class="error-message">Bitte einen gültigen Fräserdurchmesser in Millimetern eingeben.</div>'
    );
    return;
  }
 
  const settings = computeSettings(mat, t, bitD, bitType);
  const passes = Math.ceil(t / settings.depthPerPass);
  const chipLoad = settings.feedRate / (settings.spindleRPM * settings.flutes);
 
  const summaryHtml =
    '<div class="summary-row">' +
    `<div class="summary-pill">Feed: ${Math.round(settings.feedRate)} mm/min</div>` +
    `<div class="summary-pill">Plunge: ${Math.round(settings.plungeRate)} mm/min</div>` +
    `<div class="summary-pill">Chip Load: ${roundTo(chipLoad, 4)} mm/Zahn</div>` +
    `<div class="summary-pill">Drehzahl: ${Math.round(settings.spindleRPM)} U/min (Stufe ${settings.dial})</div>` +
    `<div class="summary-pill">Depth/Pass: ${roundTo(settings.depthPerPass, 2)} mm</div>` +
    `<div class="summary-pill">ca. ${passes} Durchgänge</div>` +
    '</div>';
 
  const tableHtml =
    '<table id="result-table">' +
    '<tr>' +
    '<th>Material</th>' +
    '<th>Dicke [mm]</th>' +
    '<th>Ø Bit [mm]</th>' +
    '<th>Bit-Typ</th>' +
    '<th>Drehzahl [U/min] (DeWalt-Stufe)</th>' +
    '<th>Zähne</th>' +
    '<th>Chip Load [mm]</th>' +
    '<th>Feed [mm/min]</th>' +
    '<th>Plunge [mm/min]</th>' +
    '<th>Depth/Pass [mm]</th>' +
    '<th>Plunge-Art</th>' +
    '<th>Hinweise</th>' +
    '</tr>' +
    '<tr>' +
    `<td>${mat}</td>` +
    `<td>${roundTo(t, 1)}</td>` +
    `<td>${roundTo(bitD, 2)}</td>` +
    `<td>${bitType}</td>` +
    `<td>${Math.round(settings.spindleRPM)} (Stufe ${settings.dial})</td>` +
    `<td>${settings.flutes}</td>` +
    `<td>${roundTo(chipLoad, 4)}</td>` +
    `<td>${Math.round(settings.feedRate)}</td>` +
    `<td>${Math.round(settings.plungeRate)}</td>` +
    `<td>${roundTo(settings.depthPerPass, 2)}</td>` +
    `<td>${settings.plungeType}</td>` +
    `<td>${settings.notes}</td>` +
    '</tr>' +
    '</table>';
 
  resultDiv.html(summaryHtml + tableHtml);
}
 
// Zuordnung DeWalt D26200 Drehzahl zu Reglerstufe 1–6
function dewaltDialForRPM(rpm) {
  const table = [
    { dial: 1, rpm: 16000 },
    { dial: 2, rpm: 18200 },
    { dial: 3, rpm: 20400 },
    { dial: 4, rpm: 22600 },
    { dial: 5, rpm: 24800 },
    { dial: 6, rpm: 27000 }
  ];
 
  let best = table[0];
  let bestDiff = Math.abs(rpm - best.rpm);
 
  for (let i = 1; i < table.length; i++) {
    const d = Math.abs(rpm - table[i].rpm);
    if (d < bestDiff) {
      bestDiff = d;
      best = table[i];
    }
  }
  return best.dial;
}
 
// Berechnung der Parameter
function computeSettings(material, thickness, bitDiameter, bitType) {
  let baseFeed;
  let plunge;
  let depthFactor;
  let spindleRPM;
  let flutes;
  let plungeType = 'Rampe 5° (Ramp 5°)';
  let notes;
 
  switch (material) {
    case 'Birkensperrholz (Birch Plywood)':
    case 'Bambus (Bamboo)':
    case 'MDF (MDF)':
    case 'Kirschsperrholz (Cherry Plywood)':
      baseFeed = 1800;
      plunge = 600;
      depthFactor = 0.32;
      spindleRPM = 16000; // DeWalt Stufe 1
      flutes = 2;
      notes =
        'Standard-Holzeinstellung. Empfohlene Fräser: 2-Schneider-Spiralnut, ggf. Downcut oder Kompressionsfräser.';
      break;
    case 'Kiefer (Pine)':
      baseFeed = 1900;
      plunge = 650;
      depthFactor = 0.32;
      spindleRPM = 16000; // weiches Holz, DeWalt Stufe 1 ausreichend
      flutes = 2;
      notes = 'Weiches Nadelholz; etwas höhere Vorschubgeschwindigkeit möglich.';
      break;
    case 'Harter Ahorn (Hard Maple)':
    case 'Bubinga (Bubinga)':
      baseFeed = 1300;
      plunge = 400;
      depthFactor = 0.24;
      spindleRPM = 16000; // konservativ, Stufe 1
      flutes = 2;
      notes =
        'Hartes Holz; reduzierte Zustelltiefe. Empfohlene Fräser: sehr scharfe 2-Schneider-Spiralnut.';
      break;
    case 'HDPE (HDPE)':
    case '2-Farb-HDPE (2-Color HDPE)':
    case 'Geschäumtes PVC (Expanded PVC)':
    case 'ABS (ABS)':
      baseFeed = 2200;
      plunge = 700;
      depthFactor = 0.32;
      spindleRPM = 18000; // nahe DeWalt Stufe 2 (≈18200)
      flutes = 2;
      notes =
        'Weicher Kunststoff; höherer Feed hilft gegen Schmelzen. Empfohlen: polierte Spiralnut, 1–2 Schneiden.';
      break;
    case 'Gussacryl (Cast Acrylic)':
    case '2-Farb-Acryl (2-Color Acrylic)':
      baseFeed = 1200;
      plunge = 400;
      depthFactor = 0.24;
      spindleRPM = 16000; // eher niedrige Drehzahl für Acryl
      flutes = 1;
      notes =
        'Sprödes Acryl; konservative Zustelltiefe. Einzahn-Spiralnut mit polierten Schneiden ist oft vorteilhaft.';
      break;
    case 'Corian (Corian)':
      baseFeed = 1200;
      plunge = 400;
      depthFactor = 0.24;
      spindleRPM = 16000;
      flutes = 2;
      notes = 'Dichter Mineralwerkstoff; ähnlich hartem Holz, gute Absaugung notwendig.';
      break;
    case 'Linoleum (Linoleum)':
      baseFeed = 1900;
      plunge = 650;
      depthFactor = 0.4;
      spindleRPM = 16000;
      flutes = 2;
      notes = 'Weiches Material; größere Zustelltiefe möglich. Trägerschicht beachten.';
      break;
    case 'Leiterplattenmaterial PCB (PCB)':
      baseFeed = 800;
      plunge = 250;
      depthFactor = 0.12;
      spindleRPM = 18000; // kleine Einzahnwerkzeuge, höhere Drehzahl
      flutes = 1;
      notes =
        'Isolationsfräsen; sehr flache Zustellung. Häufig kleine Einzahnwerkzeuge (0,8–1,0 mm) mit V- oder Geradegeometrie.';
      break;
    case 'Aluminium (Aluminum)':
      baseFeed = 700;
      plunge = 220;
      depthFactor = 0.1;
      spindleRPM = 16000; // auf DeWalt-Minimum angehoben (Stufe 1)
      flutes = 2;
      notes =
        'Metallbearbeitung; geringe Zustelltiefe, gute Spanntechnik und ggf. Schmierung notwendig. Empfohlen: 1–2-Schneider-Aluminiumfräser (Upcut).';
      break;
    default:
      baseFeed = 1500;
      plunge = 500;
      depthFactor = 0.28;
      spindleRPM = 16000;
      flutes = 2;
      notes = 'Generische Einstellung; bitte an Reststück prüfen.';
      break;
  }
 
  const refDiameter = 2.5;
  const feedScale = Math.sqrt(bitDiameter / refDiameter);
  let feed = baseFeed * feedScale;
 
  let depth = depthFactor * bitDiameter;
  depth = constrain(depth, 0.15, bitDiameter * 0.5);
 
  if (thickness < 4) {
    feed *= 1.05;
    notes += ' Dünnes Material: Feed leicht erhöht.';
  } else if (thickness > 12 && thickness <= 20) {
    feed *= 0.9;
    notes += ' Dickes Material: Feed leicht reduziert.';
  } else if (thickness > 20) {
    feed *= 0.85;
    notes +=
      ' Sehr dickes Material: eher in mehreren Stufen bearbeiten, ggf. von beiden Seiten.';
  }
 
  if (bitType === 'Spiralnut Aufwärts (Upcut end mill)') {
    notes += ' Gewählter Fräser: Upcut; sehr guter Spanabtransport, mögliche Ausrisse oben.';
  } else if (bitType === 'Gerade Schneide (Straight-cut end mill)') {
    notes +=
      ' Gewählter Fräser: gerade Schneide; gute Kantenqualität in Holz/Kunststoff, Spanabtransport geringer.';
  }
 
  if (bitDiameter < 1.0 && flutes > 1) {
    flutes = 1;
    notes =
      'Hinweis: Für Fräser unter 1.0 mm wird die Zahnzahl vorsorglich auf 1 gesetzt. ' +
      notes;
  }
 
  const dial = dewaltDialForRPM(spindleRPM);
 
  return {
    feedRate: feed,
    plungeRate: plunge,
    depthPerPass: depth,
    spindleRPM: spindleRPM,
    flutes: flutes,
    plungeType: plungeType,
    notes: notes,
    dial: dial
  };
}
 
function roundTo(value, decimals) {
  return Number(value.toFixed(decimals));
}

html

<!doctype html>
<html lang="de">
<head>
  <meta charset="utf-8">
  <title>CNC Feed &amp; Chip-Load Rechner V. 1.01 – Creative Technologies Lab, FH Münster</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <!-- p5.js library -->
  <script src="https://cdn.jsdelivr.net/npm/p5@1.9.0/lib/p5.min.js"></script>

  <style>
    * {
      box-sizing: border-box;
    }
    body {
      font-family: system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif;
      background-color: #f3f4f6;
      color: #111827;
      margin: 0;
      padding: 24px 0;
    }
    #calculator-container {
      max-width: 960px;
      margin: 0 auto;
      padding: 0 16px 24px 16px;
    }
    .app-title {
      font-size: 24px;
      font-weight: 600;
      margin-bottom: 4px;
    }
    .app-subtitle {
      font-size: 13px;
      color: #6b7280;
      margin-bottom: 18px;
    }
    .layout {
      display: block;
    }
    .panel {
      background-color: #ffffff;
      border-radius: 12px;
      padding: 16px 18px 18px 18px;
      box-shadow: 0 8px 20px rgba(15, 23, 42, 0.06);
      margin-bottom: 16px;
    }
    .panel-title {
      font-size: 16px;
      font-weight: 600;
      margin-bottom: 10px;
    }
    .field-group {
      margin-bottom: 10px;
    }
    .field-label {
      display: block;
      font-size: 13px;
      font-weight: 500;
      margin-bottom: 4px;
    }
    select,
    input[type="number"],
    button {
      font-family: inherit;
      font-size: 13px;
    }
    select,
    input[type="number"] {
      width: 100%;
      padding: 6px 8px;
      border-radius: 6px;
      border: 1px solid #d1d5db;
      background-color: #f9fafb;
    }
    select:focus,
    input[type="number"]:focus {
      outline: none;
      border-color: #2563eb;
      box-shadow: 0 0 0 1px #2563eb20;
      background-color: #ffffff;
    }
    .field-row {
      display: grid;
      grid-template-columns: repeat(2, minmax(0, 1fr));
      gap: 10px;
    }
    @media (max-width: 600px) {
      .field-row {
        grid-template-columns: 1fr;
      }
    }
    button {
      margin-top: 6px;
      padding: 7px 14px;
      border-radius: 999px;
      border: none;
      background-color: #2563eb;
      color: #ffffff;
      font-weight: 500;
      cursor: pointer;
    }
    button:hover {
      background-color: #1d4ed8;
    }
    .hint {
      font-size: 11px;
      color: #6b7280;
      margin-top: 3px;
    }
    .error-message {
      color: #b91c1c;
      font-weight: 500;
      margin-top: 10px;
      padding: 8px 10px;
      border-radius: 6px;
      background-color: #fee2e2;
      border: 1px solid #fecaca;
      font-size: 12px;
    }
    .summary-row {
      display: flex;
      flex-wrap: wrap;
      gap: 8px;
      margin-bottom: 10px;
    }
    .summary-pill {
      padding: 4px 8px;
      border-radius: 999px;
      background-color: #eff6ff;
      border: 1px solid #bfdbfe;
      font-size: 11px;
      color: #1d4ed8;
    }
    #result-table {
      width: 100%;
      border-collapse: collapse;
      margin-top: 6px;
    }
    #result-table th,
    #result-table td {
      border: 1px solid #e5e7eb;
      padding: 4px 6px;
      font-size: 12px;
      text-align: left;
      vertical-align: top;
    }
    #result-table th {
      background-color: #f9fafb;
      font-weight: 600;
    }
    #result-table tr:nth-child(even) td {
      background-color: #f3f4f6;
    }
    .license-note {
      margin-top: 8px;
      font-size: 11px;
      color: #6b7280;
      line-height: 1.4;
    }
    .license-note a {
      color: #2563eb;
      text-decoration: none;
    }
    .license-note a:hover {
      text-decoration: underline;
    }
  </style>
</head>
<body>

<div id="calculator-container">
  <!-- Inhalt wird durch p5.js im Sketch erzeugt -->
</div>

<script>
  // CNC feed suggestion helper with adjustable bit diameter
  // Includes spindle speed and chip load calculation
  // Version 1.01 – angepasst auf DeWalt D26200 Drehzahlstufen

  let materialSelect;
  let thicknessInput;
  let bitDiameterInput;
  let bitTypeSelect;
  let calcButton;
  let resultDiv;

  function applyCustomStyles() {
    // Styles sind im <style>-Block definiert
  }

  function setup() {
    applyCustomStyles();

    createCanvas(1, 1);
    noLoop();
    select('canvas').style('display', 'none');

    const container = select('#calculator-container');

    const title = createDiv('CNC Feed & Chip-Load Rechner V. 1.01');
    title.addClass('app-title');
    title.parent(container);

    const subtitle = createDiv(
      'Konservative Startwerte für Hobby-CNC-Router mit DeWalt D26200. Vor Serienfertigung immer an Reststücken testen.'
    );
    subtitle.addClass('app-subtitle');
    subtitle.parent(container);

    const layout = createDiv();
    layout.addClass('layout');
    layout.parent(container);

    const inputPanel = createDiv();
    inputPanel.addClass('panel');
    inputPanel.parent(layout);

    const inputTitle = createDiv('Eingaben');
    inputTitle.addClass('panel-title');
    inputTitle.parent(inputPanel);

    const matGroup = createDiv();
    matGroup.addClass('field-group');
    matGroup.parent(inputPanel);

    const matLabel = createSpan('Material (Material):');
    matLabel.addClass('field-label');
    matLabel.parent(matGroup);

    materialSelect = createSelect();
    materialSelect.parent(matGroup);
    materialSelect.option('Birkensperrholz (Birch Plywood)');
    materialSelect.option('Kiefer (Pine)');
    materialSelect.option('Bambus (Bamboo)');
    materialSelect.option('MDF (MDF)');
    materialSelect.option('Kirschsperrholz (Cherry Plywood)');
    materialSelect.option('Harter Ahorn (Hard Maple)');
    materialSelect.option('Bubinga (Bubinga)');
    materialSelect.option('HDPE (HDPE)');
    materialSelect.option('2-Farb-HDPE (2-Color HDPE)');
    materialSelect.option('Geschäumtes PVC (Expanded PVC)');
    materialSelect.option('ABS (ABS)');
    materialSelect.option('Gussacryl (Cast Acrylic)');
    materialSelect.option('2-Farb-Acryl (2-Color Acrylic)');
    materialSelect.option('Corian (Corian)');
    materialSelect.option('Linoleum (Linoleum)');
    materialSelect.option('Leiterplattenmaterial PCB (PCB)');
    materialSelect.option('Aluminium (Aluminum)');

    const thickGroup = createDiv();
    thickGroup.addClass('field-group');
    thickGroup.parent(inputPanel);

    const thickLabel = createSpan('Materialdicke [mm] (Material thickness):');
    thickLabel.addClass('field-label');
    thickLabel.parent(thickGroup);

    thicknessInput = createInput('');
    thicknessInput.attribute('type', 'number');
    thicknessInput.attribute('min', '0.1');
    thicknessInput.attribute('step', '0.1');
    thicknessInput.parent(thickGroup);

    const thickHint = createDiv('Typische Platten: 3 / 6 / 9 / 12 / 18 mm.');
    thickHint.addClass('hint');
    thickHint.parent(thickGroup);

    const toolRow = createDiv();
    toolRow.addClass('field-row');
    toolRow.parent(inputPanel);

    const bitGroup = createDiv();
    bitGroup.addClass('field-group');
    bitGroup.parent(toolRow);

    const bitLabel = createSpan('Fräserdurchmesser [mm] (Bit diameter):');
    bitLabel.addClass('field-label');
    bitLabel.parent(bitGroup);

    bitDiameterInput = createInput('2.5');
    bitDiameterInput.attribute('type', 'number');
    bitDiameterInput.attribute('min', '0.5');
    bitDiameterInput.attribute('step', '0.1');
    bitDiameterInput.parent(bitGroup);

    const typeGroup = createDiv();
    typeGroup.addClass('field-group');
    typeGroup.parent(toolRow);

    const typeLabel = createSpan('Fräser-Typ (End mill type):');
    typeLabel.addClass('field-label');
    typeLabel.parent(typeGroup);

    bitTypeSelect = createSelect();
    bitTypeSelect.parent(typeGroup);
    bitTypeSelect.option('Spiralnut Aufwärts (Upcut end mill)');
    bitTypeSelect.option('Gerade Schneide (Straight-cut end mill)');

    const typeHint = createDiv(
      'Upcut: starker Spanabtransport, mögliche Ausrisse oben. Gerade Schneide: ruhigere Kante, weniger Spanabtransport.'
    );
    typeHint.addClass('hint');
    typeHint.parent(inputPanel);

    calcButton = createButton('Vorschläge berechnen (Get suggestions)');
    calcButton.parent(inputPanel);
    calcButton.mousePressed(updateSettings);

    const btnHint = createDiv(
      'Alle Werte sind Startpunkte. Bei Geräusch, Vibration oder Brandspuren Parameter anpassen.'
    );
    btnHint.addClass('hint');
    btnHint.parent(inputPanel);

    const outputPanel = createDiv();
    outputPanel.addClass('panel');
    outputPanel.parent(container);

    const outputTitle = createDiv('Ergebnis');
    outputTitle.addClass('panel-title');
    outputTitle.parent(outputPanel);

    resultDiv = createDiv('Noch keine Berechnung durchgeführt.');
    resultDiv.parent(outputPanel);

    const licenseDiv = createDiv(
      'Creative Technologies Lab, FH Münster – Lizenz: Dieses Tool steht unter der Creative-Commons-Lizenz ' +
        '<a href="https://creativecommons.org/licenses/by-sa/3.0/" target="_blank" rel="noopener noreferrer">CC BY-SA 3.0</a>. ' +
        'Es darf kopiert, geteilt und angepasst werden, solange die Urheberschaft genannt und abgeleitete Versionen unter denselben Bedingungen weitergegeben werden; ' +
        'zusätzliche Einschränkungen oder abweichende Lizenzbedingungen dürfen nicht hinzugefügt werden.'
    );
    licenseDiv.addClass('license-note');
    licenseDiv.parent(container);
  }

  function updateSettings() {
    const mat = materialSelect.value();
    const t = parseFloat(thicknessInput.value());
    const bitD = parseFloat(bitDiameterInput.value());
    const bitType = bitTypeSelect.value();

    if (isNaN(t) || t <= 0) {
      resultDiv.html(
        '<div class="error-message">Bitte eine gültige Materialdicke in Millimetern eingeben.</div>'
      );
      return;
    }
    if (isNaN(bitD) || bitD <= 0) {
      resultDiv.html(
        '<div class="error-message">Bitte einen gültigen Fräserdurchmesser in Millimetern eingeben.</div>'
      );
      return;
    }

    const settings = computeSettings(mat, t, bitD, bitType);
    const passes = Math.ceil(t / settings.depthPerPass);
    const chipLoad = settings.feedRate / (settings.spindleRPM * settings.flutes);

    const summaryHtml =
      '<div class="summary-row">' +
      `<div class="summary-pill">Feed: ${Math.round(settings.feedRate)} mm/min</div>` +
      `<div class="summary-pill">Plunge: ${Math.round(settings.plungeRate)} mm/min</div>` +
      `<div class="summary-pill">Chip Load: ${roundTo(chipLoad, 4)} mm/Zahn</div>` +
      `<div class="summary-pill">Drehzahl: ${Math.round(settings.spindleRPM)} U/min (Stufe ${settings.dial})</div>` +
      `<div class="summary-pill">Depth/Pass: ${roundTo(settings.depthPerPass, 2)} mm</div>` +
      `<div class="summary-pill">ca. ${passes} Durchgänge</div>` +
      '</div>';

    const tableHtml =
      '<table id="result-table">' +
      '<tr>' +
      '<th>Material</th>' +
      '<th>Dicke [mm]</th>' +
      '<th>Ø Bit [mm]</th>' +
      '<th>Bit-Typ</th>' +
      '<th>Drehzahl [U/min] (DeWalt-Stufe)</th>' +
      '<th>Zähne</th>' +
      '<th>Chip Load [mm]</th>' +
      '<th>Feed [mm/min]</th>' +
      '<th>Plunge [mm/min]</th>' +
      '<th>Depth/Pass [mm]</th>' +
      '<th>Plunge-Art</th>' +
      '<th>Hinweise</th>' +
      '</tr>' +
      '<tr>' +
      `<td>${mat}</td>` +
      `<td>${roundTo(t, 1)}</td>` +
      `<td>${roundTo(bitD, 2)}</td>` +
      `<td>${bitType}</td>` +
      `<td>${Math.round(settings.spindleRPM)} (Stufe ${settings.dial})</td>` +
      `<td>${settings.flutes}</td>` +
      `<td>${roundTo(chipLoad, 4)}</td>` +
      `<td>${Math.round(settings.feedRate)}</td>` +
      `<td>${Math.round(settings.plungeRate)}</td>` +
      `<td>${roundTo(settings.depthPerPass, 2)}</td>` +
      `<td>${settings.plungeType}</td>` +
      `<td>${settings.notes}</td>` +
      '</tr>' +
      '</table>';

    resultDiv.html(summaryHtml + tableHtml);
  }

  // Zuordnung DeWalt D26200 Drehzahl zu Reglerstufe 1–6
  function dewaltDialForRPM(rpm) {
    const table = [
      { dial: 1, rpm: 16000 },
      { dial: 2, rpm: 18200 },
      { dial: 3, rpm: 20400 },
      { dial: 4, rpm: 22600 },
      { dial: 5, rpm: 24800 },
      { dial: 6, rpm: 27000 }
    ];

    let best = table[0];
    let bestDiff = Math.abs(rpm - best.rpm);

    for (let i = 1; i < table.length; i++) {
      const d = Math.abs(rpm - table[i].rpm);
      if (d < bestDiff) {
        bestDiff = d;
        best = table[i];
      }
    }
    return best.dial;
  }

  // Berechnung der Parameter
  function computeSettings(material, thickness, bitDiameter, bitType) {
    let baseFeed;
    let plunge;
    let depthFactor;
    let spindleRPM;
    let flutes;
    let plungeType = 'Rampe 5° (Ramp 5°)';
    let notes;

    switch (material) {
      case 'Birkensperrholz (Birch Plywood)':
      case 'Bambus (Bamboo)':
      case 'MDF (MDF)':
      case 'Kirschsperrholz (Cherry Plywood)':
        baseFeed = 1800;
        plunge = 600;
        depthFactor = 0.32;
        spindleRPM = 16000; // DeWalt Stufe 1
        flutes = 2;
        notes =
          'Standard-Holzeinstellung. Empfohlene Fräser: 2-Schneider-Spiralnut, ggf. Downcut oder Kompressionsfräser.';
        break;
      case 'Kiefer (Pine)':
        baseFeed = 1900;
        plunge = 650;
        depthFactor = 0.32;
        spindleRPM = 16000; // weiches Holz, Stufe 1 ausreichend
        flutes = 2;
        notes = 'Weiches Nadelholz; etwas höhere Vorschubgeschwindigkeit möglich.';
        break;
      case 'Harter Ahorn (Hard Maple)':
      case 'Bubinga (Bubinga)':
        baseFeed = 1300;
        plunge = 400;
        depthFactor = 0.24;
        spindleRPM = 16000; // konservativ, Stufe 1
        flutes = 2;
        notes =
          'Hartes Holz; reduzierte Zustelltiefe. Empfohlene Fräser: sehr scharfe 2-Schneider-Spiralnut.';
        break;
      case 'HDPE (HDPE)':
      case '2-Farb-HDPE (2-Color HDPE)':
      case 'Geschäumtes PVC (Expanded PVC)':
      case 'ABS (ABS)':
        baseFeed = 2200;
        plunge = 700;
        depthFactor = 0.32;
        spindleRPM = 18000; // nahe Stufe 2 (≈18200 U/min)
        flutes = 2;
        notes =
          'Weicher Kunststoff; höherer Feed hilft gegen Schmelzen. Empfohlen: polierte Spiralnut, 1–2 Schneiden.';
        break;
      case 'Gussacryl (Cast Acrylic)':
      case '2-Farb-Acryl (2-Color Acrylic)':
        baseFeed = 1200;
        plunge = 400;
        depthFactor = 0.24;
        spindleRPM = 16000; // eher niedrige Drehzahl für Acryl
        flutes = 1;
        notes =
          'Sprödes Acryl; konservative Zustelltiefe. Einzahn-Spiralnut mit polierten Schneiden ist oft vorteilhaft.';
        break;
      case 'Corian (Corian)':
        baseFeed = 1200;
        plunge = 400;
        depthFactor = 0.24;
        spindleRPM = 16000;
        flutes = 2;
        notes = 'Dichter Mineralwerkstoff; ähnlich hartem Holz, gute Absaugung notwendig.';
        break;
      case 'Linoleum (Linoleum)':
        baseFeed = 1900;
        plunge = 650;
        depthFactor = 0.4;
        spindleRPM = 16000;
        flutes = 2;
        notes = 'Weiches Material; größere Zustelltiefe möglich. Trägerschicht beachten.';
        break;
      case 'Leiterplattenmaterial PCB (PCB)':
        baseFeed = 800;
        plunge = 250;
        depthFactor = 0.12;
        spindleRPM = 18000; // kleine Einzahnwerkzeuge, höhere Drehzahl
        flutes = 1;
        notes =
          'Isolationsfräsen; sehr flache Zustellung. Häufig kleine Einzahnwerkzeuge (0,8–1,0 mm) mit V- oder Geradegeometrie.';
        break;
      case 'Aluminium (Aluminum)':
        baseFeed = 700;
        plunge = 220;
        depthFactor = 0.1;
        spindleRPM = 16000; // auf DeWalt-Minimum angehoben (Stufe 1)
        flutes = 2;
        notes =
          'Metallbearbeitung; geringe Zustelltiefe, gute Spanntechnik und ggf. Schmierung notwendig. Empfohlen: 1–2-Schneider-Aluminiumfräser (Upcut).';
        break;
      default:
        baseFeed = 1500;
        plunge = 500;
        depthFactor = 0.28;
        spindleRPM = 16000;
        flutes = 2;
        notes = 'Generische Einstellung; bitte an Reststück prüfen.';
        break;
    }

    const refDiameter = 2.5;
    const feedScale = Math.sqrt(bitDiameter / refDiameter);
    let feed = baseFeed * feedScale;

    let depth = depthFactor * bitDiameter;
    depth = constrain(depth, 0.15, bitDiameter * 0.5);

    if (thickness < 4) {
      feed *= 1.05;
      notes += ' Dünnes Material: Feed leicht erhöht.';
    } else if (thickness > 12 && thickness <= 20) {
      feed *= 0.9;
      notes += ' Dickes Material: Feed leicht reduziert.';
    } else if (thickness > 20) {
      feed *= 0.85;
      notes +=
        ' Sehr dickes Material: eher in mehreren Stufen bearbeiten, ggf. von beiden Seiten.';
    }

    if (bitType === 'Spiralnut Aufwärts (Upcut end mill)') {
      notes += ' Gewählter Fräser: Upcut; sehr guter Spanabtransport, mögliche Ausrisse oben.';
    } else if (bitType === 'Gerade Schneide (Straight-cut end mill)') {
      notes +=
        ' Gewählter Fräser: gerade Schneide; gute Kantenqualität in Holz/Kunststoff, Spanabtransport geringer.';
    }

    if (bitDiameter < 1.0 && flutes > 1) {
      flutes = 1;
      notes =
        'Hinweis: Für Fräser unter 1.0 mm wird die Zahnzahl vorsorglich auf 1 gesetzt. ' +
        notes;
    }

    const dial = dewaltDialForRPM(spindleRPM);

    return {
      feedRate: feed,
      plungeRate: plunge,
      depthPerPass: depth,
      spindleRPM: spindleRPM,
      flutes: flutes,
      plungeType: plungeType,
      notes: notes,
      dial: dial
    };
  }

  function roundTo(value, decimals) {
    return Number(value.toFixed(decimals));
  }
</script>

</body>
</html>