================================================================
  GENERATOR G-CODE — INSTRUKCJA OBSŁUGI
  Wersja: kształty + narożniki + rampy po ścieżce + otwory +
          laser + opóźnienia ruchu (dwell) po ON/OFF +
          arc_step (rozdzielczość aproksymacji łuków)
================================================================

----------------------------------------------------------------
1. INSTALACJA I URUCHOMIENIE
----------------------------------------------------------------

1. Zapisz plik jako:  gcode-generator.html
2. Otwórz go podwójnym kliknięciem (Chrome / Edge / Firefox / Safari).
3. Aplikacja działa w 100% lokalnie, bez internetu, bez serwera.

Ekran dzieli się na 3 kolumny:
  - LEWA      - pole danych wejściowych + przyciski akcji
  - ŚRODKOWA  - wygenerowany G-code + pobieranie/kopiowanie
  - PRAWA     - podgląd 2D na kanwie

----------------------------------------------------------------
2. WPROWADZANIE DANYCH
----------------------------------------------------------------

Format to zwykły tekst, w stylu  klucz = wartość.
Komentarze zaczynają się od "#" lub ";".
Puste linie są ignorowane.

Dane można wprowadzić trzema sposobami:
  a) wpisać ręcznie w polu tekstowym,
  b) kliknąć [Wczytaj plik] i wybrać plik .txt z dysku,
  c) przeciągnąć plik .txt na pole tekstowe (drag & drop).

Po kliknięciu [Generuj G-code] zawartość pojawi się
w środkowej kolumnie. Można ją skopiować lub pobrać jako .nc.

----------------------------------------------------------------
3. PARAMETRY GLOBALNE
----------------------------------------------------------------

  mode              mill | laser         tryb pracy (domyślnie mill)
  shape             circle | polygon | rectangle
  direction         CCW | CW             kierunek konturu
  side              outside | inside     offset konturu
  tool_diameter     liczba [mm]          Ø narzędzia (alias: kerf, d)
  use_arcs          true | false         użyj G2/G3 czy aproksymuj
  arc_step          liczba [stopnie]     kąt na odcinek aproksymacji
                                         (domyślnie 5.625°, tylko gdy
                                         use_arcs = false)
  feed              liczba [mm/min]      posuw roboczy
  safe_z            liczba [mm]          Z bezpieczne
  cut_z             liczba [mm]          Z robocze / ogniskowa
  stock_top         liczba [mm]          Z wierzchni materiału

Okrąg (shape = circle):
  cx, cy            liczba [mm]          środek
  radius            liczba [mm]          promień (alias: r)

Wielokąt (shape = polygon | rectangle):
  vertex = x, y                          kolejny wierzchołek (min. 3)
  corner = idx, typ, param               narożnik o indeksie idx (0-based)
                                         typ:  sharp | round | chamfer
                                         param: promień lub długość ścięcia

----------------------------------------------------------------
4. ROZDZIELCZOŚĆ APROKSYMACJI ŁUKÓW (arc_step)
----------------------------------------------------------------

Parametr arc_step steruje gęstością aproksymacji łuków odcinkami G1.
Dotyczy WSZYSTKICH łuków w programie:

  - narożników zaokrąglonych,
  - okręgu (shape = circle),
  - otworów frezowanych helikalnie (gdy use_arcs = false),
  - otworów wycinanych laserem (gdy use_arcs = false),
  - ramp helikalnych po ścieżce,
  - konturu gdy use_arcs = false.

Wartość podawana w stopniach — ile stopni kąta na jeden odcinek G1.

  arc_step = 15   →  24 odcinki na pełny okrąg  (zgrubne, krótki G-code)
  arc_step = 5.625 → 64 odcinki (domyślne, π/32 rad)
  arc_step = 3    → 120 odcinków (dokładne)
  arc_step = 1    → 360 odcinków (bardzo gładkie)

Aliasy klucza: arc_step, arc_step_deg, arc_resolution, arc_peck.

Uwaga: gdy use_arcs = true, parametr arc_step jest ignorowany
dla łuków emitowanych jako G2/G3 — nie ma on wtedy zastosowania.
Ale nadal obowiązuje dla aproksymacji G1, gdy use_arcs = false.

----------------------------------------------------------------
5. RAMPA WEJŚCIA / WYJŚCIA (tylko mill)
----------------------------------------------------------------

  ramp              none | linear | helical
  ramp_length       liczba [mm]          długość rampy liniowej
  ramp_pitch        liczba [mm/obr]      skok rampy helikalnej
  ramp_clearance    liczba [mm]          ile nad materiałem start

Sekwencja rampy liniowej po ścieżce (obwód P, rampa rl):
  1) rampa wejściowa:    D: 0   -> rl       Z: zTop -> cut_z
  2) obwód:              D: rl  -> P        Z = cut_z
  3) POWTÓRKA WEJŚCIA:   D: P   -> P+rl     Z = cut_z
     (ten sam ruch XY co rampa wejściowa, ale płasko na cut_z —
      docięcie początkowego fragmentu, który w kroku 1 był nad
      materiałem)
  4) rampa wyjściowa:    D: P+rl -> P+2*rl  Z: cut_z -> zTop
     (kolejny odcinek obwodu po powtórce)

KONIEC RAMPY WEJŚCIOWEJ = POCZĄTEK RAMPY WYJŚCIOWEJ.
Dzięki temu nie pozostaje niedocięty fragment materiału.

Jeżeli P < 2*rl, to rl = P/2 — rampy stykają się dokładnie
w połowie obwodu.

Rampa helikalna (ramp = helical):
  narzędzie okrąża kontur wielokrotnie, w każdej pętli opadając
  o ramp_pitch mm, aż do cut_z; wyjście analogicznie w górę.
  Liczba pętli = ceil((zTop - cut_z) / ramp_pitch).

----------------------------------------------------------------
6. OTWORY (ZAWSZE INSIDE + CCW)
----------------------------------------------------------------

Otwory są ZAWSZE traktowane jako kieszeń wewnętrzna, niezależnie
od globalnego side i direction:
  - offset wewnętrzny (promień toru = Ø_otworu/2 - Ø_narzędzia/2)
  - kierunek CCW (G3 lub aproksymacja G1 — zależnie od use_arcs)

  hole_mode         drill | helical       tryb domyślny
  peck_depth        liczba [mm]           głębokość pojedynczego wejścia
  hole = x, y, Ø, głębokość [, tryb]      definicja otworu (można wielokrotnie)

Tryby otworów:
  drill    — wiercenie z podziałem na peeki (peck_depth), pełny
             wyjazd między wejściami, szybki powrót do 0.3 mm nad
             poprzednim poziomem
  helical  — frezowanie spiralne G3 (lub aproksymacja G1, gdy
             use_arcs = false) z opadaniem o ramp_pitch na obrót;
             na końcu pełne okrążenie wyrównujące dno

Parametr use_arcs jest NADRZĘDNY — dla helical decyduje, czy
zostaną użyte komendy G2/G3, czy seria G1 z krokiem arc_step.

----------------------------------------------------------------
7. STEROWANIE WRZECIONEM I LASEREM — KOMENDY I OPÓŹNIENIA
----------------------------------------------------------------

OPÓŹNIENIE RUCHU (DWELL) — jeden uniwersalny parametr:

  dwell_cmd         szablon komendy opóźnienia
                    dostępne placeholdery:
                      {sec}  → czas w sekundach (3 miejsca)
                      {ms}   → czas w milisekundach (liczba całkowita)
                    przykłady:
                      G4 P{sec}      (GRBL, LinuxCNC, Fanuc, Mach3)
                      G4 P{ms}       (niektóre sterowniki)
                      G4 U{sec}      (część Fanuc)
                      G4 X{sec}      (część sterowników)

Jeśli dwell_cmd jest pusty lub opóźnienie = 0 ms, nic nie jest
emitowane.

--- LASER (mode = laser) ---

  laser_power       liczba 0..1000        moc S (alias: power, s)
  laser_passes      liczba >= 1           liczba przejść
  laser_mode        M3 | M4               tylko dla domyślnej komendy ON

  laser_on_cmd      szablon komendy włączenia lasera
                    placeholder: {power}
                    domyślnie: M3 S{power} (lub M4 S{power})
                    przykłady:
                      M3 S{power}          stała moc
                      M4 S{power}          dynamiczna moc (GRBL)
                      M4 S{power}\nM8      (dwie linie — patrz niżej *)

  laser_off_cmd     komenda wyłączenia lasera
                    domyślnie: M5
                    przykłady:  M5, M5\nM9

  laser_on_delay    liczba [ms]  opóźnienie PO włączeniu lasera
  laser_off_delay   liczba [ms]  opóźnienie PO wyłączeniu lasera

*) Jeśli chcesz wstawić kilka komend w jednym polu, rozdziel je
   sekwencją \n (dosłownie backslash + n) — generator zamieni go
   na przejście do nowej linii w G-code. Każda z tych linii
   pojawi się jako osobny wiersz w oknie i w pliku .nc.

--- WRZECIONO (mode = mill) ---

  spindle_on_cmd    szablon komendy włączenia wrzeciona
                    placeholder: {rpm}
                    domyślnie: puste (nic nie emitowane)
                    przykłady:
                      M3 S{rpm}
                      M3 S{rpm}\nM8       (obroty + chłodziwo)

  spindle_off_cmd   komenda wyłączenia wrzeciona
                    domyślnie: puste
                    przykłady: M5, M5\nM9

  spindle_rpm       liczba  obroty do placeholdera {rpm}
                    domyślnie: 12000

  spindle_on_delay  liczba [ms]  opóźnienie PO włączeniu
  spindle_off_delay liczba [ms]  opóźnienie PO wyłączeniu

Jeśli spindle_on_cmd / spindle_off_cmd są puste, program nie
emituje żadnej komendy wrzeciona (kompatybilność ze starszymi
plikami).

--- KOLEJNOŚĆ W GENEROWANYM G-CODE ---

Frezowanie:
  G21 / G90 / G17
  [spindle_on_cmd + dwell(spindle_on_delay)]   ← jeśli podane
  G0 Z<safe_z>
  ... otwory ...
  ... kontur (z rampami) ...
  G0 Z<safe_z>
  [spindle_off_cmd + dwell(spindle_off_delay)] ← jeśli podane
  M30

Laser:
  G21 / G90 / G17
  [laser_off_cmd + dwell(laser_off_delay)]     ← zawsze, bezpieczeństwo
  G0 Z<safe_z>
  ... otwory:
        G0 na start / G0 Z<focus>
        DLA KAŻDEGO PRZEJŚCIA:
          laser_on_cmd  →  dwell(laser_on_delay)
          ruch cięcia
          laser_off_cmd →  dwell(laser_off_delay)
  ... kontur (analogicznie)
  M30

----------------------------------------------------------------
8. PRZYKŁADY
----------------------------------------------------------------

-------------------------
8.1  FREZOWANIE — prostokąt z narożnikami + rampa + otwory
-------------------------

mode           = mill
shape          = polygon
direction      = CCW
side           = outside
tool_diameter  = 6
use_arcs       = true
arc_step       = 3
feed           = 300
safe_z         = 5
cut_z          = -3
stock_top      = 0

ramp           = linear
ramp_length    = 20
ramp_clearance = 1
ramp_pitch     = 1.5

spindle_on_cmd    = M3 S{rpm}
spindle_off_cmd   = M5
spindle_rpm       = 12000
spindle_on_delay  = 500
spindle_off_delay = 0

dwell_cmd      = G4 P{sec}

hole_mode      = helical
peck_depth     = 2

vertex = 0, 0
vertex = 60, 0
vertex = 60, 40
vertex = 0, 40

corner = 0, round, 6
corner = 1, round, 6
corner = 2, chamfer, 8
corner = 3, chamfer, 8

hole = 15, 15, 8, 12
hole = 45, 15, 8, 12
hole = 30, 28, 12, 15, helical

-------------------------
8.2  FREZOWANIE — okrąg Ø40, frez na zewnątrz
-------------------------

shape          = circle
direction      = CCW
side           = outside
tool_diameter  = 6
use_arcs       = true
feed           = 400
safe_z         = 5
cut_z          = -2

cx     = 50
cy     = 50
radius = 20

-------------------------
8.3  FREZOWANIE — kieszeń 100×60, fazy 10×10, wejście po skosie
-------------------------

mode           = mill
shape          = rectangle
direction      = CW
side           = inside
tool_diameter  = 8
use_arcs       = true
feed           = 500
safe_z         = 5
cut_z          = -6

ramp           = linear
ramp_length    = 12
ramp_clearance = 1

vertex = 0, 0
vertex = 100, 0
vertex = 100, 60
vertex = 0, 60

corner = 0, chamfer, 10
corner = 1, chamfer, 10
corner = 2, chamfer, 10
corner = 3, chamfer, 10

-------------------------
8.4  FREZOWANIE — tylko otwory (bez konturu)
-------------------------

mode           = mill
tool_diameter  = 6
feed           = 250
safe_z         = 5
stock_top      = 0

hole_mode      = drill
peck_depth     = 2.5

hole = 20, 20, 5, 15
hole = 60, 20, 5, 15
hole = 20, 50, 5, 15
hole = 60, 50, 5, 15
hole = 40, 35, 20, 10, helical

-------------------------
8.5  FREZOWANIE — rampa helikalna, kieszeń kwadratowa
-------------------------

mode           = mill
shape          = polygon
direction      = CCW
side           = inside
tool_diameter  = 8
feed           = 500
safe_z         = 5
cut_z          = -8
stock_top      = 0

ramp           = helical
ramp_pitch     = 1.5
ramp_clearance = 1

vertex = 0, 0
vertex = 60, 0
vertex = 60, 60
vertex = 0, 60

corner = 0, round, 5
corner = 1, round, 5
corner = 2, round, 5
corner = 3, round, 5

-------------------------
8.6  FREZOWANIE bez G2/G3 (aproksymacja odcinkami)
-------------------------

use_arcs       = false
arc_step       = 5

...reszta jak w 8.1 — wszystkie łuki zostaną rozbite na odcinki G1
co 5°, co 5.625° (jeśli arc_step pominąć), itd.

-------------------------
8.7  LASER — prostokąt + otwór, własne komendy i opóźnienia
-------------------------

mode           = laser
shape          = polygon
direction      = CCW
side           = outside
kerf           = 0.15
use_arcs       = true
arc_step       = 3
feed           = 800
safe_z         = 5
cut_z          = 0
stock_top      = 0

laser_power    = 700
laser_passes   = 2
laser_mode     = M4

laser_on_cmd   = M4 S{power}
laser_off_cmd  = M5

laser_on_delay  = 150
laser_off_delay = 80

dwell_cmd      = G4 P{sec}

vertex = 0, 0
vertex = 80, 0
vertex = 80, 50
vertex = 0, 50

corner = 0, round, 5
corner = 1, round, 5
corner = 2, round, 5
corner = 3, round, 5

hole = 40, 25, 10, 0

-------------------------
8.8  LASER — chłodziwo powietrzne razem z wiązką (wiele komend)
-------------------------

laser_on_cmd   = M4 S{power}\nM8
laser_off_cmd  = M5\nM9

laser_on_delay  = 200
laser_off_delay = 300

dwell_cmd      = G4 P{sec}
(...reszta jak w 8.7)

-------------------------
8.9  FREZOWANIE z chłodziwem
-------------------------

spindle_on_cmd  = M3 S{rpm}\nM8
spindle_off_cmd = M5\nM9
spindle_rpm     = 10000
spindle_on_delay  = 800
spindle_off_delay = 200

dwell_cmd       = G4 P{sec}

----------------------------------------------------------------
9. INTERPRETACJA PODGLĄDU
----------------------------------------------------------------

  Niebieska linia przerywana - oryginalny kształt (z narożnikami)
  Pomarańczowa linia         - ścieżka środka narzędzia (z offsetem)
  Czerwona linia             - ścieżka lasera (mode = laser)
  Różowe kółka z krzyżykiem  - otwory
  Zielone "S"                - punkt startu konturu

----------------------------------------------------------------
10. OGRANICZENIA I UWAGI PRAKTYCZNE
----------------------------------------------------------------

- Programowany jest ŚRODEK NARZĘDZIA. Nie używamy G41/G42 —
  kompensacja jest wbudowana w geometrię ścieżki.

- Dla trybu frezowania komendy wrzeciona są opcjonalne. Jeśli
  chcesz je mieć, podaj spindle_on_cmd / spindle_off_cmd.
  W przeciwnym razie nic nie jest emitowane.

- Dla trybu lasera laser_off_cmd + laser_off_delay są zawsze
  emitowane na starcie (bezpieczeństwo), a potem przed/po
  każdym konturem i każdym otworem.

- Opóźnienia ruchu (dwell) są emitowane tylko jeśli wartość > 0 ms
  i dwell_cmd nie jest pusty.

- W jednym polu komendy można podać wiele komend rozdzielonych
  sekwencją \n (dosłownie: backslash + n). Zostanie zamieniona
  na przejście do nowej linii — każda linia jest osobno w G-code.

- use_arcs jest NADRZĘDNE dla wszystkich łuków w programie —
  także dla otworów frezowanych helikalnie i ramp helikalnych.
  Gdy use_arcs = false, wszystkie łuki (narożniki, okrąg,
  otwory, rampy) są aproksymowane odcinkami G1 z krokiem
  arc_step.

- Otwory są ZAWSZE traktowane jako kieszeń wewnętrzna:
    - offset wewnętrzny (promień toru = Ø_otworu/2 - Ø_narzędzia/2)
    - kierunek CCW
  Dotyczy to także otworów, gdy side konturu = outside.

- Rampa helikalna po ścieżce (i helical otworów) wymaga od
  sterownika obsługi G2/G3 z interpolacją Z (LinuxCNC, Mach3,
  Fanuc). Jeśli Twój sterownik tego nie obsługuje — ustaw
  use_arcs = false; program wygeneruje aproksymację G1 (wolniej,
  ale uniwersalnie).

- Parametr ramp_length na bardzo krótkim konturze (P < 2*ramp)
  nie spowoduje błędu — rampy automatycznie rozciągną się na
  połowę obwodu i spotkają się dokładnie w środku.

----------------------------------------------------------------
11. ROZWIĄZYWANIE PROBLEMÓW
----------------------------------------------------------------

"Brak kształtu i otworów"          -> podaj shape z wierzchołkami
                                      albo co najmniej jeden hole

"Zaokrąglenie mniejsze niż R narzędzia"
                                   -> zmniejsz tool_diameter
                                      lub zwiększ promień w corner

"Nieznany kształt: xxx"            -> dozwolone: circle, polygon, rectangle

"Nie udało się wczytać pliku"      -> plik musi być tekstowy UTF-8

Rysunek na kanwie "pusty"          -> sprawdź, czy wierzchołki mają
                                      sensowne współrzędne

Otwór pominięty z ostrzeżeniem     -> narzędzie >= otwór; użyj
                                      mniejszego freza do helical

G-code "dziwnie zakrzywiony"       -> sprawdź direction i side;
                                      dla kieszeni: inside + CW (climb)
                                      lub inside + CCW (conventional)

Dwell nie pojawia się w kodzie      -> sprawdź, czy opóźnienie > 0
                                      i dwell_cmd nie jest puste

Komenda ON/OFF nie pojawia się      -> sprawdź, czy pole nie jest
                                      puste (dla mill są opcjonalne)

Nowa linia w komendzie nie działa  -> użyj \n (dwa znaki: \ i n),
                                      nie realnego Entera. W oknie
                                      G-code MUSZĄ pojawić się dwie
                                      osobne linie.

Helical nadal używa G3 mimo       -> sprawdź, czy use_arcs = false
use_arcs = false                     jest podane w danych. Odśwież
                                     (przycisk Generuj).

Zbyt mało/dużo linii G-code        -> dostosuj arc_step. Mniejsza
                                      wartość = więcej odcinków =
                                      gładsze łuki. Większa wartość =
                                      szybszy program.

----------------------------------------------------------------
12. TABELA WSZYSTKICH PARAMETRÓW
----------------------------------------------------------------

  Klucz                  Dotyczy    Wartości                    Opis
  ---------------------  ---------  --------------------------  ----------------------------
  mode                   oba        mill | laser                tryb pracy
  shape                  oba        circle | polygon | rect     kontur
  direction              oba        CCW | CW                    kierunek konturu
  side                   oba        outside | inside            offset konturu
  tool_diameter/kerf     oba        liczba mm                   Ø narzędzia / szczelina
  use_arcs               oba        true | false                G2/G3 czy aproksymacja G1
  arc_step               oba        liczba stopni               kąt odcinka aproksymacji
                                    (domyślnie 5.625)           (dotyczy narożników, okręgu,
                                                                 otworów, ramp, konturu)
  feed                   oba        liczba mm/min               posuw
  safe_z                 oba        liczba mm                   Z bezpieczne
  cut_z                  oba        liczba mm                   Z robocze / ogniskowa
  stock_top              mill       liczba mm                   Z wierzchni materiału
  cx, cy, radius         circle     liczba mm                   geometria okręgu
  vertex = x, y          polygon    liczba mm                   wierzchołki
  corner = i, t, p       polygon    sharp/round/chamfer         narożniki
  ramp                   mill       none/linear/helical         tryb rampy
  ramp_length            mill+lin   liczba mm                   długość wejścia i wyjścia
  ramp_pitch             mill+hel   liczba mm/obr               skok rampy helikalnej
  ramp_clearance         mill       liczba mm                   ile nad materiałem start
  hole_mode              mill       drill | helical             domyślny tryb otworów
  peck_depth             mill+drill liczba mm                   głębokość pojedynczego wejścia
  hole = x,y,Ø,g[,t]     oba        mm, mm, mm, mm, tryb        definicja otworu
  laser_power/power      laser      0..1000                     moc S
  laser_passes/passes    laser      >= 1                        liczba przejść
  laser_mode             laser      M3 | M4                     tylko dla domyślnej ON
  laser_on_cmd           laser      szablon z {power}            komenda włączenia lasera
  laser_off_cmd          laser      szablon                     komenda wyłączenia lasera
  laser_on_delay         laser      liczba ms                   opóźnienie po ON
  laser_off_delay        laser      liczba ms                   opóźnienie po OFF
  spindle_on_cmd         mill       szablon z {rpm}             komenda włączenia wrzeciona
  spindle_off_cmd        mill       szablon                     komenda wyłączenia wrzeciona
  spindle_rpm            mill       liczba                      obroty do {rpm}
  spindle_on_delay       mill       liczba ms                   opóźnienie po ON
  spindle_off_delay      mill       liczba ms                   opóźnienie po OFF
  dwell_cmd              oba        szablon z {sec} / {ms}      komenda opóźnienia ruchu

================================================================
Koniec instrukcji.
================================================================