{
To do list:
___________

1. pridat sekvenci mereni do NC kodu - viz email z 3. 5. 2021
  %001 (CncProgram)
  N10 LHoming (Zavolani homing funkce)
  N20	G90
  N30  G01 X0.0 Y0.0 F4000 (pocatek tisku, k nemu se pricita offset zadany v programu)
  N40  M50 (zapnuti mereni offsetu pro posledni vrstu tisku a cekani na zapis do promenne)
  N50  G172 Z20+Z_CORR (k vzdalenosti 20 mm se jeste pricte offset)
  ....
  N100 G172 Z50+Z_CORR
  M30

-2. zobrazovani udaju a panel statistika - HOTOVO 11. 5. 2021
3. Zoom, zmena pohledu bez zmeny meritka, Zoom mesh - uplne chybi!
-4. kdyz neni vybran zadny objekt, smazat edit pole o poloze a rotaci - HOTOVO 11. 5. 2021
-5. optimalizace vice polygonu - upravit podminky razeni - VOLBA V PRARAMETRECH 3. 11. 2021
6. konrola prekryvani jednotlivych siti / automaticke pozicovani


}
// How do I render both the front and back face of the mesh?
// > Turn off FaceCulling (either in the Material or in the Viewer.Buffer).
// > You may also want to turn on two-sided lighting (Viewer.Buffer.ContextOptions)

unit Unit1;
{$WARN UNIT_PLATFORM OFF}

interface

uses
  Variants, Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Math, IniFiles, _Math, System.ImageList, System.UITypes, System.Types, SynEdit,
  Vcl.ImgList, Vcl.ExtCtrls, Vcl.ComCtrls, RealEdit, Vcl.Buttons, Vcl.ToolWin,
  GLS.BitmapFont, GLS.WindowsFont, GLS.Scene, GLS.FileSTL, GLS.HUDObjects, GLS.Objects,
  GLS.Graph, GLS.GeomObjects, GLS.Coordinates, GLS.BaseClasses, GLS.SceneViewer,
  GLS.VectorFileObjects, GLS.VectorTypes, GLS.Color, GLS.Material, GLS.VectorGeometry,
  GLS.GeometryBB, GLS.Context, Vcl.WinXCtrls, System.Actions, Vcl.ActnList,
  Vcl.ActnCtrls, Vcl.PlatformDefaultStyleActnCtrls, Vcl.ActnMan, Vcl.ActnMenus,
  Vcl.NumberBox, GLS.VectorLists, GLS.PersistentClasses;

const
  NULA_REAL = 1e-7;
  NULA_CNC  = 1e-3;
  OTEV_PROFIL = 0;
  UZAV_PROFIL = 1;
  
  SPIR_PROFIL = 4;
  SOUBOR_CFG  = 'Param.cfg';
  VYCH_PROFIL = 'vychoziprofil';
  NAST_PROGR  = 'nast_prg';
  // pomer mezi prumerem trysky a sirkou steny
  MAX_EXTR = 1.25;  // sirka steny muze byt MAX_EXTR krat vetsi, nez prumer trysky
  MIN_EXTR = 0.95;  // sirka steny muze byt MIN_EXTR krat mensi, nez prumer trysky
  nc_KOM_1  = '''';
  nc_KOM_2  = '/';
  nc_KOM_3  = '(';
  nc_KOM_3a = ')';
  nc_KOM_4  = ';';
  AutZmena: Boolean = True;

type
  TBod = record
    Nalezeno: Boolean;
    Index_I, Index_J: Integer;
    X, Y: Double;
  end;
  TBodXYZ = record
    X, Y, Z: Double;
  end;
  TBodXY = record
    X, Y: Double;
  end;
  TBodXYarray = array of TBodXY;
  TPolyline = record
    Delka: Double;
    Priznak: Integer;
    Body: TBodXYarray;
  end;
  TDataRezu = array of TPolyline;
  TStrojData = record
    X, Y, Z, R, I, J, E: Double;
    FceG: Integer;
    Pohyb, Extruze: Boolean;
  end;
  TSimData = record
    X, Y: Double;
  end;
  TTiskPar = record
    PosuvXY, PosuvZ, RychlXY, MinCas, KrokNC, Evel, Koment, Optimal, MerNvrst: Integer;
    TlVrstvy, PosunZ, ZdvihZ, Dmat, Dtrysky, TlSteny, NasMat, KoefMat, TolSTL, TolNC, KratkyXY: Double;
    Ezap, Evyp, EzNC, StartNC, KonecNC, Pripona, MerFunkce, MerVypoc: string;
    OsaE, Pomalu1, Spirala, NevypEvXY, NevypEvZ, PouzitE, RadkyNC, Mereni: Boolean;
    MinX, MinY, MaxX, MaxY, MaxZ, Rastr: Double;
    MerVyskaZ, MerCidloX, MerCidloY: Double;
    ProklObl, ParamIJ: Boolean;
    PomerUsec, MaxChybKr, MaxRadius: Double;
  end;
  TParSW = record
    Posun, Rotace, VyberMesh, PosunMesh: TShiftState;
    SmerZoom: Integer;
    OrtoProj, SystCesta: Boolean;
  end;
  TZoomMode = (zm_XY, zm_XZ, zm_YZ, zm_ISO, zm_ALL);
  ESpatnyFormatSTL = class(Exception);
  TLines = record
    L: TGLLines;
    Vrstva, G: Integer;
    Z, Delka: Double;
  end;
  TLSim = record
    L: TGLLines;
    G: Integer;
    Z: Double;
  end;
  TMesh = record
    DC_Mesh: TGLDummyCube;
    MeshObal: TGLLines;
    Mesh: TGLFreeForm;
    Soubor: string;
    PocTroj: Integer;
    MeshMin, MeshMax: TVector3f;
    Posun, Rotace, Meritko: TVector4f;
    Stavet, Start: Boolean;
  end;
  PMesh = ^TMesh;
  
type
  TForm1 = class(TForm)
    PageControl: TPageControl;
    TS_CAD: TTabSheet;
    P_Ikony: TPanel;
    TB_Ovl: TToolBar;
    TB_OtevriProj: TToolButton;
    OpenDialog: TOpenDialog;
    SaveDialog: TSaveDialog;
    PanelCisel: TPanel;
    Panel4: TPanel;
    L_Trojuhelniku: TLabel;
    Panel12: TPanel;
    L_VelX: TLabel;
    Panel13: TPanel;
    L_VelY: TLabel;
    InfoPanel: TPanel;
    Panel14: TPanel;
    L_VelZ: TLabel;
    GLScene: TGLScene;
    XYGrid: TGLXYZGrid;
    CameraCube: TGLDummyCube;
    GLLightSource: TGLLightSource;
    GLCamera: TGLCamera;
    TS_Nast3Dtisk: TTabSheet;
    ImageList_Ovl: TImageList;
    TB_UlozProj: TToolButton;
    P_Stat: TPanel;
    Panel2: TPanel;
    ModelCube: TGLDummyCube;
    DC_Prostor: TGLDummyCube;
    TargetCube: TGLDummyCube;
    GLArrowLineX: TGLArrowLine;
    GLArrowLineY: TGLArrowLine;
    GLArrowLineZ: TGLArrowLine;
    ImageList_Zobr: TImageList;
    TS_NastPrg: TTabSheet;
    Panel15: TPanel;
    Label14: TLabel;
    Label15: TLabel;
    Label21: TLabel;
    Label29: TLabel;
    Label30: TLabel;
    CB_Zoom: TCheckBox;
    CB_OrtoProjekce: TCheckBox;
    Label31: TLabel;
    Label32: TLabel;
    Panel17: TPanel;
    CB_Posun_L: TCheckBox;
    CB_Posun_M: TCheckBox;
    CB_Posun_R: TCheckBox;
    CB_Rotace_L: TCheckBox;
    CB_Rotace_M: TCheckBox;
    CB_Rotace_R: TCheckBox;
    CB_PosMesh_L: TCheckBox;
    CB_PosMesh_M: TCheckBox;
    CB_PosMesh_R: TCheckBox;
    Panel18: TPanel;
    CB_Posun_A: TCheckBox;
    CB_Posun_C: TCheckBox;
    CB_Posun_S: TCheckBox;
    CB_Rotace_A: TCheckBox;
    CB_Rotace_C: TCheckBox;
    CB_Rotace_S: TCheckBox;
    CB_PosMesh_A: TCheckBox;
    CB_PosMesh_C: TCheckBox;
    CB_PosMesh_S: TCheckBox;
    Label53: TLabel;
    Panel16: TPanel;
    Label57: TLabel;
    RB_AppData: TRadioButton;
    RB_CestaPrg: TRadioButton;
    Panel1: TPanel;
    Label44: TLabel;
    CB_VybMesh_L: TCheckBox;
    CB_VybMesh_M: TCheckBox;
    CB_VybMesh_R: TCheckBox;
    CB_VybMesh_A: TCheckBox;
    CB_VybMesh_C: TCheckBox;
    CB_VybMesh_S: TCheckBox;
    IL_Zamek: TImageList;
    ScrollBox: TScrollBox;
    Label12: TLabel;
    Label45: TLabel;
    SB_UlozPar: TSpeedButton;
    Label7: TLabel;
    Label8: TLabel;
    Label9: TLabel;
    Label10: TLabel;
    Label33: TLabel;
    Label34: TLabel;
    Label35: TLabel;
    Label36: TLabel;
    Label37: TLabel;
    Label38: TLabel;
    Label39: TLabel;
    Label40: TLabel;
    Label41: TLabel;
    Label42: TLabel;
    Label11: TLabel;
    Label13: TLabel;
    L_Uvod: TLabel;
    L_Zaver: TLabel;
    Label20: TLabel;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    Label16: TLabel;
    Label17: TLabel;
    Label18: TLabel;
    Label19: TLabel;
    Label24: TLabel;
    Label25: TLabel;
    Label58: TLabel;
    Label59: TLabel;
    Label26: TLabel;
    Label60: TLabel;
    Label61: TLabel;
    Label65: TLabel;
    CB_Profily: TComboBox;
    E_TolSTL: TRealEdit;
    E_TolNC: TRealEdit;
    CB_Pomalu1: TCheckBox;
    CB_Spirala: TCheckBox;
    E_Tloustka: TRealEdit;
    E_PosuvXY: TRealEdit;
    E_PosuvZ: TRealEdit;
    E_RychlXY: TRealEdit;
    E_ZdvihZ: TRealEdit;
    E_MinCas: TRealEdit;
    M_StartNC: TMemo;
    M_KonecNC: TMemo;
    E_MinX: TRealEdit;
    E_MinY: TRealEdit;
    E_MaxX: TRealEdit;
    E_MaxY: TRealEdit;
    E_MaxZ: TRealEdit;
    E_Rastr: TRealEdit;
    CB_RadkyNC: TCheckBox;
    E_KrokNC: TRealEdit;
    E_PriponaNC: TRealEdit;
    GroupBox1: TGroupBox;
    RB_Strednik: TRadioButton;
    RB_Zavorky: TRadioButton;
    RB_Apostrof: TRadioButton;
    RB_Lomeno: TRadioButton;
    CB_Mereni: TCheckBox;
    PC_Extruder: TPageControl;
    TS_E: TTabSheet;
    Label46: TLabel;
    Label50: TLabel;
    Label47: TLabel;
    Label52: TLabel;
    Label48: TLabel;
    Label51: TLabel;
    Label49: TLabel;
    Label43: TLabel;
    L_KoefMat: TLabel;
    E_Dmat: TRealEdit;
    E_Dtrysky: TRealEdit;
    E_TlSteny: TRealEdit;
    E_NasMat: TRealEdit;
    TS_XY: TTabSheet;
    Label22: TLabel;
    Label23: TLabel;
    Label27: TLabel;
    Label28: TLabel;
    E_Ezap: TRealEdit;
    E_Evyp: TRealEdit;
    E_EzNC: TRealEdit;
    E_Evel: TRealEdit;
    CB_EzNC: TCheckBox;
    CB_Extruder: TCheckBox;
    Label66: TLabel;
    E_MerVyskaZ: TRealEdit;
    Label67: TLabel;
    Label81: TLabel;
    E_MerCidloX: TRealEdit;
    Label82: TLabel;
    Label83: TLabel;
    E_MerCidloY: TRealEdit;
    Label84: TLabel;
    Label70: TLabel;
    E_MerNvrst: TRealEdit;
    Label68: TLabel;
    E_MerFunkce: TRealEdit;
    Label69: TLabel;
    E_MerVypoc: TRealEdit;
    I_MeshStatus: TImage;
    Label85: TLabel;
    Panel3: TPanel;
    RB_Opt_prejezdy: TRadioButton;
    RB_Opt_uzavrene1otrvrene2: TRadioButton;
    CB_NevypEvXY: TCheckBox;
    CB_NevypEvZ: TCheckBox;
    Label86: TLabel;
    E_KratkyXY: TRealEdit;
    Label87: TLabel;
    Label88: TLabel;
    E_PosunZ: TRealEdit;
    Label89: TLabel;
    CB_ProklObl: TCheckBox;
    Panel23: TPanel;
    RB_ParamIJ: TRadioButton;
    RB_ParamR: TRadioButton;
    Label90: TLabel;
    E_MaxChybKr: TRealEdit;
    Label91: TLabel;
    Label92: TLabel;
    E_MaxRadius: TRealEdit;
    Label93: TLabel;
    Label94: TLabel;
    E_PomerUsec: TRealEdit;
    Panel19: TPanel;
    Panel24: TPanel;
    ScrollBox2: TScrollBox;
    Panel20: TPanel;
    Label62: TLabel;
    LB_Mesh: TListBox;
    Panel22: TPanel;
    Label78: TLabel;
    Label79: TLabel;
    Label80: TLabel;
    Label71: TLabel;
    Label72: TLabel;
    Label77: TLabel;
    Bevel1: TBevel;
    E_PolX: TRealEdit;
    E_PolY: TRealEdit;
    E_PolZ: TRealEdit;
    E_RotX: TRealEdit;
    E_RotY: TRealEdit;
    E_RotZ: TRealEdit;
    E_MerX: TRealEdit;
    E_MerY: TRealEdit;
    E_MerZ: TRealEdit;
    CB_PrizpPoloze: TCheckBox;
    P_Rezy: TPanel;
    Label63: TLabel;
    Label64: TLabel;
    RB_RezyVse: TRadioButton;
    RB_JedenRez: TRadioButton;
    RB_RezyOdDo: TRadioButton;
    E_VrstvaOd: TRealEdit;
    TB_VrstvaOd: TTrackBar;
    E_VrstvaDo: TRealEdit;
    TB_VrstvaDo: TTrackBar;
    GLSceneViewer: TGLSceneViewer;
    P_NC_kod: TPanel;
    P_VelSJ: TPanel;
    M_Gkod: TSynEdit;
    ToolBar1: TToolBar;
    TB_OtevriSTL: TToolButton;
    TB_SmazSTL: TToolButton;
    ToolButton1: TToolButton;
    TB_STLzobr: TToolButton;
    TB_STLskryj: TToolButton;
    ToolButton3: TToolButton;
    TB_STLstav: TToolButton;
    TB_STLnestav: TToolButton;
    ToolButton8: TToolButton;
    TB_STLrezy: TToolButton;
    TB_NCkod: TToolButton;
    Panel6: TPanel;
    L_NCkod: TLabel;
    TB_NC_Atena: TToolButton;
    P_Simulace: TPanel;
    Label73: TLabel;
    Shape1: TShape;
    Label74: TLabel;
    L_Xsim: TLabel;
    Shape2: TShape;
    Label75: TLabel;
    L_Ysim: TLabel;
    Shape3: TShape;
    Label76: TLabel;
    L_Zsim: TLabel;
    Label54: TLabel;
    Label55: TLabel;
    Label56: TLabel;
    TB_Rychlost: TTrackBar;
    CB_CelaVrstva: TCheckBox;
    ToolBar_Sim: TToolBar;
    TB_Sim: TToolButton;
    TB_SimBlok: TToolButton;
    TB_SimStop: TToolButton;
    TB_SimZpet: TToolButton;
    ToolButton2: TToolButton;
    ToolBar2: TToolBar;
    TB_Info: TToolButton;
    ToolButton4: TToolButton;
    ToolButton6: TToolButton;
    ToolBar3: TToolBar;
    TB_Zamek: TToolButton;
    SB_Zamek: TSpeedButton;
    TB_CAD: TToolBar;
    TB_ZoomVse: TToolButton;
    TB_PohlXY: TToolButton;
    TB_PohlXZ: TToolButton;
    TB_PohlYZ: TToolButton;
    TB_PohlISO: TToolButton;
    ToolButton7: TToolButton;
    procedure GLSceneViewerMouseDown(Sender: TObject;
      Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure GLSceneViewerMouseMove(Sender: TObject; Shift: TShiftState;
      X, Y: Integer);
    procedure FormMouseWheel(Sender: TObject; Shift: TShiftState;
      WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
    procedure TB_RezyClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormCreate(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure CB_ProfilyClick(Sender: TObject);
    procedure E_TiskParChange(Sender: TObject);
    procedure TB_OtevriSTLClick(Sender: TObject);
    procedure TB_InfoClick(Sender: TObject);
    procedure SB_UlozParClick(Sender: TObject);
    procedure TB_NCkod1Click(Sender: TObject);
    procedure TB_SimClick(Sender: TObject);
    procedure TB_SimBlokClick(Sender: TObject);
    procedure TB_SimStopClick(Sender: TObject);
    procedure TB_SimZpetClick(Sender: TObject);
    procedure M_GkodSpecialLineColors(Sender: TObject; Line: Integer;
      var Special: Boolean; var FG, BG: TColor);
    procedure P_StatClick(Sender: TObject);
    procedure CB_ExtruderClick(Sender: TObject);
    procedure CB_OrtoProjekceClick(Sender: TObject);
    procedure TB_ZoomVseClick(Sender: TObject);
    procedure TB_PohlXYClick(Sender: TObject);
    procedure TB_PohlXZClick(Sender: TObject);
    procedure TB_PohlYZClick(Sender: TObject);
    procedure TB_PohlISOClick(Sender: TObject);
    procedure CB_CADmanClick(Sender: TObject);
    procedure CB_ZoomClick(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure RB_AppDataClick(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure LB_MeshClick(Sender: TObject);
    procedure TB_SmazSTLClick(Sender: TObject);
    procedure E_PozMeshChange(Sender: TObject);
    procedure TB_STLzobrClick(Sender: TObject);
    procedure TB_STLskryjClick(Sender: TObject);
    procedure CB_PrizpPolozeClick(Sender: TObject);
    procedure RB_RezyVseClick(Sender: TObject);
    procedure RB_JedenRezClick(Sender: TObject);
    procedure RB_RezyOdDoClick(Sender: TObject);
    procedure E_VrstvaOdDoChange(Sender: TObject);
    procedure TB_VrstvaOdChange(Sender: TObject);
    procedure TB_VrstvaDoChange(Sender: TObject);
    procedure LB_MeshDrawItem(Control: TWinControl; Index: Integer;
      Rect: TRect; State: TOwnerDrawState);
    procedure TB_STLstavClick(Sender: TObject);
    procedure TB_STLnestavClick(Sender: TObject);
    procedure TB_OtevriProjClick(Sender: TObject);
    procedure TB_UlozProjClick(Sender: TObject);
    procedure E_PomerUsecExit(Sender: TObject);
    procedure P_VelSJMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure TB_NC_AtenaClick(Sender: TObject);
    procedure TB_ZamekClick(Sender: TObject);
  private
    { Private declarations }
    FS: TFormatSettings;
    Mdx, Mdy: Integer;
    MeshList: TList;
    Lines: array of TLines;
    L_Sim: array of TLSim;
    // Posunuti nuloveho bodu
    // Další lokální proměnné a procedury
    ParSW: TParSW;
    // simulace NC kodu
    TiskPar: TTiskPar;
    SimStop: Boolean;
    PrgMonIndex, MIndex: Integer;
    ExtrZ: Double;
    SimStroje: TStrojData;
    procedure ZrusDataRezu(var RezZ: TDataRezu);
    procedure ZapisRez(const B1, B2: TBodXY; var RezZ: TDataRezu);
    function VypocitejRez(const Vrstva: Integer; var RezZ: TDataRezu; const Start: Boolean): Boolean;
    function HledejBodMinVzd(const X, Y: Double; const RezZ: TDataRezu; var Vzd: Double): TBod;
    procedure OtocPolygon(var Data: TBodXYarray);
    function VypocitejObsah(const Data: TBodXYarray): Double; overload;
    function VypocitejObsah(const Data: TMatice): Double; overload;
    procedure ZmenPocDleMinVzd(var Uzavr, Otevr: TBodXYarray; const ZacX, ZacY: Double);
    procedure ZmenVychBodDleMinVzd(var Uzavr: TBodXYarray; const X, Y: Double);
    function VzdBoduOdPrimky(const Ax, Ay, Bx, By: Double; const Bodx, Body: Double): Double;
    procedure KopirujaDataRezu(var Co, Kam: TDataRezu);
    procedure SestavPolygony(var RezZ: TDataRezu; const ZacX, ZacY: Double);
    procedure OptimalizujData(var RezZ: TDataRezu; const ZacX, ZacY: Double);
    procedure NactiTiskParam(var Param: TTiskPar; const Akce: Boolean);
    procedure NastavEditTiskPar(const Reset: Boolean);
    function  NactiCisloI(const Radek: string; var Pozice: Integer): Integer;
    function  NactiCisloR(const Radek: string; var Pozice: Integer): Double;
    function  SmerovyUhel(const Dx, Dy: Double; const Smer: Integer): Double;
    procedure ObloukNaPolygon(const PocX, PocY, KonX, KonY: Double; const I, J, R, Z: Double; const FceG: Integer);
    procedure ProvedRadek(const Radek: string);
    function  OdstranKoment(const Radek: string): string;
    procedure SmazSimulaci;
    procedure NastavSimulaci;
    procedure ResetSimulace;
    function  CfgSoubor(const JenSystem: Boolean): string;
    procedure NovaMesh;
    procedure ZrusMesh(const M: Integer);
    procedure NactiSoubor(const JmenoSouboru: string);
    procedure NactiProjekt(const JmenoSouboru: string);
    procedure Cekej(const Ms: Int64);
    procedure Zoom(RootObject: TGLBaseSceneObject; Mode: TZoomMode);
    procedure WMDropFiles(var Msg: TMessage); message WM_DROPFILES;
    function  IntToShiftState(const I: Integer): TShiftState;
    function  ShiftStateToInt(const S: TShiftState): Integer;
    procedure NastavCADman;
    procedure UrciMinMax(const Sit: PMesh; var MinM, MaxM: TVector4f; const Lokalne, Komplet, MeshMinMax: Boolean);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

uses O_Prog, Unit2, ShellAPI;

{$R *.DFM}

procedure TForm1.ZrusDataRezu(var RezZ: TDataRezu);
var
  I: Integer;
begin
  I := 0;
  while I < Length(RezZ) do
  begin
    Finalize(RezZ[I].Body);
    Inc(I);
  end;
  Finalize(RezZ);
end;

procedure TForm1.ZapisRez(const B1, B2: TBodXY; var RezZ: TDataRezu);
var
  I, J: Integer;
  Exist: Boolean;
begin
  Exist := False;
  // kontrola, zda uz dany rez v polygonu neexistuje
  I := 0;
  while (I < Length(RezZ)) and not(Exist) do
  begin
    J := 1;
    while (J < Length(RezZ[I].Body)) and not(Exist) do
    begin
      Exist := ((Abs(B1.X - RezZ[I].Body[J-1].X) < NULA_REAL) and (Abs(B1.Y - RezZ[I].Body[J-1].Y) < NULA_REAL) and
                (Abs(B2.X - RezZ[I].Body[J].X)   < NULA_REAL) and (Abs(B2.Y - RezZ[I].Body[J].Y)   < NULA_REAL)) or
               ((Abs(B2.X - RezZ[I].Body[J-1].X) < NULA_REAL) and (Abs(B2.Y - RezZ[I].Body[J-1].Y) < NULA_REAL) and
                (Abs(B1.X - RezZ[I].Body[J].X)   < NULA_REAL) and (Abs(B1.Y - RezZ[I].Body[J].Y)   < NULA_REAL));
      Inc(J, 2);
    end;
    Inc(I);
  end;
  // pokud neexistuje, uloz data rezu
  if not(Exist) then
  begin
    I := Length(RezZ);
    SetLength(RezZ, I + 1);
    J := Length(RezZ[I].Body);
    SetLength(RezZ[I].Body, J + 2);
    RezZ[I].Body[J] := B1;
    RezZ[I].Body[J + 1] := B2;
  end;
end;

function TForm1.VypocitejRez(const Vrstva: Integer; var RezZ: TDataRezu; const Start: Boolean): Boolean;
var
  I, J, K, L, M, PocBodu, PocDat: Integer;
  Z: Double;
  Poradi: array[0..2] of Integer;
  V: array[0..2] of TVector3f;
  MinM, MaxM: TVector4f;
  B: array[0..2] of TBodXYZ;
  P: array[0..2] of TBodXY;
  Sit: PMesh;
begin
  Result := False;
  ZrusDataRezu(RezZ);
  try
    for M := 0 to MeshList.Count - 1 do
    begin
      Sit := MeshList.Items[M];
      if Sit^.Stavet and (Start = Sit^.Start) then
      begin
        UrciMinMax(Sit, MinM, MaxM, True, False, False);
        Z := MinM.Z + TiskPar.TlVrstvy * Vrstva;
        if Z > MaxM.Z then
          Continue;
        for I := 0 to Sit^.Mesh.MeshObjects.Count - 1 do
        begin
          J := 0;
          if Frac(Sit^.Mesh.MeshObjects[I].Vertices.Count / 3) > 0 then
            raise ESpatnyFormatSTL.Create('Špatný formát STL dat - počet bodů není dělitelný třemi.');
          // vlastni vypocet rezu
          while J < Sit^.Mesh.MeshObjects[I].Vertices.Count do
          begin
            V[0] := Sit^.Mesh.LocalToAbsolute(Sit^.Mesh.MeshObjects[I].Vertices.Items[J]);
            Inc(J);
            V[1] := Sit^.Mesh.LocalToAbsolute(Sit^.Mesh.MeshObjects[I].Vertices.Items[J]);
            Inc(J);
            V[2] := Sit^.Mesh.LocalToAbsolute(Sit^.Mesh.MeshObjects[I].Vertices.Items[J]);
            Inc(J);
            // serazeni vrcholu trojuhelnika od minimalni Z-souradnice po maximalni
            if (V[0].Z <= V[1].Z) and (V[0].Z <= V[2].Z) then Poradi[0] := 1;
            if (V[1].Z <  V[0].Z) and (V[1].Z <= V[2].Z) then Poradi[0] := 2;
            if (V[2].Z <  V[0].Z) and (V[2].Z <  V[1].Z) then Poradi[0] := 4;
            if (V[0].Z >= V[1].Z) and (V[0].Z >= V[2].Z) then Poradi[2] := 1;
            if (V[1].Z >  V[0].Z) and (V[1].Z >= V[2].Z) then Poradi[2] := 2;
            if (V[2].Z >  V[0].Z) and (V[2].Z >  V[1].Z) then Poradi[2] := 4;
            if (Abs(V[0].Z -  V[1].Z) < NULA_REAL) and (Abs(V[0].Z - V[2].Z) < NULA_REAL) then
              Continue;  // vsechny body lezi ve stejne urovni, neni treba se jimi zabyvat
            Poradi[1] := 7 - Poradi[0] - Poradi[2];
            for K := 0 to 2 do
            begin
              if Poradi[K] = 1 then Poradi[K] := 0;
              if Poradi[K] = 2 then Poradi[K] := 1;
              if Poradi[K] = 4 then Poradi[K] := 2;
            end;
            for K := 0 to 2 do
            begin
              B[K].X := V[Poradi[K]].X;
              B[K].Y := V[Poradi[K]].Y;
              B[K].Z := V[Poradi[K]].Z;
            end;
            // pripady kdy je vrchol B0 pod rovinou rezu a B2 nad rovinou rezu, body B0 a B2 nejsou v rovine rezu
            if ((B[0].Z - Z) < 0) and (Abs(B[0].Z - Z) > NULA_REAL) and ((B[2].Z - Z) > 0) and (Abs(B[2].Z - Z) > NULA_REAL) then
            begin
              // rovina rezu protina trojuhelnik, vrcholy jsou mimo rez - vypocet pruseciku
              // prusecik B0-B2
              P[0].X := (B[0].X + (Z - B[0].Z) * (B[2].X - B[0].X) / (B[2].Z - B[0].Z));
              P[0].Y := (B[0].Y + (Z - B[0].Z) * (B[2].Y - B[0].Y) / (B[2].Z - B[0].Z));
              K := 1;
              // zjisteni, ktera dve ze spojnic B0-B1, B1-B2 Z souradnic protina rez
              if ((B[1].Z - Z) > 0) and (Abs(B[1].Z - Z) > NULA_REAL) then
              begin
                // prusecik B0-B1
                P[K].X := (B[0].X + (Z - B[0].Z) * (B[1].X - B[0].X) / (B[1].Z - B[0].Z));
                P[K].Y := (B[0].Y + (Z - B[0].Z) * (B[1].Y - B[0].Y) / (B[1].Z - B[0].Z));
                Inc(K);
              end;
              if ((B[1].Z - Z) < 0) and (Abs(B[1].Z - Z) > NULA_REAL) then
              begin
                // prusecik B1-B2
                if K > 1 then
                  raise ESpatnyFormatSTL.Create('Chyba při výpočtu řezů');
                P[K].X := (B[1].X + (Z - B[1].Z) * (B[2].X - B[1].X) / (B[2].Z - B[1].Z));
                P[K].Y := (B[1].Y + (Z - B[1].Z) * (B[2].Y - B[1].Y) / (B[2].Z - B[1].Z));
                Inc(K);
              end;
              if Abs(B[1].Z - Z) < NULA_REAL then
              begin
                // vrchol B1
                if K > 1 then
                  raise ESpatnyFormatSTL.Create('Chyba při výpočtu řezů');
                P[K].X := B[1].X;
                P[K].Y := B[1].Y;
                Inc(K);
              end;
              // kontrola pred vlastnim zapisem, zda jsou nalezene body ruzne
              if (K = 2) and (Abs(P[0].X - P[1].X) < NULA_REAL) and (Abs(P[0].Y - P[1].Y) < NULA_REAL) then
                K := 0;
              // zapsani nalezenych bodu do neoptimalizovanych dat
              if K = 2 then
              begin
                PocDat := Length(RezZ);
                SetLength(RezZ, PocDat + 1);
                PocBodu := Length(RezZ[PocDat].Body);
                SetLength(RezZ[PocDat].Body, PocBodu + K);
                for L := 0 to K - 1 do
                begin
                  RezZ[PocDat].Body[PocBodu + L].X := P[L].X;
                  RezZ[PocDat].Body[PocBodu + L].Y := P[L].Y;
                end;
              end;
            end;
            // pripady, kdy vrcholy B0, B1 nebo B1, B2 lezi v rovine rezu
            P[0].X := B[0].X;   P[0].Y := B[0].Y;
            P[1].X := B[1].X;   P[1].Y := B[1].Y;
            P[2].X := B[2].X;   P[2].Y := B[2].Y;
            // nove se zapisuje jen hrana toho trojuhelniku, ktery ma treti vrchol pod rovinou rezu (tj. B[0] < Z)
            // z duvodu moznych duplicit hran, pokud je na siti chyba (viz napr. Rotunda, Z = 50)
///            if (Abs(B[0].Z - Z) < NULA_REAL) and (Abs(B[1].Z - Z) < NULA_REAL) then
///              ZapisRez(P[0], P[1], Round(Z * KOEF), RezZ);
            if (Abs(B[1].Z - Z) < NULA_REAL) and (Abs(B[2].Z - Z) < NULA_REAL) then
              ZapisRez(P[1], P[2], RezZ);
          end;
        end;
      end;
    end;
    Result := True;
  except
    on E: Exception do ShowMessage(E.Message)
    else raise;
  end;
end;

function TForm1.HledejBodMinVzd(const X, Y: Double; const RezZ: TDataRezu; var Vzd: Double): TBod;
var
  I, J, Pocet: Integer;
  Min: Double;
begin
  Result.Nalezeno := False;
  // Priprava vyhledavani
  I := 0;
  Vzd := High(Int64);
  while I < Length(RezZ) do
  begin
    Pocet := Length(RezZ[I].Body);
    if Pocet > 0 then
    begin
      J := 0;
      // pracuje se pouze s prvnim a poslednim bodem polygonu
      while J < Pocet do
      begin
        Min := Hypot(X - RezZ[I].Body[J].X, Y - RezZ[I].Body[J].Y);
        if Min <= Vzd then
        begin
          Vzd := Min;
          Result.Nalezeno := True;
          Result.Index_I := I;
          Result.Index_J := J;
          Result.X := RezZ[I].Body[J].X;
          Result.Y := RezZ[I].Body[J].Y;
        end;
        J := J + Pocet - 1;
      end;
    end;
    Inc(I);
  end;
end;

procedure TForm1.OtocPolygon(var Data: TBodXYarray);
var
  I: Integer;
  Zaloha: TBodXYarray;
begin
  SetLength(Zaloha, Length(Data));
  for I := 0 to Length(Data) - 1 do
    Zaloha[Length(Data) - 1 - I] := Data[I];
  for I := 0 to Length(Data) - 1 do
    Data[I] := Zaloha[I];
  Finalize(Zaloha);
end;

function TForm1.VypocitejObsah(const Data: TBodXYarray): Double;
var
  I, J, K: Integer;
begin
  Result := 0;
  for I := 0 to Length(Data) - 1 do
  begin
    J := I - 1;
    K := I + 1;
    if J < 0 then J := Length(Data) - 1;
    if K >= Length(Data) then K := 0;
    Result := Result + Data[I].X * (Data[K].Y - Data[J].Y);
  end;
  Result := Result / 2;
end;

function TForm1.VypocitejObsah(const Data: TMatice): Double;
var
  I, J, K: Integer;
begin
  Result := 0;
  for I := 0 to Length(Data) - 1 do
  begin
    J := I - 1;
    K := I + 1;
    if J < 0 then J := Length(Data) - 1;
    if K >= Length(Data) then K := 0;
    Result := Result + Data[I, 0] * (Data[K, 1] - Data[J, 1]);
  end;
  Result := Result / 2;
end;

procedure TForm1.ZmenPocDleMinVzd(var Uzavr, Otevr: TBodXYarray; const ZacX, ZacY: Double);
var
  I, J, Index: Integer;
  Min, Vzd: Double;
  Zaloha: TBodXYarray;
begin
  // rozhodnuti, ktery konec otevreneho polygony je blize vychozimu bodu
  // a pripadne otoceni otevreneho polygonu
  if Hypot(ZacX - Otevr[0].X, ZacY - Otevr[0].Y) > Hypot(ZacX - Otevr[Length(Otevr) - 1].X, ZacY - Otevr[Length(Otevr) - 1].Y) then
    OtocPolygon(Otevr);
  // Priprava vyhledavani
  Index := 0;
  Vzd := High(Int64);  //Hypot(Plt.Max_X - Plt.Min_X, Plt.Max_Y - Plt.Min_Y);
  for I := 0 to Length(Uzavr) - 1 do
  begin
    Min := Hypot(Uzavr[I].X - Otevr[0].X, Uzavr[I].Y - Otevr[0].Y);
    if Min <= Vzd then
    begin
      Vzd   := Min;
      Index := I;  // index nejblizsiho bodu v uzavrenem polygony k nekteremu konci otevreneho polygonu
    end;
  end;
  J := 0;
  SetLength(Zaloha, Length(Uzavr));
  for I := Index to Length(Uzavr) - 1 do
  begin
    Zaloha[J] := Uzavr[I];
    Inc(J);
  end;
  for I := 1 to Index do  // vynechani prvniho bodu (je stejny, jako posledni) a uzavreni polygonu
  begin
    Zaloha[J] := Uzavr[I];
    Inc(J);
  end;
  for I := 0 to J - 1 do
    Uzavr[I] := Zaloha[I];
  Finalize(Zaloha);
end;

procedure TForm1.ZmenVychBodDleMinVzd(var Uzavr: TBodXYarray; const X, Y: Double);
var
  I, J, Index: Integer;
  Min, Vzd: Double;
  Zaloha: TBodXYarray;
begin
  Index := 0;
  Vzd := High(Int64);  //Hypot(Plt.Max_X - Plt.Min_X, Plt.Max_Y - Plt.Min_Y);
  for I := 0 to Length(Uzavr) - 2 do  // posledni bod uzavreneho polygonu je stejny jako prvni
  begin
    Min := Hypot(Uzavr[I].X - X, Uzavr[I].Y - Y);
    if Min <= Vzd then
    begin
      Vzd   := Min;
      Index := I;  // index nejblizsiho bodu v uzavrenem polygony k nekteremu konci otevreneho polygonu
    end;
  end;
  if Index > 0 then
  begin
    J := 0;
    SetLength(Zaloha, Length(Uzavr));
    for I := Index to Length(Uzavr) - 1 do
    begin
      Zaloha[J] := Uzavr[I];
      Inc(J);
    end;
    for I := 1 to Index do  // vynechani prvniho bodu (je stejny, jako posledni) a uzavreni polygonu
    begin
      Zaloha[J] := Uzavr[I];
      Inc(J);
    end;
    for I := 0 to J - 1 do
      Uzavr[I] := Zaloha[I];
    Finalize(Zaloha);
  end;
end;

function Tform1.VzdBoduOdPrimky(const Ax, Ay, Bx, By: Double; const Bodx, Body: Double): Double;
var
  Pa, Pb, Pc: Double;
begin
  Pa := Ay - By;
  Pb := Bx - Ax;
  Pc := Ax * By - Bx * Ay;
  if (Abs(Pa) < NULA_REAL) and (Abs(Pb) < NULA_REAL) then
    Result := 0
  else
    Result := Abs((Pa * Bodx + Pb * Body + Pc) / Hypot(Pa, Pb));
end;

procedure TForm1.KopirujaDataRezu(var Co, Kam: TDataRezu);
var
  I, J: Integer;
begin
  SetLength(Kam, Length(Co));
  for I := 0 to Length(Co) - 1 do
  begin
    Kam[I].Delka := Co[I].Delka;
    Kam[I].Priznak := Co[I].Priznak;
    SetLength(Kam[I].Body, Length(Co[I].Body));
    for J := 0 to Length(Co[I].Body) - 1 do
    begin
      Kam[I].Body[J] := Co[I].Body[J];
    end;
  end;
end;

procedure TForm1.SestavPolygony(var RezZ: TDataRezu; const ZacX, ZacY: Double);
const
  NULA_TOL = 1e-5;  // pro optimalizaci polygonu je vhodne pouzit mensi toleraci, nez standardne brana realna nula
var
  I, J, PocelPolyline, PocetDat, Zac: Integer;
  TolSTL, Vzd, Vzd2, Obsah: Double;
  Bod: TBod;
  OptData: TDataRezu;
begin
  // pripadne zruseni optimalizovanych dat
  ZrusDataRezu(OptData);
  // nalezeni pocatku
  Bod := HledejBodMinVzd(ZacX, ZacY, RezZ, Vzd);
  // inicializace optimalizace, zatim bez prirazeni urovne Z
  TolSTL := TiskPar.TolSTL;
  Vzd := TolSTL + 0.0001;
  PocelPolyline := 0;
  // optimalizace dat podle vzdalenosti k pocatku
  while Bod.Nalezeno do
  begin
    if Vzd < TolSTL then
    begin
      // novy polygon navazuje na puvodni polygon
      PocetDat := Length(RezZ[Bod.Index_I].Body);
      J := Length(OptData[PocelPolyline - 1].Body);
      Zac := 0;
      if J > 0 then
        Zac := 1;
      if Bod.Index_J = 0 then
      begin
        SetLength(OptData[PocelPolyline - 1].Body, J + PocetDat - Zac);
        for I := Zac to PocetDat - 1 do
        begin
          OptData[PocelPolyline - 1].Body[J] := RezZ[Bod.Index_I].Body[I];
          Inc(J);
        end;
        Finalize(RezZ[Bod.Index_I]);
      end else
      begin
        if Bod.Index_J = (PocetDat - 1) then
        begin
          // hledany bod je na konci polygonu, musi se otocit jeho smer
          SetLength(OptData[PocelPolyline - 1].Body, J + PocetDat - Zac);
          for I := PocetDat - 1 - Zac downto 0 do
          begin
            OptData[PocelPolyline - 1].Body[J] := RezZ[Bod.Index_I].Body[I];
            Inc(J);
          end;
          Finalize(RezZ[Bod.Index_I]);
        end;
      end;
      Bod := HledejBodMinVzd(OptData[PocelPolyline - 1].Body[J - 1].X, OptData[PocelPolyline - 1].Body[J - 1].Y, RezZ, Vzd);
      if Vzd > TolSTL then
      begin
        // polygon je hotovy, ale nemusi byt optimalizovany vuci pozadovanemu pocatku
        // kontrola, zda se jedna o uzavreny profil
        if Hypot(OptData[PocelPolyline - 1].Body[0].X - OptData[PocelPolyline - 1].Body[J - 1].X,
                 OptData[PocelPolyline - 1].Body[0].Y - OptData[PocelPolyline - 1].Body[J - 1].Y) < TolSTL then
        begin
          OptData[PocelPolyline - 1].Body[J - 1].X := OptData[PocelPolyline - 1].Body[0].X;
          OptData[PocelPolyline - 1].Body[J - 1].Y := OptData[PocelPolyline - 1].Body[0].Y;
          // profil je uzavreny, bude nasledne mozne optimalizovat pocatek
          OptData[PocelPolyline - 1].Priznak := UZAV_PROFIL;
          // kontrola smeru uzavreneho polygonu
          Obsah := VypocitejObsah(OptData[PocelPolyline - 1].Body);
          if Obsah < 0 then
            OtocPolygon(OptData[PocelPolyline - 1].Body);
          if PocelPolyline > 1 then
          begin
            J := Length(OptData[PocelPolyline - 2].Body) - 1;
            ZmenVychBodDleMinVzd(OptData[PocelPolyline - 1].Body, OptData[PocelPolyline - 2].Body[J].X, OptData[PocelPolyline - 2].Body[J].Y);
          end;
        end else begin
          // polygon je otevreny, ale navazovani muze existovat z druhe strany
          Bod := HledejBodMinVzd(OptData[PocelPolyline - 1].Body[0].X, OptData[PocelPolyline - 1].Body[0].Y, RezZ, Vzd);
          if Vzd < TolSTL then
          begin
            // navazovani existuje z druhe strany - po otoceni smeru polygonu se muze pokracovat v jeho optimalizaci
            OtocPolygon(OptData[PocelPolyline - 1].Body);
          end else begin
            // pripadne otoceni predchoziho otevreneho polygonu dle vzdalenosti koncu
            if (PocelPolyline > 0) and (OptData[PocelPolyline - 1].Priznak = OTEV_PROFIL) then
            begin
              if PocelPolyline = 1 then
              begin
                // vyhodnocuje se k zadanemu bodu procedury
                Vzd  := Hypot(OptData[PocelPolyline - 1].Body[0].X - ZacX,
                              OptData[PocelPolyline - 1].Body[0].Y - ZacY);
                Vzd2 := Hypot(OptData[PocelPolyline - 1].Body[J - 1].X - ZacX,
                              OptData[PocelPolyline - 1].Body[J - 1].Y - ZacY);
                if Vzd2 < Vzd then
                  OtocPolygon(OptData[PocelPolyline - 1].Body);
              end else begin
                // vyhodnocuje se ke konci predchoziho polygonu
                PocetDat := Length(OptData[PocelPolyline - 2].Body);
                Vzd  := Hypot(OptData[PocelPolyline - 1].Body[0].X - OptData[PocelPolyline - 2].Body[PocetDat - 1].X,
                              OptData[PocelPolyline - 1].Body[0].Y - OptData[PocelPolyline - 2].Body[PocetDat - 1].Y);
                Vzd2 := Hypot(OptData[PocelPolyline - 1].Body[J - 1].X - OptData[PocelPolyline - 2].Body[PocetDat - 1].X,
                              OptData[PocelPolyline - 1].Body[J - 1].Y - OptData[PocelPolyline - 2].Body[PocetDat - 1].Y);
                if Vzd2 < Vzd then
                  OtocPolygon(OptData[PocelPolyline - 1].Body);
              end;
            end;
            // navazovani neexistuje, bude se tvorit novy polygon od nejblizsiho bodu, kde skoncil predchozi polygon
            Bod := HledejBodMinVzd(OptData[PocelPolyline - 1].Body[J - 1].X, OptData[PocelPolyline - 1].Body[J - 1].Y, RezZ, Vzd);
          end;
        end;
      end;
    end else begin
      // tvori se novy polygon
      Inc(PocelPolyline);
      SetLength(OptData, PocelPolyline);
      OptData[PocelPolyline - 1].Priznak  := OTEV_PROFIL;  // 0 -> otevreny polygon, >0 -> uzavreny polygon
      // (std. = 1, dale se pri generovani NC kodu priznak = 2,3,4, pokud lze tisknout kruhove oblouky nebo po spirale)
      Vzd := 0;
    end;
  end;  // while
  ZrusDataRezu(RezZ);
  KopirujaDataRezu(OptData, RezZ);
  ZrusDataRezu(OptData);
end;

procedure TForm1.OptimalizujData(var RezZ: TDataRezu; const ZacX, ZacY: Double);
const
  NULA_TOL = 1e-5;  // pro optimalizaci polygonu je vhodne pouzit mensi toleraci, nez standardne brana realna nula
var
  I, J, K, Zac: Integer;
  OptData: TDataRezu;
  Poradi: array of Integer;
begin
  SestavPolygony(RezZ, ZacX, ZacY);
  // optimalizace mnozstvi usecek na zaklade zadane tolerance TolNC
  // - vynechani bodu polygonu, ktere jsou blize ke spojnici sousednich bodu, nez zadana vzdalenost
  for I := 0 to Length(RezZ) - 1 do
  begin
    K := 2;
    for J := 2 to Length(RezZ[I].Body) - 1 do
    begin
      if VzdBoduOdPrimky(RezZ[I].Body[K-2].X, RezZ[I].Body[K-2].Y,
                         RezZ[I].Body[J].X, RezZ[I].Body[J].Y,
                         RezZ[I].Body[K-1].X, RezZ[I].Body[K-1].Y) > (NULA_TOL + TiskPar.TolNC) then
      begin
        // vzdalenost je vetsi, novy bod se prida ke stavajicimu polygonu
        Inc(K);
        RezZ[I].Body[K - 1] := RezZ[I].Body[J];
      end else begin
        // vzdalenost je mensi nez tolerance, novy bod nahradi posledni bod polygonu
        RezZ[I].Body[K - 1] := RezZ[I].Body[J];
      end;
    end;
    SetLength(RezZ[I].Body, K);
    // pri uzavrenem polygonu mohl nastat pripad, ze body s indexy 0, 1 a n-2 lezi v dane toleranci od primky
    // body n-1 a 0 jsou totozne
    if (RezZ[I].Priznak = UZAV_PROFIL) and (K > 5) and
       (VzdBoduOdPrimky(RezZ[I].Body[1].X, RezZ[I].Body[1].Y,
                        RezZ[I].Body[K-2].X, RezZ[I].Body[K-2].Y,
                        RezZ[I].Body[0].X, RezZ[I].Body[0].Y) < (NULA_TOL + TiskPar.TolNC)) then
    begin
      // prvni nebo predposledni bod lze posunout
      if Hypot(RezZ[I].Body[0].X - RezZ[I].Body[1].X, RezZ[I].Body[0].Y - RezZ[I].Body[1].Y) <
         Hypot(RezZ[I].Body[0].X - RezZ[I].Body[K-2].X, RezZ[I].Body[0].Y - RezZ[I].Body[K-2].Y) then
      begin
        SetLength(RezZ[I].Body, K - 1);
        for J := 0 to K - 3 do
        begin
          RezZ[I].Body[J].X := RezZ[I].Body[J+1].X;
          RezZ[I].Body[J].Y := RezZ[I].Body[J+1].Y;
        end;
        RezZ[I].Body[K-2].X := RezZ[I].Body[0].X;
        RezZ[I].Body[K-2].Y := RezZ[I].Body[0].Y;
      end else begin
        SetLength(RezZ[I].Body, K - 1);
        RezZ[I].Body[0].X := RezZ[I].Body[K-2].X;
        RezZ[I].Body[0].Y := RezZ[I].Body[K-2].Y;
      end;
    end;
  end;
  // vysledne usporadani polygonu
  // if RB_Opt_prejezdy.Checked then - vysledna data budou zapsana tak, jak byla pocitana - prejezdy by mely byt nejkratsi,
  // ale vlivem optimalizace poctu usecek se mohlo stat, ze toto neplati - proto jeste jednou projiti dat
  if TiskPar.Optimal = 1  then  // RB_Opt_prejezdy.Checked
  begin
    if (Length(RezZ) > 0) and (Length(RezZ[0].Body) > 0) then
      SestavPolygony(RezZ, RezZ[0].Body[0].X, RezZ[0].Body[0].Y);
  end;
  if TiskPar.Optimal = 2  then  // RB_Opt_uzavrene1otrvrene2.Checked
  begin
    SetLength(OptData, Length(RezZ));
    SetLength(Poradi, Length(RezZ));
    for I := 0 to Length(RezZ) - 1 do
      Poradi[I] := I;
    // je treba zapsat uzavrene polygony jako prvni a optimalizovat pocatek dle nasledneho polygonu
    J := 0;
    for I := 0 to Length(RezZ) - 1 do
    begin
      if RezZ[I].Priznak = UZAV_PROFIL then
      begin
        Poradi[J] := I;
        Inc(J);
      end;
    end;
    Zac := J - 1;
    // vsechny uzavrene polygony jsou nalezeny, doplneni otevrenych
    for I := 0 to Length(RezZ) - 1 do
    begin
      if RezZ[I].Priznak = OTEV_PROFIL then
      begin
        Poradi[J] := I;
        Inc(J);
      end;
    end;
    // pokud je min 1 uzavreny a nasleduje otevreny polygon, zmen poc. bod uzavreneho dle otevreneho polygonu
    if (Zac >= 0) and (Zac < Length(RezZ) - 1) then
      ZmenPocDleMinVzd(RezZ[Poradi[Zac]].Body, RezZ[Poradi[Zac + 1]].Body, ZacX, ZacY);
    // promenna Poradi nyni urcuje poradi zapisu polygonu
    for I := 0 to Length(RezZ) - 1 do
    begin
      OptData[I].Delka := RezZ[Poradi[I]].Delka;
      OptData[I].Priznak  := RezZ[Poradi[I]].Priznak;
      SetLength(OptData[I].Body, Length(RezZ[Poradi[I]].Body));
      for J := 0 to Length(RezZ[Poradi[I]].Body) - 1 do
      begin
        OptData[I].Body[J] := RezZ[Poradi[I]].Body[J];
      end;
    end;
    Finalize(Poradi);
    ZrusDataRezu(RezZ);
    KopirujaDataRezu(OptData, RezZ);
    // uvolneni pameti pro OptData
    ZrusDataRezu(OptData);
  end;
end;

procedure TForm1.NactiTiskParam(var Param: TTiskPar; const Akce: Boolean);
var
  I: Integer;
  Radek: string;
begin
  Param.TlVrstvy  := StrToFloatDef(E_Tloustka.Text, 0, FS);
  Param.PosuvXY   := StrToIntDef(E_PosuvXY.Text, 0);
  Param.PosuvZ    := StrToIntDef(E_PosuvZ.Text, 0);
  Param.RychlXY   := StrToIntDef(E_RychlXY.Text, 0);
  Param.ZdvihZ    := StrToFloatDef(E_ZdvihZ.Text, 0, FS);
  Param.MinCas    := StrToIntDef(E_MinCas.Text, 0);
  Param.PosunZ    := StrToFloatDef(E_PosunZ.Text, 0, FS);

  Param.OsaE      := CB_Extruder.Checked;
  Param.Dmat      := StrToFloatDef(E_Dmat.Text, 0, FS);
  Param.Dtrysky   := StrToFloatDef(E_Dtrysky.Text, 0, FS);
  Param.TlSteny   := StrToFloatDef(E_TlSteny.Text, 0, FS);
  Param.NasMat    := StrToFloatDef(E_NasMat.Text, 0, FS);
  Param.Ezap      := E_Ezap.Text;
  Param.Evyp      := E_Evyp.Text;
  Param.EzNC      := E_EzNC.Text;
  Param.Evel      := StrToIntDef(E_Evel.Text, 0);
  Param.PouzitE   := CB_EzNC.Checked;

  Param.TolSTL    := StrToFloatDef(E_TolSTL.Text, 0, FS);
  Param.TolNC     := StrToFloatDef(E_TolNC.Text, 0, FS);

  Param.Pomalu1   := CB_Pomalu1.Checked;
  Param.Spirala   := CB_Spirala.Checked;

  Param.ProklObl  := CB_ProklObl.Checked;
  Param.ParamIJ   := RB_ParamIJ.Checked;
  Param.PomerUsec := StrToFloatDef(E_PomerUsec.Text, 1.1, FS);
  Param.MaxChybKr := StrToFloatDef(E_MaxChybKr.Text, 0.1, FS);
  Param.MaxRadius := StrToFloatDef(E_MaxRadius.Text, 999.9, FS);

  Param.NevypEvXY := CB_NevypEvXY.Checked;
  Param.KratkyXY  := StrToFloatDef(E_KratkyXY.Text, 0, FS);
  Param.NevypEvZ  := CB_NevypEvZ.Checked;
  
  Param.RadkyNC   := CB_RadkyNC.Checked;
  Param.KrokNC    := StrToIntDef(E_KrokNC.Text, 2);
  Param.Pripona   := E_PriponaNC.Text;
  Param.Koment    := 1;  // apostrof
  if RB_Lomeno.Checked then Param.Koment := 2;  // lomeno
  if RB_Zavorky.Checked then Param.Koment := 3;  // zavorky
  if RB_Strednik.Checked then Param.Koment := 4;  // strednik

  Param.Optimal   := 1;
  if RB_Opt_uzavrene1otrvrene2.Checked then Param.Optimal := 2;

  Param.Mereni    := CB_Mereni.Checked;
  Param.MerVyskaZ := StrToFloatDef(E_MerVyskaZ.Text, 100.0, FS);
  Param.MerCidloX := StrToFloatDef(E_MerCidloX.Text, 100.0, FS);
  Param.MerCidloY := StrToFloatDef(E_MerCidloY.Text, 100.0, FS);
  Param.MerNvrst  := StrToIntDef(E_MerNvrst.Text, 10);
  Param.MerFunkce := E_MerFunkce.Text;
  Param.MerVypoc  := E_MerVypoc.Text;

  if Param.TlSteny > (Param.Dtrysky * MAX_EXTR) then
    Param.TlSteny := Param.Dtrysky * MAX_EXTR;
  if Param.TlSteny < (Param.Dtrysky * MIN_EXTR) then
    Param.TlSteny := Param.Dtrysky * MIN_EXTR;

  if Param.OsaE then
  begin
    if Param.Dmat > NULA_REAL then
    begin
      Param.KoefMat  := (PI * Param.TlVrstvy * Param.TlVrstvy / 4 + Param.TlVrstvy * (Param.TlSteny - Param.TlVrstvy)) * Param.NasMat / (PI * Param.Dmat * Param.Dmat / 4);
      L_KoefMat.Caption := Format('%1.3f', [Param.KoefMat], FS);
    end else begin
      Param.KoefMat  := Infinity;
      L_KoefMat.Caption := '-';
    end;
  end;
  Param.StartNC := '';
  for I := 0 to M_StartNC.Lines.Count - 1 do
  begin
    Radek := Trim(M_StartNC.Lines[I]);
    if Length(Radek) > 0 then
      Param.StartNC := Param.StartNC + Radek + '\n';
  end;
  Param.KonecNC := '';
  for I := 0 to M_KonecNC.Lines.Count - 1 do
  begin
    Radek := Trim(M_KonecNC.Lines[I]);
    if Length(Radek) > 0 then
      Param.KonecNC := Param.KonecNC + Radek + '\n';
  end;

  Param.MinX  := StrToFloatDef(E_MinX.Text, 0, FS);
  Param.MinY  := StrToFloatDef(E_MinY.Text, 0, FS);
  Param.MaxX  := StrToFloatDef(E_MaxX.Text, 0, FS);
  Param.MaxY  := StrToFloatDef(E_MaxY.Text, 0, FS);
  Param.MaxZ  := StrToFloatDef(E_MaxZ.Text, 0, FS);
  Param.Rastr := StrToFloatDef(E_Rastr.Text, 0, FS);

  if Akce then
  begin
    // zmena velikosti prac. prostoru
    XYGrid.XSamplingScale.Min := Param.MinX;
    XYGrid.YSamplingScale.Min := Param.MinY;
    XYGrid.XSamplingScale.Max := Param.MaxX;
    XYGrid.YSamplingScale.Max := Param.MaxY;
    DC_Prostor.Scale.X := Param.MaxX - Param.MinX;
    DC_Prostor.Scale.Y := Param.MaxY - Param.MinY;
    DC_Prostor.Scale.Z := Param.MaxZ;
    DC_Prostor.Position.X := (Param.MaxX + Param.MinX) / 2;
    DC_Prostor.Position.Y := (Param.MaxY + Param.MinY) / 2;
    DC_Prostor.Position.Z := Param.MaxZ / 2;
    // meritko os sour. systemu v nasobcich 10
    I := Trunc(Max(DC_Prostor.Scale.X, DC_Prostor.Scale.Y) / 200) * 10;
    if I > 50 then
      I := 50;
    if I < 10 then
      I := 10;
    GLArrowLineX.Position.X := I;
    GLArrowLineX.Height := I * 2;
    GLArrowLineX.TopArrowHeadHeight := I;
    GLArrowLineX.TopArrowHeadRadius := I div 3;
    GLArrowLineX.TopRadius := I div 10;
    GLArrowLineX.BottomRadius := GLArrowLineX.TopRadius;
      GLArrowLineY.Position.Y := I;
      GLArrowLineY.Height := I * 2;
      GLArrowLineY.TopArrowHeadHeight := I;
      GLArrowLineY.TopArrowHeadRadius := I div 3;
      GLArrowLineY.TopRadius := I div 10;
      GLArrowLineY.BottomRadius := GLArrowLineY.TopRadius;
    GLArrowLineZ.Position.Z := I;
    GLArrowLineZ.Height := I * 2;
    GLArrowLineZ.TopArrowHeadHeight := I;
    GLArrowLineZ.TopArrowHeadRadius := I div 3;
    GLArrowLineZ.TopRadius := I div 10;
    GLArrowLineZ.BottomRadius := GLArrowLineZ.TopRadius;
    XYGrid.XSamplingScale.Step := Param.Rastr;
    XYGrid.YSamplingScale.Step := Param.Rastr;
  end;
end;

procedure TForm1.NastavEditTiskPar(const Reset: Boolean);
const
  BARVA = $0000D0FF;
var
  Temp: TTiskPar;
begin
  if Reset then
  begin
    // nastaveni barev pozadi TEdit parametru tisku na clWindow
    E_Tloustka.Color  := clWindow;
    E_PosuvXY.Color   := clWindow;
    E_PosuvZ.Color    := clWindow;
    E_RychlXY.Color   := clWindow;
    E_ZdvihZ.Color    := clWindow;
    E_MinCas.Color    := clWindow;
    E_PosunZ.Color    := clWindow;

    E_Dmat.Color      := clWindow;
    E_Dtrysky.Color   := clWindow;
    E_TlSteny.Color   := clWindow;
    E_NasMat.Color    := clWindow;
    E_Ezap.Color      := clWindow;
    E_Evyp.Color      := clWindow;
    E_EzNC.Color      := clWindow;
    E_Evel.Color      := clWindow;
    CB_EzNC.Color     := clBtnFace;

    E_TolSTL.Color    := clWindow;
    E_TolNC.Color     := clWindow;

    L_KoefMat.Color   := clBtnFace;
    CB_Extruder.Color := clBtnFace;
    CB_Pomalu1.Color  := clBtnFace;
    CB_Spirala.Color  := clBtnFace;

    CB_ProklObl.Color := clBtnFace;
    RB_ParamIJ.Color  := clBtnFace;
    RB_ParamR.Color   := clBtnFace;
    E_PomerUsec.Color := clWindow;
    E_MaxChybKr.Color := clWindow;
    E_MaxRadius.Color := clWindow;

    CB_NevypEvXY.Color := clBtnFace;
    E_KratkyXY.Color   := clWindow;
    CB_NevypEvZ.Color  := clBtnFace;

    M_StartNC.Color   := clWindow;
    M_KonecNC.Color   := clWindow;

    E_MinX.Color      := clWindow;
    E_MinY.Color      := clWindow;
    E_MaxX.Color      := clWindow;
    E_MaxY.Color      := clWindow;
    E_MaxZ.Color      := clWindow;
    E_Rastr.Color     := clWindow;

    CB_RadkyNC.Color  := clBtnFace;
    E_KrokNC.Color    := clWindow;
    E_PriponaNC.Color := clWindow;

    RB_Opt_prejezdy.Color := clBtnFace;
    RB_Opt_uzavrene1otrvrene2.Color := clBtnFace;

    CB_Mereni.Color := clBtnFace;
    E_MerVyskaZ.Color := clWindow;
    E_MerCidloX.Color := clWindow;
    E_MerCidloY.Color := clWindow;
    E_MerNvrst.Color  := clWindow;
    E_MerFunkce.Color := clWindow;
    E_MerVypoc.Color  := clWindow;
  end else begin
    // nacteni aktualnich hodnot parametru tisku do promenne Temp
    NactiTiskParam(Temp, False);
(*
  TTiskPar = record
    PosuvXY, PosuvZ, RychlXY, MinCas, KrokNC, Evel, Koment: Integer;
    TlVrstvy, ZdvihZ, Dmat, Dtrysky, TlSteny, NasMat, KoefMat, MerXYZ, TolSTL, TolNC, PosunZ: Double;
    Ezap, Evyp, EzNC, StartNC, KonecNC, Pripona: string;
    OsaE, Pomalu1, Spirala, PouzitE, RadkyNC: Boolean;
    MinX, MinY, MaxX, MaxY, MaxZ, Rastr: Double;
  end;

    (*
    if Temp.TlSteny = 0 then
      Temp.TlSteny := Temp.Dtrysky * 1.2;
    if (Temp.TlSteny > (Temp.Dtrysky * MAX_EXTR)) or (Temp.TlSteny < (Temp.Dtrysky * MIN_EXTR)) then
    begin
      if Temp.TlSteny < (Temp.Dtrysky * MIN_EXTR) then
        Temp.TlSteny := Temp.Dtrysky * MIN_EXTR
      else
        Temp.TlSteny := Temp.Dtrysky * MAX_EXTR;
      E_TlSteny.Color := CHYBA;
    end else
      E_TlSteny.Color := clWhite;
    *)
    
    // nastaveni barev pozadi TEdit parametru tisku, pokud jsou ruzne od globalniho nastaveni
    if Temp.TlVrstvy = TiskPar.TlVrstvy then
      E_Tloustka.Color := clWindow
    else
      E_Tloustka.Color := BARVA;
    if Temp.PosuvXY = TiskPar.PosuvXY then
      E_PosuvXY.Color := clWindow
    else
      E_PosuvXY.Color := BARVA;
    if Temp.PosuvZ = TiskPar.PosuvZ then
      E_PosuvZ.Color := clWindow
    else
      E_PosuvZ.Color := BARVA;
    if Temp.RychlXY = TiskPar.RychlXY then
      E_RychlXY.Color := clWindow
    else
      E_RychlXY.Color := BARVA;
    if Temp.ZdvihZ = TiskPar.ZdvihZ then
      E_ZdvihZ.Color := clWindow
    else
      E_ZdvihZ.Color := BARVA;
    if Temp.MinCas = TiskPar.MinCas then
      E_MinCas.Color := clWindow
    else
      E_MinCas.Color := BARVA;
    if Temp.PosunZ = TiskPar.PosunZ then
      E_PosunZ.Color := clWindow
    else
      E_PosunZ.Color := BARVA;

    if Temp.OsaE = TiskPar.OsaE then
      CB_Extruder.Color := clBtnFace
    else
      CB_Extruder.Color := BARVA;
    if Temp.Dmat = TiskPar.Dmat then
      E_Dmat.Color := clWindow
    else
      E_Dmat.Color := BARVA;
    if Temp.Dtrysky = TiskPar.Dtrysky then
      E_Dtrysky.Color := clWindow
    else
      E_Dtrysky.Color := BARVA;
    if Temp.TlSteny = TiskPar.TlSteny then
      E_TlSteny.Color := clWindow
    else
      E_TlSteny.Color := BARVA;
    if Temp.NasMat = TiskPar.NasMat then
      E_NasMat.Color := clWindow
    else
      E_NasMat.Color := BARVA;
    if Temp.Ezap = TiskPar.Ezap then
      E_Ezap.Color := clWindow
    else
      E_Ezap.Color := BARVA;
    if Temp.Evyp = TiskPar.Evyp then
      E_Evyp.Color := clWindow
    else
      E_Evyp.Color := BARVA;
    if Temp.EzNC = TiskPar.EzNC then
      E_EzNC.Color := clWindow
    else
      E_EzNC.Color := BARVA;
    if Temp.Evel = TiskPar.Evel then
      E_Evel.Color := clWindow
    else
      E_Evel.Color := BARVA;
    if Temp.PouzitE = TiskPar.PouzitE then
      CB_EzNC.Color := clBtnFace
    else
      CB_EzNC.Color := BARVA;

    if Temp.TolSTL = TiskPar.TolSTL then
      E_TolSTL.Color := clWindow
    else
      E_TolSTL.Color := BARVA;
    if Temp.TolNC = TiskPar.TolNC then
      E_TolNC.Color := clWindow
    else
      E_TolNC.Color := BARVA;

    if Temp.KoefMat = TiskPar.KoefMat then
      L_KoefMat.Color := clBtnFace
    else
      L_KoefMat.Color := BARVA;
    if Temp.Pomalu1 = TiskPar.Pomalu1 then
      CB_Pomalu1.Color := clBtnFace
    else
      CB_Pomalu1.Color := BARVA;
    if Temp.Spirala = TiskPar.Spirala then
      CB_Spirala.Color := clBtnFace
    else
      CB_Spirala.Color := BARVA;

    if Temp.ProklObl = TiskPar.ProklObl then
      CB_ProklObl.Color := clBtnFace
    else
      CB_ProklObl.Color := BARVA;
    if Temp.ParamIJ = TiskPar.ParamIJ then
    begin
      RB_ParamIJ.Color  := clBtnFace;
      RB_ParamR.Color   := clBtnFace;
    end else begin
      RB_ParamIJ.Color  := BARVA;
      RB_ParamR.Color   := BARVA;
    end;
    if Temp.PomerUsec = TiskPar.PomerUsec then
      E_PomerUsec.Color := clWindow
    else
      E_PomerUsec.Color := BARVA;
    if Temp.MaxChybKr = TiskPar.MaxChybKr then
      E_MaxChybKr.Color := clWindow
    else
      E_MaxChybKr.Color := BARVA;
    if Temp.MaxRadius = TiskPar.MaxRadius then
      E_MaxRadius.Color := clWindow
    else
      E_MaxRadius.Color := BARVA;

    if Temp.NevypEvXY = TiskPar.NevypEvXY then
      CB_NevypEvXY.Color := clBtnFace
    else
      CB_NevypEvXY.Color := BARVA;
    if Temp.KratkyXY = TiskPar.KratkyXY then
      E_KratkyXY.Color := clWindow
    else
      E_KratkyXY.Color := BARVA;
    if Temp.NevypEvZ = TiskPar.NevypEvZ then
      CB_NevypEvZ.Color := clBtnFace
    else
      CB_NevypEvZ.Color := BARVA;

    if Temp.StartNC = TiskPar.StartNC then
      M_StartNC.Color := clWindow
    else
      M_StartNC.Color := BARVA;
    if Temp.KonecNC = TiskPar.KonecNC then
      M_KonecNC.Color := clWindow
    else
      M_KonecNC.Color := BARVA;

    if Temp.MinX = TiskPar.MinX then
      E_MinX.Color := clWindow
    else
      E_MinX.Color := BARVA;
    if Temp.MinY = TiskPar.MinY then
      E_MinY.Color := clWindow
    else
      E_MinY.Color := BARVA;
    if Temp.MaxX = TiskPar.MaxX then
      E_MaxX.Color := clWindow
    else
      E_MaxX.Color := BARVA;
    if Temp.MaxY = TiskPar.MaxY then
      E_MaxY.Color := clWindow
    else
      E_MaxY.Color := BARVA;
    if Temp.MaxZ = TiskPar.MaxZ then
      E_MaxZ.Color := clWindow
    else
      E_MaxZ.Color := BARVA;
    if Temp.Rastr = TiskPar.Rastr then
      E_Rastr.Color := clWindow
    else
      E_Rastr.Color := BARVA;

    if Temp.RadkyNC = TiskPar.RadkyNC then
      CB_RadkyNC.Color := clBtnFace
    else
      CB_RadkyNC.Color := BARVA;
    if Temp.KrokNC = TiskPar.KrokNC then
      E_KrokNC.Color := clWindow
    else
      E_KrokNC.Color := BARVA;
    if Temp.Pripona = TiskPar.Pripona then
      E_PriponaNC.Color := clWindow
    else
      E_PriponaNC.Color := BARVA;

    if Temp.Optimal = TiskPar.Optimal then
    begin
      RB_Opt_prejezdy.Color := clBtnFace;
      RB_Opt_uzavrene1otrvrene2.Color := clBtnFace;
    end else begin
      RB_Opt_prejezdy.Color := BARVA;
      RB_Opt_uzavrene1otrvrene2.Color := BARVA;
    end;

    if Temp.Mereni = TiskPar.Mereni then
      CB_Mereni.Color := clBtnFace
    else
      CB_Mereni.Color := BARVA;
    if Temp.MerVyskaZ = TiskPar.MerVyskaZ then
      E_MerVyskaZ.Color := clWindow
    else
      E_MerVyskaZ.Color := BARVA;
    if Temp.MerCidloX = TiskPar.MerCidloX then
      E_MerCidloX.Color := clWindow
    else
      E_MerCidloX.Color := BARVA;
    if Temp.MerCidloY = TiskPar.MerCidloY then
      E_MerCidloY.Color := clWindow
    else
      E_MerCidloY.Color := BARVA;
    if Temp.MerNvrst = TiskPar.MerNvrst then
      E_MerNvrst.Color  := clWindow
    else
      E_MerNvrst.Color  := BARVA;
    if Temp.MerFunkce = TiskPar.MerFunkce then
      E_MerFunkce.Color := clWindow
    else
      E_MerFunkce.Color := BARVA;
    if Temp.MerVypoc = TiskPar.MerVypoc then
      E_MerVypoc.Color  := clWindow
    else
      E_MerVypoc.Color  := BARVA;
  end;
end;

function TForm1.NactiCisloI(const Radek: string; var Pozice: Integer): Integer;
var
  I: Integer;
  Cislo: string;
begin
  // nacita cele kladne cislo, prip. nulu (zaporna cisla se nepredpokladaji)
  Cislo := '';
  I := 1;
  // nacitani pripadneho znamenka +
  if (Pozice <= Length(Radek)) and (Radek[Pozice] in ['+']) then
    Inc(Pozice);
  // nacitani vlastniho cisla
  while (Pozice <= Length(Radek)) and (I <= 2) and (Radek[Pozice] in ['0'..'9']) do
  begin
    Cislo := Cislo + Radek[Pozice];
    Inc(I);
    Inc(Pozice);
  end;
  if Length(Cislo) > 0 then
    Result := StrToInt(Cislo)
  else
    Result  := Low(Integer);
end;

function TForm1.NactiCisloR(const Radek: string; var Pozice: Integer): Double;
var
  Cislo: string;
begin
  // nacita realne cislo
  Cislo := '';
  // nacitani znamenka a cele casti cisla
  if (Pozice <= Length(Radek)) and (Radek[Pozice] in ['-', '+']) then
  begin
    Cislo := Cislo + Radek[Pozice];
    Inc(Pozice);
  end;
  while (Pozice <= Length(Radek)) and (Radek[Pozice] in ['0'..'9']) do
  begin
    Cislo := Cislo + Radek[Pozice];
    Inc(Pozice);
  end;
  // pripadne nacitani desetinne casti cisla
  if (Pozice <= Length(Radek)) and (Radek[Pozice] = '.') then
  begin
    Cislo := Cislo + FS.DecimalSeparator;
    Inc(Pozice);
    while (Pozice <= Length(Radek)) and (Radek[Pozice] in ['0'..'9']) do
    begin
      Cislo := Cislo + Radek[Pozice];
      Inc(Pozice);
    end;
  end;
  if Length(Cislo) > 0 then
  begin
    try
      Result := StrToFloat(Cislo, FS);
    except
      Result := Low(Integer);
    end;
  end else
    Result := Low(Integer);
end;

function TForm1.SmerovyUhel(const Dx, Dy: Double; const Smer: Integer): Double;
begin
  if Abs(Dy) < NULA_REAL then
  begin
    if Dx > 0 then Result := PI / 2
              else Result := 3 * PI / 2;
  end else begin
    Result := ArcTan(Abs(Dx / Dy));
    if (Dx >= 0) and (Dy < 0) then
      Result := PI - Result;
    if (Dx < 0) and (Dy < 0) then
      Result := PI + Result;
    if (Dx < 0) and (Dy >= 0) then
      Result := 2 * PI - Result;
   end;
   // pro G02 (Smer = -1) je treba pouzit doplnkovy uhel do 2*Pi
   if Smer = -1 then
     Result := 2 * PI - Result;
end;

procedure TForm1.ObloukNaPolygon(const PocX, PocY, KonX, KonY: Double; const I, J, R, Z: Double; const FceG: Integer);
const
  NULA_KRUH  = 0.005;
  TOLER_KRUH = 0.015;
var
  K, Smer, PocetUsecek: Integer;
  Stred: TBodXY;
  Dx, Dy, Tol, L, P, U, Uhel: Double;
  S, C: Extended;
  SimData: array of TSimData;
begin
  // definovani smeru oblouku
  Smer := 1;
  if FceG = 3 then
    Smer := -1;
  // vypocet souradnic stredu oblouku
  if (Abs(I) < NULA_REAL) and (Abs(J) < NULA_REAL) and (R > 0) then
  begin
    // kruhovy oblouk byl zadan pomoci radiusu
    Dx := KonX - PocX;
    Dy := KonY - PocY;
    L  := Hypot(Dx, Dy);
    if ((2 * R - L) < 0) and ((L - 2 * R) < NULA_KRUH) then
      L := 2 * R;
    P  := Sqrt(4 * R * R - L * L);
    Stred.X := (L * (PocX + KonX) + Smer * P * Dy) / (2 * L);
    Stred.Y := (L * (PocY + KonY) - Smer * P * Dx) / (2 * L);
  end else begin
    // kruhovy oblouk byl zadan pomoci parametru I, J
    Stred.X := PocX + I;
    Stred.Y := PocY + J;
    if Abs(R - Hypot(Stred.X - KonX, Stred.Y - KonY)) > NULA_KRUH then
    begin
      Exit;
    end;
  end;
  // vlastni prevod oblouku na polygon
  if R <= NULA_KRUH then
  begin
    // vykresleni oblouku
    L_Sim[Length(L_Sim) - 1].L.Nodes.AddNode(KonX, KonY, Z);
    Exit;
  end;
  // vypocet uhlu sevreneho obloukem
  Uhel := SmerovyUhel(PocX - Stred.X, PocY - Stred.Y, Smer);
  Uhel := SmerovyUhel(KonX - Stred.X, KonY - Stred.Y, Smer) - Uhel;
  while Uhel > 2 * PI do Uhel := Uhel - 2 * PI;
  while Uhel < 0 do Uhel := Uhel + 2 * PI;
  if (Uhel - PI) > NULA_REAL then
  begin
    // podminka by matematicky fungovala, ale neni podporovano na realnem stroji
    /// nahlasit chybu simulace
    Finalize(SimData);
    Exit;
  end;
  // vypocet polygonu usecek
  PocetUsecek := 1;
  repeat
    Inc(PocetUsecek);
    Tol := R * (1 - Cos(Uhel / (2 * PocetUsecek)));
  until Tol <= TOLER_KRUH;
  // vypocet souradnic koncovych bodu jednotlivych usecek
  SetLength(SimData, PocetUsecek);
  // aktualni pozice
  SimData[0].X := PocX;
  SimData[0].Y := PocY;
  for K := 1 to PocetUsecek - 1 do
  begin
    // vypocet bodu lezicich na vrcholech polygonu nahrazujiciho kruhovy oblouk
    U := -(K * Uhel / PocetUsecek) * Smer;
    Math.SinCos(U, S, C);
    SimData[K].X   := (PocX - Stred.X) * C - (PocY - Stred.Y) * S + Stred.X;
    SimData[K].Y   := (PocX - Stred.X) * S + (PocY - Stred.Y) * C + Stred.Y;
    // vykresleni dane casti oblouku
    L_Sim[Length(L_Sim) - 1].L.Nodes.AddNode(SimData[K].X, SimData[K].Y, Z);
  end;
  // vykresleni posledni casti oblouku
  L_Sim[Length(L_Sim) - 1].L.Nodes.AddNode(KonX, KonY, Z);
  Finalize(SimData);
end;

procedure TForm1.ProvedRadek(const Radek: string);
var
  I, J, L, FceG, CisloI, VelDat, Typ: Integer;
  CisloR: Double;
  Znak: Char;
  PuvData: TStrojData;
begin
  if Radek <> '' then
  begin
    PuvData := SimStroje;
    // vynulovat nemodalni souradnice
    SimStroje.R := 0;
    SimStroje.I := 0;
    SimStroje.J := 0;
    FceG := PuvData.FceG;
    I := 1;
    // rozbor ISO kodu - prochazeni radkem programu
    while I <= Length(Radek) do
    begin
      Znak  := Radek[I];
      // klicova slova vcetne cislic
      if Znak = 'G' then
      begin
        Inc(I);
        CisloI := NactiCisloI(Radek, I);
        // kontrola znamosti cisla funkce
        if (CisloI >= 0) and (CisloI <= 3) then
        begin
          SimStroje.Pohyb := True;
          FceG := CisloI;
        end;
      end;
      if Znak = 'M' then
      begin
        Inc(I);
        CisloI := NactiCisloI(Radek, I);
        // kontrola znamosti cisla funkce
        J := 1;
        while (J <= Length(TiskPar.Ezap)) and not(TiskPar.Ezap[J] in ['0'..'9']) do
          Inc(J);
        if CisloI = NactiCisloI(TiskPar.Ezap, J) then
          SimStroje.Extruze := True;
        J := 1;
        while (J <= Length(TiskPar.Evyp)) and not(TiskPar.Evyp[J] in ['0'..'9']) do
          Inc(J);
        if CisloI = NactiCisloI(TiskPar.Evyp, J) then
          SimStroje.Extruze := False;
      end;
      // Souradnice
      if Znak in ['X', 'Y', 'Z', 'R', 'I', 'J', 'E'] then
      begin
        Inc(I);
        CisloR := NactiCisloR(Radek, I);
        if Znak in ['X', 'Y', 'Z'] then SimStroje.Pohyb := True;
        case Znak of
          'X': SimStroje.X := CisloR;
          'Y': SimStroje.Y := CisloR;
          'Z': SimStroje.Z := CisloR;
          'R': SimStroje.R := CisloR;
          'I': SimStroje.I := CisloR;
          'J': SimStroje.J := CisloR;
          'E': SimStroje.E := CisloR;
        end;
      end;
      Inc(I);
    end;  // while (I <= Length(Radek)) ...

    // cely radek precten => pokud je programovan pohyb, vykresleni pohybu
    if SimStroje.Pohyb then
    begin
      // extrudovani materialu nebo jen presun?
      if ((SimStroje.E - PuvData.E) > 0) or SimStroje.Extruze then
      begin
        // extrudovani
        Typ := 1;
      end else begin
        // presun
        Typ := 0;
      end;
      // priprava dat
      L := Length(L_Sim) - 1;
      if L < 0 then
      begin
        L := 0;
        SetLength(L_Sim, L + 1);
        L_Sim[L].G := Typ;
        L_Sim[L].Z := SimStroje.Z;
        L_Sim[L].L := TGLLines.CreateAsChild(ModelCube);
        L_Sim[L].L.NodesAspect := lnaInvisible;
        L_Sim[L].L.LineColor.Color := ConvertWinColor(clBlack);
        if Typ = 0 then
        begin
          L_Sim[L].L.LinePattern := $0F0F;
          L_Sim[L].L.LineWidth := 1;
        end else begin
          L_Sim[L].L.LinePattern := $FFFF;
          L_Sim[L].L.LineWidth := 10;
        end;
      end;

      L_XSim.Caption := Format('%3.3f', [SimStroje.X]);
      L_YSim.Caption := Format('%3.3f', [SimStroje.Y]);
      L_ZSim.Caption := Format('%3.3f', [SimStroje.Z]);

      // vlastni vykresleni
      if Abs(SimStroje.Z - L_Sim[L].Z) > NULA_REAL then
      begin
        // zesediveni predchozich vrstev
        J := 1;
        if CB_CelaVrstva.Checked then
          Inc(J);
        for I := 0 to Length(L_Sim) - J do
          L_Sim[I].L.LineColor.Color := ConvertWinColor(clGray);
        Inc(L);
        SetLength(L_Sim, L + 1);
        L_Sim[L].G := Typ;
        L_Sim[L].Z := SimStroje.Z;
        L_Sim[L].L := TGLLines.CreateAsChild(ModelCube);
        L_Sim[L].L.NodesAspect := lnaInvisible;
        L_Sim[L].L.LineColor.Color := ConvertWinColor(clBlack);
        if Typ = 0 then
        begin
          L_Sim[L].L.LinePattern := $0F0F;
          L_Sim[L].L.LineWidth := 1;
        end else begin
          L_Sim[L].L.LinePattern := $FFFF;
          L_Sim[L].L.LineWidth := 10;
        end;
        VelDat := L_Sim[L-1].L.Nodes.Count - 1;
        if VelDat >= 0 then
          L_Sim[L].L.Nodes.AddNode(L_Sim[L-1].L.Nodes[VelDat].X, L_Sim[L-1].L.Nodes[VelDat].Y, L_Sim[L-1].L.Nodes[VelDat].Z);
      end;
      if L_Sim[L].G <> Typ then
      begin
        Inc(L);
        SetLength(L_Sim, L + 1);
        L_Sim[L].G := Typ;
        L_Sim[L].Z := SimStroje.Z;
        L_Sim[L].L := TGLLines.CreateAsChild(ModelCube);
        L_Sim[L].L.NodesAspect := lnaInvisible;
        L_Sim[L].L.LineColor.Color := ConvertWinColor(clBlack);
        if Typ = 0 then
        begin
          L_Sim[L].L.LinePattern := $0F0F;
          L_Sim[L].L.LineWidth := 1;
        end else begin
          L_Sim[L].L.LinePattern := $FFFF;
          L_Sim[L].L.LineWidth := 10;
        end;
        VelDat := L_Sim[L-1].L.Nodes.Count - 1;
        if VelDat >= 0 then
          L_Sim[L].L.Nodes.AddNode(L_Sim[L-1].L.Nodes[VelDat].X, L_Sim[L-1].L.Nodes[VelDat].Y, L_Sim[L-1].L.Nodes[VelDat].Z);
      end;
      case FceG of
        0, 1:
          begin
            // vykresleni pohybu nastroje
            L_Sim[L].L.Nodes.AddNode(SimStroje.X, SimStroje.Y, SimStroje.Z);
          end;
        2, 3:
          begin
            if Abs(SimStroje.R) < NULA_REAL then
              SimStroje.R := Hypot(SimStroje.I, SimStroje.J);
            ObloukNaPolygon(PuvData.X, PuvData.Y, SimStroje.X, SimStroje.Y, SimStroje.I, SimStroje.J, SimStroje.R, SimStroje.Z, FceG);
          end;
      end;  // case
    end;  // if Pohyb
  end;
end;

function TForm1.OdstranKoment(const Radek: string): string;
var
  I: Integer;
begin
  Result := '';
  I := 1;
  while I <= Length(Radek) do
  begin
    if Radek[I] in [nc_KOM_1, nc_KOM_2, nc_KOM_3, nc_KOM_4] then
    begin
      case Radek[I] of
        nc_KOM_1, nc_KOM_2, nc_KOM_4: I := Length(Radek);
        nc_KOM_3: while (I <= Length(Radek)) and (Radek[I] <> nc_KOM_3a) do
                    Inc(I);
      end;
    end else
      Result := Result + Radek[I];
    Inc(I);
  end;
  Result := Trim(Result);
end;

procedure TForm1.SmazSimulaci;
var
  I: Integer;
begin
  for I := 0 to Length(L_Sim) - 1 do
  begin
    L_Sim[I].L.Nodes.Clear;
    L_Sim[I].L.Free;
  end;
  Finalize(L_Sim);
end;

procedure TForm1.NastavSimulaci;
begin
  SmazSimulaci;
  // uvodni inicializace promennych
  ResetSimulace;
end;

procedure TForm1.ResetSimulace;
var
  I: Integer;
begin
  // konec simulace
  SimStop := True;
  PrgMonIndex := -1;
  MIndex := PrgMonIndex;
  M_GKod.TopLine := 0;
  M_GKod.Refresh;
  ExtrZ := -1;
  SimStroje.Pohyb    := False;
  SimStroje.Extruze  := False;
  SimStroje.FceG     := 1;  // vychozi na 3D toskarnach je G01
  TB_Sim.Enabled     := M_Gkod.Lines.Count > 0;
  TB_SimBlok.Enabled := M_Gkod.Lines.Count > 0;
  TB_SimStop.Enabled := False;
  TB_SimZpet.Enabled := False;
  // reset 3D dat simulace
  SmazSimulaci;
  for I := 0 to Length(Lines) - 1 do
    Lines[I].L.Visible := True;  
end;

function TForm1.CfgSoubor(const JenSystem: Boolean): string;
var
  Soubor: string;
  Path: array[0..MAX_PATH] of Char;
begin
  Result := '';
  if ExpandEnvironmentStrings('%APPDATA%', Path, Length(Path)) > 1 then
  begin
    Result := Path;
    if Result[Length(Result)] <> '\' then
      Result := Result + '\';
    Result := Result + 'Slicer4Atena\';
  end;
  Result := Result + SOUBOR_CFG;
  Soubor := Result;
  if not(JenSystem) then
  begin
    // pokus o nalezeni souboru s konfiguraci i na ceste, kde je vlastni program
    Result := ExtractFilePath(ParamStr(0));
    if Result[Length(Result)] <> '\' then
      Result := Result + '\';
    Result := Result + SOUBOR_CFG;
    if not(FileExists(Result)) then
      Result := Soubor;
  end;
end;

procedure TForm1.NovaMesh;
var
  Sit: PMesh;
begin
  New(Sit);
  // vytvoreni DummyCube pro objekty Meshe
  Sit^.DC_Mesh := TGLDummyCube.CreateAsChild(ModelCube);
  Sit^.DC_Mesh.Pickable := False;
  Sit^.DC_Mesh.CubeSize := 1;
  Sit^.DC_Mesh.Direction.X := 0;
  Sit^.DC_Mesh.Direction.Y := 0;
  Sit^.DC_Mesh.Direction.Z := 1;
  Sit^.DC_Mesh.VisibleAtRunTime := False;
  // vytvoreni kvadru kolem Meshe
  Sit^.MeshObal := TGLLines.CreateAsChild(ModelCube);//(Sit^.DC_Mesh);
  Sit^.MeshObal.NodesAspect := lnaInvisible;
  Sit^.MeshObal.LineColor.Color := ConvertWinColor(clBlue);
  Sit^.MeshObal.LinePattern := $FFFF;
  Sit^.MeshObal.LineWidth := 3;
  Sit^.MeshObal.Nodes.AddNode(-0.5, -0.5, -0.5);
  Sit^.MeshObal.Nodes.AddNode(0.5, -0.5, -0.5);
  Sit^.MeshObal.Nodes.AddNode(0.5, 0.5, -0.5);
  Sit^.MeshObal.Nodes.AddNode(-0.5, 0.5, -0.5);
  Sit^.MeshObal.Nodes.AddNode(-0.5, -0.5, -0.5);
  Sit^.MeshObal.Nodes.AddNode(-0.5, -0.5, 0.5);
  Sit^.MeshObal.Nodes.AddNode(0.5, -0.5, 0.5);
  Sit^.MeshObal.Nodes.AddNode(0.5, 0.5, 0.5);
  Sit^.MeshObal.Nodes.AddNode(-0.5, 0.5, 0.5);
  Sit^.MeshObal.Nodes.AddNode(-0.5, -0.5, 0.5);
  Sit^.MeshObal.Nodes.AddNode(0.5, -0.5, 0.5);
  Sit^.MeshObal.Nodes.AddNode(0.5, -0.5, -0.5);
  Sit^.MeshObal.Nodes.AddNode(0.5, 0.5, -0.5);
  Sit^.MeshObal.Nodes.AddNode(0.5, 0.5, 0.5);
  Sit^.MeshObal.Nodes.AddNode(-0.5, 0.5, 0.5);
  Sit^.MeshObal.Nodes.AddNode(-0.5, 0.5, -0.5);
  Sit^.MeshObal.Direction.X := 0;
  Sit^.MeshObal.Direction.Y := 0;
  Sit^.MeshObal.Direction.Z := 1;
  // vytvoreni meshe
  Sit^.Mesh := TGLFreeForm.CreateAsChild(Sit^.DC_Mesh);
  Sit^.Mesh.Material.LibMaterialName := '';
  Sit^.Mesh.Material.FaceCulling := fcNoCull;
  Sit^.Mesh.Material.BlendingMode := bmOpaque;
  Sit^.Mesh.Material.BackProperties.Diffuse.Alpha  := 1;
  Sit^.Mesh.Material.FrontProperties.Diffuse.Alpha := 1;
  Sit^.Mesh.Material.BackProperties.Ambient.Color  := ConvertWinColor(clBlue);//VectorMake(0.2, 0.2, 1, 1);
  Sit^.Mesh.Material.BackProperties.Diffuse.Color  := VectorMake(1, 1, 1, 1);
  Sit^.Mesh.Material.FrontProperties.Ambient.Color := ConvertWinColor(clBlue);//VectorMake(0.2, 0.2, 1, 1);
  Sit^.Mesh.Material.FrontProperties.Diffuse.Color := VectorMake(1, 1, 1, 1); //ConvertWinColor(clWhite);
  Sit^.Mesh.Direction.X := 0;
  Sit^.Mesh.Direction.Y := 0;
  Sit^.Mesh.Direction.Z := 1;

//Sit^.Mesh.Material.PolygonMode := pmLines;
  Sit^.Meritko.X := 1;
  Sit^.Meritko.Y := 1;
  Sit^.Meritko.Z := 1;

  MeshList.Add(Sit);
end;

procedure TForm1.ZrusMesh(const M: Integer);
var
  Sit: PMesh;
begin
  if (M > (MeshList.Count - 1)) or (M < 0) then
    Exit;
  Sit := MeshList.Items[M];
  Sit^.Mesh.Free;
  Sit^.MeshObal.Free;
  Sit^.DC_Mesh.Free;
  MeshList.Delete(M);
  Dispose(Sit);
end;

procedure TForm1.NactiSoubor(const JmenoSouboru: string);
var
  RozjDraha: Boolean;
  AktKurzor: TCursor;
  I, J: Integer;
  Bmp: TBitmap;
  MinM, MaxM: TVector4f;
  Sit: PMesh;
begin
  OpenDialog.InitialDir := ExtractFilePath(JmenoSouboru);
  SaveDialog.InitialDir := OpenDialog.InitialDir;
  // Nastavení počátečních podmínek
  P_Stat.Visible := False;
  // Načtení souboru
  AktKurzor := Screen.Cursor;
  Screen.Cursor := crHourglass;
  try
    NovaMesh;
    try
      Sit := MeshList.Items[MeshList.Count - 1];
      Sit^.Mesh.LoadFromFile(JmenoSouboru);
      Sit^.Mesh.Name := Format('Mesh%d', [MeshList.Count]);
      Sit^.Mesh.StructureChanged;
      // nalezeni minima a maxima souradnic
      UrciMinMax(Sit, MinM, MaxM, True, True, True);
      // nacteni probehlo v poradku - pridani polozky do seznamu, vcetne grafickych ikon
      RozjDraha := Pos('START', ExtractFileName(AnsiUpperCase(JmenoSouboru))) = 1;
      Bmp := TBitmap.Create;
      Bmp.Width  := 42;
      Bmp.Height := 15;
      Bmp.Canvas.CopyRect(Rect(0, 0, 21, 15), I_MeshStatus.Picture.Bitmap.Canvas, Rect(0, 0, 21, 15));
      if RozjDraha then
        Bmp.Canvas.CopyRect(Rect(21, 0, 42, 15), I_MeshStatus.Picture.Bitmap.Canvas, Rect(88, 0, 109, 15))
      else
        Bmp.Canvas.CopyRect(Rect(21, 0, 42, 15), I_MeshStatus.Canvas, Rect(44, 0, 65, 15));
      LB_Mesh.Items.AddObject(ExtractFileName(JmenoSouboru), TBitmap.Create);
      TBitmap(LB_Mesh.Items.Objects[LB_Mesh.Count - 1]).Assign(Bmp);
      Bmp.Free;
      // dalsi nastaveni
      NactiTiskParam(TiskPar, True);
      NastavSimulaci;
      Sit^.Soubor := JmenoSouboru;
      Sit^.Posun.X := 0;
      Sit^.Posun.Y := 0;
      Sit^.Posun.Z := -Sit^.MeshMin.Z;
      Sit^.Posun.W := 1;
        Sit^.Rotace.X := 0;
        Sit^.Rotace.Y := 0;
        Sit^.Rotace.Z := 0;
        Sit^.Rotace.W := 1;
      Sit^.Meritko.X := 1;
      Sit^.Meritko.Y := 1;
      Sit^.Meritko.Z := 1;
      Sit^.Meritko.W := 1;
        Sit^.Stavet := True;
        Sit^.Start  := RozjDraha;
      // umisteni komponenty DC_Mesh
      Sit^.DC_Mesh.Position.X := (Sit^.MeshMax.X + Sit^.MeshMin.X) / 2 + Sit^.Posun.X;
      Sit^.DC_Mesh.Position.Y := (Sit^.MeshMax.Y + Sit^.MeshMin.Y) / 2 + Sit^.Posun.Y;
      Sit^.DC_Mesh.Position.Z := Sit^.Posun.Z;
      Sit^.DC_Mesh.Scale.X := Sit^.Meritko.X;
      Sit^.DC_Mesh.Scale.Y := Sit^.Meritko.Y;
      Sit^.DC_Mesh.Scale.Z := Sit^.Meritko.Z;
      // umisteni Mesh
      Sit^.Mesh.Position.X := -(Sit^.MeshMax.X + Sit^.MeshMin.X) / 2;
      Sit^.Mesh.Position.Y := -(Sit^.MeshMax.Y + Sit^.MeshMin.Y) / 2;
      Sit^.Mesh.Position.Z := 0;
      // umisteni boxu kolem Meshe
      Sit^.MeshObal.Scale.X := MaxM.X - MinM.X;
      Sit^.MeshObal.Scale.Y := MaxM.Y - MinM.Y;
      Sit^.MeshObal.Scale.Z := MaxM.Z - MinM.Z;
      Sit^.MeshObal.Position.X := (MaxM.X + MinM.X) / 2;
      Sit^.MeshObal.Position.Y := (MaxM.Y + MinM.Y) / 2;
      Sit^.MeshObal.Position.Z := (MaxM.Z + MinM.Z) / 2;
      Sit^.MeshObal.Visible := True;
      // vypocet poctu trojuhelniku dane meshe
      J := 0;
      for I := 0 to Sit^.Mesh.MeshObjects.Count - 1 do
        J := J + Sit^.Mesh.MeshObjects[I].Vertices.Count;
      Sit^.PocTroj := J div 3;
      LB_Mesh.Selected[LB_Mesh.Count - 1] := Sit^.MeshObal.Visible;
      LB_MeshClick(Self);
      E_PozMeshChange(Self);
      PanelCisel.Visible := True;
      Zoom(Sit^.Mesh, zm_ISO);
    except
      ZrusMesh(MeshList.Count - 1);
      MessageDlg('Soubor má nestandardní formát!', mtError, [mbOk], 0);
    end;
  finally
    Screen.Cursor := AktKurzor;
  end;
end;

procedure TForm1.NactiProjekt(const JmenoSouboru: string);
var
  Nalezeno, Otevrit: Boolean;
  I, M, Precteno: Integer;
  Delka, PocSiti: Word;
  Text: array of Byte;
  Radek, Cesta: string;
  Sit: PMesh;
  F: file of Byte;
  V, Vu: array[0..3] of Byte;
function DekodujText: string;
 var
  I: Integer;
 begin
  Result := '';
  I := 0;
  Otevrit := Delka = Precteno;
  while Otevrit and (I < Precteno) and (Text[I] <> 0) do
  begin
   Result := Result + Chr(Text[I]);
   Inc(I);
  end;
 end;
begin
  Cesta := JmenoSouboru;
  // nacteni hlavicky a rozhodnuti, v jake forme byl projekt ulozen
  Form3.PrectiVerziSW(Application.ExeName, V[0], V[1], V[2], V[3]);
  AssignFile(F, Cesta);
  try
    FileMode := 0;
    Reset(F);
    if FileSize(F) > 14 then
    begin
      // hlavicka souboru
      Delka := 7;
      SetLength(Text, Delka);
      BlockRead(F, Text[0], Delka, Precteno);
      Radek := DekodujText;
      BlockRead(F, Vu, SizeOf(V));
      if (Radek = '3dt-v.') and (Vu[2] = V[2]) then
      begin
        // precteni, zda jsou ulozeny pouze odkazy (0) nebo vsechna data (255)
        BlockRead(F, Delka, SizeOf(Delka));
        if Delka = 0 then
        begin
          // ulozeny pouze odkazy na soubory a jejich konkretni nastaveni
          // pouzity profil 3D tiskarny
          BlockRead(F, Delka, SizeOf(Delka));
          SetLength(Text, Delka);
          BlockRead(F, Text[0], Delka, Precteno);
          Radek := DekodujText;
          // nastaveni konkretniho profilu (pokud existuje)
          Nalezeno := False;
          for I := 0 to CB_Profily.Items.Count - 1 do
          begin
            // nacteni profilu
            Nalezeno := Radek = CB_Profily.Items[I];
            if Nalezeno then
            begin
              CB_Profily.ItemIndex := I;
              CB_ProfilyClick(Self);
              Break;
            end;
          end;
          if not(Nalezeno) then
          begin
            MessageDlg(Format('Profil parametrů tisku ''%s'' nebyl nalezen!', [Radek]), mtError, [mbOk], 0);
            Otevrit := False;
          end;
          if Otevrit then
          begin
            // pocet siti
            BlockRead(F, PocSiti, SizeOf(PocSiti));
            // jednotlive site a jejich nastaveni
            M := 0;
            while (M < PocSiti) and Otevrit do
            begin
              // soubor site
              BlockRead(F, Delka, SizeOf(Delka));
              SetLength(Text, Delka);
              BlockRead(F, Text[0], Delka, Precteno);
              Radek := DekodujText;
              Nalezeno := FileExists(Radek);
              if not(Nalezeno) then
              begin
                Cesta := ExtractFilePath(Cesta);
                Radek := Cesta + ExtractFileName(Radek);
                Nalezeno := FileExists(Radek);
              end;
              if Nalezeno then
              begin
                NactiSoubor(Radek);
              end else begin
                MessageDlg(Format('Soubor ''%s'' nebyl nalezen!', [Radek]), mtError, [mbOk], 0);
                Otevrit := False;
              end;
              if Otevrit then
              begin
                Sit := MeshList.Items[M];
                BlockRead(F, Sit^.Posun, SizeOf(Sit^.Posun));
                BlockRead(F, Sit^.Rotace, SizeOf(Sit^.Rotace));
                BlockRead(F, Sit^.Meritko, SizeOf(Sit^.Meritko));
                BlockRead(F, Sit^.Stavet, SizeOf(Sit^.Stavet));
                BlockRead(F, Sit^.Start, SizeOf(Sit^.Start));
                LB_MeshClick(Self);
                E_PozMeshChange(Self);
              end;
              Inc(M);
            end;
          end;
        end else begin
          // ulozeny kompletni obsah projektu

        end;
      end else
        MessageDlg('Soubor projektu nemá správný formát, nebo je pro jinou verzi programu!', mtError, [mbOk], 0);
    end else
      MessageDlg('Soubor projektu nemá správný formát!', mtError, [mbOk], 0);
  finally
    CloseFile(F);
  end;
end;

procedure TForm1.Cekej(const Ms: Int64);
var
  Cas: Int64;
begin
  Cas := GetTickCount;
  while Abs(GetTickCount - Cas) < Ms do
    Application.ProcessMessages;
end;

procedure TForm1.Zoom(RootObject: TGLBaseSceneObject; Mode: TZoomMode);
const
  SIN_45 = 0.707106781;
  AABB_SIZE_FACTOR = 1.01;
  INITIAL_DISTANCE = 10000;
  TOP_BOTTOM_PERSPECTIVE_DELTA = 0.001;
var
  DX, DY, DZ, Distance, VL: Double;
  AABB: TAABB;
  BSphere: TBSphere;
  V: TVector4f;
  BoxCenter: TAffineVector;
begin
  Distance := Min(GLSceneViewer.Width, GLSceneViewer.Height);
  Distance := GLCamera.FocalLength * 2 * 96 / (25.4 * Distance);
  AABB := RootObject.AxisAlignedBoundingBoxAbsolute(True, False);
  case Mode of
    zm_XY..zm_YZ: begin
      DX := Abs(AABB.Max.X - AABB.Min.X);
      DY := Abs(AABB.Max.Y - AABB.Min.Y);
      DZ := Abs(AABB.Max.Z - AABB.Min.Z);
      BoxCenter := VectorAdd(VectorScale(AABB.Max, 0.5), VectorScale(AABB.Min, 0.5));
      case Mode of
        zm_XY: begin
          Distance := Distance * (Max(DX, DY) * AABB_SIZE_FACTOR);
          if CB_OrtoProjekce.Checked then
          begin
            GLCamera.FocalLength := Min(GLSceneViewer.Width, GLSceneViewer.Height) / Max(DX, DY) * 0.05;
            //TGLSceneBuffer.ScreenVectorIntersectWithPlane(

            GLCamera.TargetObject.Position.SetPoint(BoxCenter.X, BoxCenter.Y, 0);
            GLCamera.Position.SetPoint(BoxCenter.X, BoxCenter.Y, 10000);
          end else begin
            GLCamera.TargetObject.Position.SetPoint(BoxCenter.X, BoxCenter.Y, AABB.Max.Z);
            GLCamera.Position.X := GLCamera.TargetObject.Position.X;
            GLCamera.Position.Y := GLCamera.TargetObject.Position.Y + TOP_BOTTOM_PERSPECTIVE_DELTA;
            GlCamera.Position.Z := GLCamera.TargetObject.Position.Z + INITIAL_DISTANCE;
          end;
          GLCamera.Direction.SetVector(0, 0, -1, 0);
          GLCamera.Up.SetVector(0, 1, 0, 0);
        end;
        zm_XZ: begin
          Distance := Distance * (Max(DX, DZ) * AABB_SIZE_FACTOR);
          if CB_OrtoProjekce.Checked then
          begin
            GLCamera.FocalLength := Min(GLSceneViewer.Width, GLSceneViewer.Height) / Max(DX, DZ) * 0.05;
            GLCamera.TargetObject.Position.SetPoint(BoxCenter.X, 0, BoxCenter.Z);
            GLCamera.Position.SetPoint(BoxCenter.X, -10000, BoxCenter.Z);
          end else begin
            GLCamera.TargetObject.Position.SetPoint(BoxCenter.X, AABB.Min.Y, BoxCenter.Z);
            GLCamera.Position.X := GLCamera.TargetObject.Position.X;
            GLCamera.Position.Y := GLCamera.TargetObject.Position.Y - INITIAL_DISTANCE;
            GLCamera.Position.Z := GLCamera.TargetObject.Position.Z;
          end;
          GLCamera.Direction.SetVector(0, 1, 0, 0);
          GLCamera.Up.SetVector(0, 0, 1, 0);
        end;
        zm_YZ: begin
          Distance := Distance * (Max(DY, DZ) * AABB_SIZE_FACTOR);
          if CB_OrtoProjekce.Checked then
          begin
            GLCamera.FocalLength := Min(GLSceneViewer.Width, GLSceneViewer.Height) / Max(DY, DZ) * 0.05;
            GLCamera.TargetObject.Position.SetPoint(0, BoxCenter.Y, BoxCenter.Z);
            GLCamera.Position.SetPoint(10000, BoxCenter.Y, BoxCenter.Z);
          end else begin
            GLCamera.TargetObject.Position.SetPoint(AABB.Min.X, BoxCenter.Y, BoxCenter.Z);
            GLCamera.Position.X := GLCamera.TargetObject.Position.X + INITIAL_DISTANCE;
            GLCamera.Position.Y := GLCamera.TargetObject.Position.Y;
            GLCamera.Position.Z := GLCamera.TargetObject.Position.Z;
          end;
          GLCamera.Direction.SetVector(-1, 0, 0, 0);
          GLCamera.Up.SetVector(0, 0, 1, 0);
        end;
      end;
    end;
    zm_ISO: begin
      AABBToBSphere(AABB, BSphere);
      Distance := Distance * BSphere.Radius * 2;
      if CB_OrtoProjekce.Checked then
        GLCamera.FocalLength := Min(GLSceneViewer.Width, GLSceneViewer.Height) / (BSphere.Radius * 2) * 0.05;
      GLCamera.TargetObject.Position.SetPoint(BSphere.Center.X, BSphere.Center.Y, BSphere.Center.Z);
      GLCamera.Position.X := GLCamera.TargetObject.Position.X - INITIAL_DISTANCE;
      GLCamera.Position.Y := GLCamera.TargetObject.Position.Y - INITIAL_DISTANCE * 2;
      GLCamera.Position.Z := GLCamera.TargetObject.Position.Z + INITIAL_DISTANCE * SIN_45;
      GLCamera.Direction.SetVector(0, 1, 0, 0);
      GLCamera.Up.SetVector(0, 0, 1);
    end;
    zm_ALL: begin
      AABB := RootObject.AxisAlignedBoundingBox(True);
      AABBToBSphere(AABB, BSphere);
      Distance := Distance * BSphere.Radius * 2;
      if CB_OrtoProjekce.Checked then
        GLCamera.FocalLength := Min(GLSceneViewer.Width, GLSceneViewer.Height) / (BSphere.Radius * 2) * 0.05;
      GLCamera.TargetObject.Position.SetPoint(BSphere.Center.X, BSphere.Center.Y, BSphere.Center.Z);
    end;
  end;
  if not(CB_OrtoProjekce.Checked) then
  begin
    V := VectorSubtract(GLCamera.AbsolutePosition, GLCamera.TargetObject.AbsolutePosition);
    VL := VectorLength(v);
    if VL = Distance then Exit;
    ScaleVector(V, Distance / VL);
    AddVector(V, GLCamera.TargetObject.AbsolutePosition);
    GLCamera.Position.AsVector := V;
  end;
end;

function TForm1.IntToShiftState(const I: Integer): TShiftState;
begin
  Result := [];
  if (I and $01) = $01 then Result := Result + [ssShift];
  if (I and $02) = $02 then Result := Result + [ssCtrl];
  if (I and $04) = $04 then Result := Result + [ssAlt];
  if (I and $08) = $08 then Result := Result + [ssRight];
  if (I and $10) = $10 then Result := Result + [ssMiddle];
  if (I and $20) = $20 then Result := Result + [ssLeft];
end;

function TForm1.ShiftStateToInt(const S: TShiftState): Integer;
begin
  Result := 0;
  if ssShift  in S then Result := Result or $01;
  if ssCtrl   in S then Result := Result or $02;
  if ssAlt    in S then Result := Result or $04;
  if ssRight  in S then Result := Result or $08;
  if ssMiddle in S then Result := Result or $10;
  if ssLeft   in S then Result := Result or $20;
end;

procedure TForm1.NastavCADman;
begin
  // nastaveni prvku v nastaveni programu dle promenne ParSW
  CB_Posun_L.Checked := ssLeft in ParSW.Posun;
  CB_Posun_M.Checked := ssMiddle in ParSW.Posun;
  CB_Posun_R.Checked := ssRight in ParSW.Posun;
  CB_Posun_A.Checked := ssAlt in ParSW.Posun;
  CB_Posun_C.Checked := ssCtrl in ParSW.Posun;
  CB_Posun_S.Checked := ssShift in ParSW.Posun;
    CB_Rotace_L.Checked := ssLeft in ParSW.Rotace;
    CB_Rotace_M.Checked := ssMiddle in ParSW.Rotace;
    CB_Rotace_R.Checked := ssRight in ParSW.Rotace;
    CB_Rotace_A.Checked := ssAlt in ParSW.Rotace;
    CB_Rotace_C.Checked := ssCtrl in ParSW.Rotace;
    CB_Rotace_S.Checked := ssShift in ParSW.Rotace;
  CB_VybMesh_L.Checked := ssLeft in ParSW.VyberMesh;
  CB_VybMesh_M.Checked := ssMiddle in ParSW.VyberMesh;
  CB_VybMesh_R.Checked := ssRight in ParSW.VyberMesh;
  CB_VybMesh_A.Checked := ssAlt in ParSW.VyberMesh;
  CB_VybMesh_C.Checked := ssCtrl in ParSW.VyberMesh;
  CB_VybMesh_S.Checked := ssShift in ParSW.VyberMesh;
    CB_PosMesh_L.Checked := ssLeft in ParSW.PosunMesh;
    CB_PosMesh_M.Checked := ssMiddle in ParSW.PosunMesh;
    CB_PosMesh_R.Checked := ssRight in ParSW.PosunMesh;
    CB_PosMesh_A.Checked := ssAlt in ParSW.PosunMesh;
    CB_PosMesh_C.Checked := ssCtrl in ParSW.PosunMesh;
    CB_PosMesh_S.Checked := ssShift in ParSW.PosunMesh;
  CB_Zoom.Checked := ParSW.SmerZoom = -1;
  CB_OrtoProjekce.Checked := ParSW.OrtoProj;
    RB_AppData.Checked  := ParSW.SystCesta;
    RB_CestaPrg.Checked := not(ParSW.SystCesta);
    CB_OrtoProjekceClick(Self);
end;

procedure TForm1.UrciMinMax(const Sit: PMesh; var MinM, MaxM: TVector4f; const Lokalne, Komplet, MeshMinMax: Boolean);
var
  I, J, K: Integer;
  MinI, MinJ, MaxI, MaxJ: array[0..2] of Integer;
  V: TVector3f;
  Pom: TVector4f;
 procedure Porovnej;
 begin
  // X min / max
  if Pom.X < MinM.X then MinM.X := Pom.X;
  if Pom.X > MaxM.X then MaxM.X := Pom.X;
  // Y min / max
  if Pom.Y < MinM.Y then MinM.Y := Pom.Y;
  if Pom.Y > MaxM.Y then MaxM.Y := Pom.Y;
  // Z min / max
  if Pom.Z < MinM.Z then MinM.Z := Pom.Z;
  if Pom.Z > MaxM.Z then MaxM.Z := Pom.Z;
 end;
begin
  MinM.W := 1;
  MaxM.W := 1;
  if Lokalne then
  begin
    // hleda se min/max jen pro jednu mesh (tj. lokalne)
    for K := 0 to 2 do
    begin
      MinM.V[K] := Infinity;
      MaxM.V[K] := NegInfinity;
    end;
  end;
  if Komplet then
  begin
    // nalezeni minima a maxima souradnic
    for I := 0 to Sit^.Mesh.MeshObjects.Count - 1 do
      for J := 0 to Sit^.Mesh.MeshObjects[I].Vertices.Count - 1 do
      begin
        V := Sit^.Mesh.LocalToAbsolute(Sit^.Mesh.MeshObjects[I].Vertices.Items[J]);
        for K := 0 to 2 do
        begin
          if MinM.V[K] > V.V[K] then begin MinM.V[K] := V.V[K]; MinI[K] := I; MinJ[K] := J; end;
          if V.V[K] > MaxM.V[K] then begin MaxM.V[K] := V.V[K]; MaxI[K] := I; MaxJ[K] := J; end;
        end;
      end;
    if MeshMinMax then
    begin
      // pocita se pouze pri nacteni nove site, pak uz ne - problem je, ze pri rotaci site uz nemusi vyjit "telesova uhlopricka"
      for K := 0 to 2 do
      begin
        Sit^.MeshMin.V[K] := Sit^.Mesh.MeshObjects[MinI[K]].Vertices.Items[MinJ[K]].V[K];
        Sit^.MeshMax.V[K] := Sit^.Mesh.MeshObjects[MaxI[K]].Vertices.Items[MaxJ[K]].V[K];
      end;
    end;
  end else begin
    // levy, predni, dolni roh
    Pom := Sit^.Mesh.LocalToAbsolute(VectorMake(Sit^.MeshMin.X, Sit^.MeshMin.Y, Sit^.MeshMin.Z, 1));
    Porovnej;
    // pravy, predni, dolni roh
    Pom := Sit^.Mesh.LocalToAbsolute(VectorMake(Sit^.MeshMax.X, Sit^.MeshMin.Y, Sit^.MeshMin.Z, 1));
    Porovnej;
    // pravy, zadni, dolni roh
    Pom := Sit^.Mesh.LocalToAbsolute(VectorMake(Sit^.MeshMax.X, Sit^.MeshMax.Y, Sit^.MeshMin.Z, 1));
    Porovnej;
    // levy, zadni, dolni roh
    Pom := Sit^.Mesh.LocalToAbsolute(VectorMake(Sit^.MeshMin.X, Sit^.MeshMax.Y, Sit^.MeshMin.Z, 1));
    Porovnej;
    // levy, predni, horni roh
    Pom := Sit^.Mesh.LocalToAbsolute(VectorMake(Sit^.MeshMin.X, Sit^.MeshMin.Y, Sit^.MeshMax.Z, 1));
    Porovnej;
    // pravy, predni, horni roh
    Pom := Sit^.Mesh.LocalToAbsolute(VectorMake(Sit^.MeshMax.X, Sit^.MeshMin.Y, Sit^.MeshMax.Z, 1));
    Porovnej;
    // pravy, zadni, horni roh
    Pom := Sit^.Mesh.LocalToAbsolute(VectorMake(Sit^.MeshMax.X, Sit^.MeshMax.Y, Sit^.MeshMax.Z, 1));
    Porovnej;
    // levy, zadni, horni roh
    Pom := Sit^.Mesh.LocalToAbsolute(VectorMake(Sit^.MeshMin.X, Sit^.MeshMax.Y, Sit^.MeshMax.Z, 1));
    Porovnej;
  end;
end;

// reakce na udalosti Form1

procedure TForm1.GLSceneViewerMouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
  M, Index: Integer;
  Vzd, Min: Double;
  PohlStart, PohlVektor, Bod, Normala: TVector4f;
  Sit: PMesh;
begin
  Mdx := X;
  Mdy := Y;
  if (Shift = ParSW.VyberMesh) or (Shift = (ParSW.VyberMesh - [ssCtrl])) then
  begin
    // vyber mesh objektu
    // zjisteni, ktery objekt byl vybran pomoci mysi
    if GLCamera.CameraStyle = csPerspective then
    begin
      SetVector(PohlStart, GLCamera.AbsolutePosition);
      SetVector(PohlVektor, GLSceneViewer.Buffer.ScreenToVector(AffineVectorMake(X, GLSceneViewer.Height - Y, 0)));
    end else begin
      SetVector(PohlStart, GLSceneViewer.Buffer.OrthoScreenToWorld(X, GLSceneViewer.Height - Y), 1);
      VectorSubtract(GLCamera.TargetObject.AbsolutePosition, GLCamera.AbsolutePosition, PohlVektor);
    end;
    NormalizeVector(PohlVektor);
    // prochazeni objekty a hledani pruseciku s vektorem pohledu
    Index := -1;
    Min := Infinity;
    for M := 0 to MeshList.Count - 1 do
    begin
      Sit := MeshList.Items[M];
      if Sit^.Mesh.RayCastIntersect(PohlStart, PohlVektor, @Bod, @Normala) then
      begin
        Vzd := VectorDistance(Bod, PohlStart);
        if Vzd < Min then
        begin
          Min := Vzd;
          Index := M;
        end;
      end;
      if not(ssCtrl in Shift) then
      begin
        Sit^.MeshObal.Visible := False;
        LB_Mesh.Selected[M]   := False;
      end;
    end;
    if Index >= 0 then
    begin
      Sit := MeshList.Items[Index];
      Sit^.MeshObal.Visible := not(Sit^.MeshObal.Visible);
      LB_Mesh.Selected[Index] := Sit^.MeshObal.Visible;
      LB_MeshClick(Self);
    end;
  end;
end;

procedure TForm1.GLSceneViewerMouseMove(Sender: TObject;
  Shift: TShiftState; X, Y: Integer);
var
  M: Integer;
  VT, VTA, VTS, VTW: TVector4f;
  Dx, Dy, StredX, StredY, MinDim: Integer;
  dRoll, RotRatio: Double;
  dPitch, dTurn: Double;
  Sit: PMesh;
begin
  Dx  := Mdx - X;
  Dy  := Mdy - Y;
  Mdx := X;
  Mdy := Y;
  if (Abs(Dx) > 50) or (Abs(Dy) > 50) then
    Exit;
  if Shift = ParSW.PosunMesh then
  begin
    // posun pouze mesh objektu
    // priprava vektoru na vlastni posun objektu
    VT := GLCamera.TargetObject.Position.AsVector;
    VTA := GLScene.Objects.LocalToAbsolute(VT);
    VTS := GLSceneViewer.Buffer.WorldToScreen(VTA);
    VTS.X := VTS.X + Dx;
    VTS.y := VTS.Y - Dy;
    VTW := GLSceneViewer.Buffer.ScreenToWorld(VTS);
    // prochazeni objekty a hledani vybranych siti k posunuti
    for M := 0 to MeshList.Count - 1 do
    begin
      if LB_Mesh.Selected[M] then
      begin
        Sit := MeshList.Items[M];
        Sit^.Posun.X := Sit^.Posun.X - (VTW.X - VT.X);
        Sit^.Posun.Y := Sit^.Posun.Y - (VTW.Y - VT.Y);
        AutZmena := True;
        if E_PolX.Text <> '' then E_PolX.Text := Format('%.3f', [Sit^.Posun.X], FS);
        if E_PolY.Text <> '' then E_PolY.Text := Format('%.3f', [Sit^.Posun.Y], FS);
        AutZmena := False;
        E_PozMeshChange(Self);
      end;
    end;
  end;
  if Shift = ParSW.Posun then
  begin
    // posun
    VT := GLCamera.TargetObject.Position.AsVector;
    VTA := GLScene.Objects.LocalToAbsolute(VT);
    VTS := GLSceneViewer.Buffer.WorldToScreen(VTA);
    VTS.X := VTS.X + Dx;
    VTS.y := VTS.Y - Dy;
    VTW := GLSceneViewer.Buffer.ScreenToWorld(VTS);
    GLCamera.TargetObject.Position.AsVector := VTW;
    GLCamera.Position.AsVector := VectorAdd(GLCamera.Position.AsVector, VectorSubtract(VTW, VT));
//   glCamera.TransformationChanged;
  end;
  if Shift = ParSW.Rotace then
  begin
    // for a natural feel orbit and roll combined this is the exact behaviour as in AutoCad...
    StredX := GLSceneViewer.Width div 2;
    StredY := GLSceneViewer.Height div 2;
    // Calculate roll angle
    dRoll := RadToDeg(ArcTan2(Y - StredY, X - StredX) - ArcTan2(Mdy - StredY, Mdx - StredX));

    // dRoll always between -180° and 180°
    if dRoll < -180 then dRoll := dRoll + 360;
    if dRoll >  180 then dRoll := dRoll - 360;

    // Calculate angles
    MinDim := Min(GLSceneViewer.Width, GLSceneViewer.Height);
    // Moving mouse top to bottom will rotate for 180°
    dPitch := 180 * Dy / MinDim;
    dTurn  := 180 * Dx / MinDim;

    // Scale dRoll when orbitting
    // Center screen: 0
    // Towards the extends: 0.5
    rotRatio := Sqrt(Power((Mdx - StredX), 2) + Power((Mdy - StredY), 2)) / MinDim / 2;

    // Orbit
    GLCamera.MoveAllAroundTarget(dPitch, dTurn);
    // Roll
    GLCamera.Roll(rotRatio*dRoll);

    GLCamera.MoveAroundTarget(Dy, Dx);
  end;
end;

procedure TForm1.TB_RezyClick(Sender: TObject);
var
  Chyba, Start: Boolean;
  I, J, L, M, Vrstva, PocBodu, PocDat, PoSpirale: Integer;
  X, Y, Z, Lx, Ly, Lz, MaxV: Double;
  MinM, MaxM: TVector4f;
  Sit: PMesh;
  Rez: TDataRezu;
  Kurzor: TCursor;
begin
  NactiTiskParam(TiskPar, True);
  if TiskPar.TlVrstvy <= 0 then
  begin
    MessageDlg('Tloušťka vrstvy musí být větší než nula.', mtError, [mbOK], 0);
    Exit;
  end;
  Kurzor := Screen.Cursor;
  Screen.Cursor := crHourGlass;
  try
    // pripadne zruseni drive vypocitanych dat
    for I := 0 to Length(Lines) - 1 do
    begin
      Lines[I].L.Nodes.Clear;
      Lines[I].L.Free;
    end;
    Finalize(Lines);
    // nacteni pocatecniho nastaveni
    // vypocet minimalnich a maximalnich souradnic v datech siti
    for I := 0 to 2 do
    begin
      MinM.V[I] := Infinity;
      MaxM.V[I] := NegInfinity;
    end;
    MaxV := 0;  // maximalni vyska Meshe;
    for M := 0 to MeshList.Count - 1 do
    begin
      Sit := MeshList.Items[M];
      if Sit^.Stavet then
      begin
        Sit^.Mesh.Visible := False;
        TBitmap(LB_Mesh.Items.Objects[M]).Canvas.CopyRect(Rect(0, 0, 21, 15), I_MeshStatus.Canvas, Rect(22, 0, 43, 15));
        LB_Mesh.Refresh;
        // hledani nejvyssi site v danem natoceni a meritku
        UrciMinMax(Sit, MinM, MaxM, False, False, False);
        Z := MaxM.Z;/// - MinM.Z;
        if MaxV < Z then
          MaxV := Z;
      end;
    end;
    Vrstva := 1;
    X := MinM.X - 1;
    Y := MinM.Y - 1;
    Z := MinM.Z + TiskPar.TlVrstvy * Vrstva;
///    Z := TiskPar.TlVrstvy;
    Chyba := False;
    PoSpirale := 0;
    L := -1;  // index pro strukturu Lines[L]
    Lx := 0;  Ly := 0;  Lz := 0;
    Start := True;
    while (Z < (MaxV + TiskPar.TlVrstvy)) and not(Chyba) do
    begin
      InfoPanel.Caption := Format('  Generuji řez Z: %3.2f', [Z], FS);
      InfoPanel.Refresh;
      Application.ProcessMessages;
      Chyba := not(VypocitejRez(Vrstva, Rez, Start));
      if Chyba then
        ZrusDataRezu(Rez)
      else begin
        if (Vrstva = 1) and not(Chyba) and not(Start) then
        begin
          I := TiskPar.Optimal;
          TiskPar.Optimal := 2;
          OptimalizujData(Rez, X, Y);
          TiskPar.Optimal := I;
          for I := 0 to 1 do
          begin
            PocDat  := Length(Rez);
            PocBodu := Length(Rez[PocDat - 1].Body);
            X := Rez[PocDat - 1].Body[PocBodu - 1].X;
            Y := Rez[PocDat - 1].Body[PocBodu - 1].Y;
            Chyba := not(VypocitejRez(Vrstva, Rez, False));
            if Chyba then
              ZrusDataRezu(Rez)
            else
              OptimalizujData(Rez, X, Y);
          end;
        end else
          OptimalizujData(Rez, X, Y);
      end;

      if (Length(Rez) > 0) and (Length(Rez[Length(Rez) - 1].Body) > 0) then
      begin
        // rozhodnuti, zda muze byt tisknuto po spirale - predpokladem je jen jeden uzavreny polygon na vrstvu
        if (Length(Rez) = 1) and (Rez[0].Priznak > OTEV_PROFIL) and (TiskPar.Spirala) then
        begin
          if PoSpirale > 0 then
            Rez[0].Priznak := SPIR_PROFIL;
          Inc(PoSpirale);
        end;
        // ulozeni a vykresleni polygonu rezu
        for I := 0 to Length(Rez) - 1 do
        begin
          // vypocet delky polygonu rezu
          Rez[I].Delka := 0;
          for J := 1 to Length(Rez[I].Body) - 1 do
            Rez[I].Delka := Rez[I].Delka + Hypot((Rez[I].Body[J].X - Rez[I].Body[J-1].X), (Rez[I].Body[J].Y - Rez[I].Body[J-1].Y));
          // priprava pro vykresleni rezu ve 3D
          Inc(L);
          SetLength(Lines, L + 1);
          Lines[L].Vrstva := Vrstva;
          Lines[L].Z := MinM.Z + TiskPar.TlVrstvy * Vrstva;
          Lines[L].G := 1;
          Lines[L].Delka := Rez[I].Delka;
          Lines[L].L := TGLLines.CreateAsChild(ModelCube);
          Lines[L].L.NodesAspect := lnaInvisible;
          if Rez[I].Priznak <= UZAV_PROFIL then
          begin
            if L > 0 then
            begin
              // prejezd rychloposuvem (krome prvniho polygonu a pohybu po spirale)
              Lines[L].L.LineColor.Color := ConvertWinColor(clGray);
              Lines[L].L.LinePattern := $0F0F;
              Lines[L].L.LineWidth := 1;
              Lines[L].G := 0;
              if Abs(Z - Lz) > NULA_REAL then
              begin
                // nejprve vypocet celkove delky prejezdu do nove vrstvy (je jina, nez bylo vyse ulozeno)
                Lines[L].Delka := Z + TiskPar.ZdvihZ - Lz + Hypot(Rez[I].Body[0].X - Lx, Rez[I].Body[0].Y - Ly) + TiskPar.ZdvihZ;
                // rozhodnuti, zda se prejizdi rychloposuvem, nebo se jedna o kratky prejezd a pokracuje se pracovnim posuvem
                if (Lines[L].Delka < TiskPar.KratkyXY) and TiskPar.NevypEvXY and TiskPar.NevypEvZ then
                begin
                  // jedna se jen o kratky prejezd rychloposuvem a je povoleno nevypinat extruder
                  // => vrati se funkce G1
                  Lines[L].L.LineColor.Color := ConvertWinColor(clRed);
                  Lines[L].L.LinePattern := $FFFF;
                  Lines[L].L.LineWidth := 2;
                  Lines[L].G := 1;
                end;
{
                  // jedna se o prejezd pracovnim posuvem => nejprve dojeti v XY, pak Z
                  // prejezd v XY
                  Lines[L].L.Nodes.AddNode(Rez[I].Body[0].X, Rez[I].Body[0].Y, Lz);


                  // prejezd do dalsi vrstvy nejprve v Z ( + pripadny zdvih)
                  Lines[L].L.Nodes.AddNode(Rez[I].Body[0].X, Rez[I].Body[0].Y, Z + TiskPar.ZdvihZ);
                  if TiskPar.ZdvihZ > NULA_REAL then
                  begin
                    // prejezd zpet do tiskove urovne
                    Lines[L].L.Nodes.AddNode(Rez[I].Body[0].X, Rez[I].Body[0].Y, Z);
                  end;
                end else begin
}
                // jedna se o prejezd rychloposuvem => nejprve Z, pak XY
                // prejezd do dalsi vrstvy nejprve v Z ( + pripadny zdvih)
                Lines[L].L.Nodes.AddNode(Lx, Ly, Lz);
                Lines[L].L.Nodes.AddNode(Lx, Ly, Z + TiskPar.ZdvihZ);
                // prejezd v XY
                Lines[L].L.Nodes.AddNode(Rez[I].Body[0].X, Rez[I].Body[0].Y, Z + TiskPar.ZdvihZ);
                if TiskPar.ZdvihZ > NULA_REAL then
                begin
                  // prejezd zpet do tiskove urovne
                  Lines[L].L.Nodes.AddNode(Rez[I].Body[0].X, Rez[I].Body[0].Y, Z);
                end;
{
                end;
}                
              end else begin
                // pouze prejezd rychloposuvem ve vrstve
                Lines[L].L.Nodes.AddNode(Lx, Ly, Lz);
                if TiskPar.ZdvihZ > NULA_REAL then
                begin
                  // prejezd ve vrstve se zdvihem
                  Lines[L].L.Nodes.AddNode(Lx, Ly, Lz + TiskPar.ZdvihZ);
                  Lines[L].L.Nodes.AddNode(Rez[I].Body[0].X, Rez[I].Body[0].Y, Lz + TiskPar.ZdvihZ);
                  Lines[L].L.Nodes.AddNode(Rez[I].Body[0].X, Rez[I].Body[0].Y, Z);
                  Lines[L].Delka := Hypot(Rez[I].Body[0].X - Lx, Rez[I].Body[0].Y - Ly) + 2 * TiskPar.ZdvihZ;
                  if (Lines[L].Delka < TiskPar.KratkyXY) and TiskPar.NevypEvXY then
                  begin
                    // jedna se jen o kratky prejezd rychloposuvem a je povoleno nevypinat extruder
                    // => vrati se funkce G1
                    Lines[L].L.LineColor.Color := ConvertWinColor(clRed);
                    Lines[L].L.LinePattern := $FFFF;
                    Lines[L].L.LineWidth := 2;
                    Lines[L].G := 1;
                  end;
                end else begin
                  Lines[L].L.Nodes.AddNode(Rez[I].Body[0].X, Rez[I].Body[0].Y, Z);
                  Lines[L].Delka := Hypot(Rez[I].Body[0].X - Lx, Rez[I].Body[0].Y - Ly);
                  if (Lines[L].Delka < TiskPar.KratkyXY) and TiskPar.NevypEvXY then
                  begin
                    // jedna se jen o kratky prejezd rychloposuvem a je povoleno nevypinat extruder
                    // => vrati se funkce G1
                    Lines[L].L.LineColor.Color := ConvertWinColor(clRed);
                    Lines[L].L.LinePattern := $FFFF;
                    Lines[L].L.LineWidth := 2;
                    Lines[L].G := 1;
                  end;
                end;
              end;  // prejezd rychloposuvem
              Inc(L);
              SetLength(Lines, L + 1);
              Lines[L].Vrstva := Vrstva;
              Lines[L].Z := MinM.Z + TiskPar.TlVrstvy * Vrstva;
              Lines[L].G := 1;
              Lines[L].Delka := Rez[I].Delka;
              Lines[L].L := TGLLines.CreateAsChild(ModelCube);
              Lines[L].L.NodesAspect := lnaInvisible;
            end;
          end else begin
            // pohyb po spirale - prechod do dalsi vrstvy

          end;
          Lines[L].L.LineColor.Color := ConvertWinColor(clRed);
          Lines[L].L.LinePattern := $FFFF;
          Lines[L].L.LineWidth := 2;
          if Rez[I].Priznak <= UZAV_PROFIL then  // pri tisku po spirale vynechavam prvni bod
            Lines[L].L.Nodes.AddNode(Rez[I].Body[0].X, Rez[I].Body[0].Y, Z)
          else
            Lines[L].L.Nodes.AddNode(Lx, Ly, Lz);
          // prochazeni ostatnimi body
          for J := 1 to Length(Rez[I].Body) - 1 do
          begin
            // vykresleni 3D dat rezu
            Lx := Rez[I].Body[J].X;
            Ly := Rez[I].Body[J].Y;
            if Rez[I].Priznak = SPIR_PROFIL then
            begin
              // nutno pocitat pohyb v souradnici Z po spirale
              Lz := Lz + Hypot( (Rez[I].Body[J].X - Rez[I].Body[J-1].X),
                                (Rez[I].Body[J].Y - Rez[I].Body[J-1].Y)) /
                         Rez[I].Delka * TiskPar.TlVrstvy;
              if Lz > (Z + TiskPar.TlVrstvy) then
                Lz := Z + TiskPar.TlVrstvy;
            end else
              Lz := Z;  // tisk v dane rovine Z
            Lines[L].L.Nodes.AddNode(Lx, Ly, Lz);
          end;
        end;
        PocDat  := Length(Rez);
        PocBodu := Length(Rez[PocDat - 1].Body);
        X := Rez[PocDat - 1].Body[PocBodu - 1].X;
        Y := Rez[PocDat - 1].Body[PocBodu - 1].Y;
      end;
      if Start then
      begin
        // v prvni vrstve se pocita nejprve rozjezdova draha, dale uz se pocitat nebude
        Start := False;
        X := MinM.X - 1;
        Y := MinM.Y - 1;
      end else begin
        // priprava na dalsi rez
        Inc(Vrstva);
        Z := MinM.Z + TiskPar.TlVrstvy * Vrstva;
      end;
    end;
    InfoPanel.Caption := '';

    LB_Mesh.ClearSelection;
    LB_MeshClick(Self);

    P_Rezy.Visible := True;
    RB_RezyVseClick(Self);

    P_Stat.Visible := True;
  finally
    Screen.Cursor := Kurzor;
    PageControl.ActivePage := TS_CAD;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  I: Integer;
  Cesta, Profil: string;
  Cfg: TIniFile;
begin
  // uvodni inicializace
  FS := TFormatSettings.Create(LOCALE_SYSTEM_DEFAULT);
  FS.DecimalSeparator := '.';
  MeshList := TList.Create;
  ResetSimulace;
  // nacteni seznamu konfiguracnich profilu
  CB_Profily.Items.Clear;
  ParSW.SystCesta := False;
  Cesta := CfgSoubor(ParSW.SystCesta);
  if FileExists(Cesta) then
  begin
    // otevreni souboru a nacteni parametru, kde ma byt cfg soubor ulozen
    Cfg := TIniFile.Create(Cesta);
    try
      // nacteni parametru programu
      ParSW.SystCesta := Cfg.ReadBool(NAST_PROGR, 'AppData', True);
    finally
      Cfg.Free;
    end;
  end;
  Cesta := CfgSoubor(ParSW.SystCesta);
  if FileExists(Cesta) then
  begin
    // otevreni souboru a nacteni parametru
    Cfg := TIniFile.Create(Cesta);
    try
      // nacteni parametru SW
      ParSW.Posun     := IntToShiftState(Cfg.ReadInteger(NAST_PROGR, 'Posun', 16));
      ParSW.Rotace    := IntToShiftState(Cfg.ReadInteger(NAST_PROGR, 'Rotace', 8));
      ParSW.VyberMesh := IntToShiftState(Cfg.ReadInteger(NAST_PROGR, 'VyberObj', 34));
      ParSW.PosunMesh := IntToShiftState(Cfg.ReadInteger(NAST_PROGR, 'PosunObj', 33));
      ParSW.SmerZoom  := Cfg.ReadInteger(NAST_PROGR, 'Zoom', 1);
      if not((ParSW.SmerZoom = 1) or (ParSW.SmerZoom = -1)) then
        ParSW.SmerZoom := 1;
      ParSW.OrtoProj  := Cfg.ReadBool(NAST_PROGR, 'Orto', True);
      NastavCADman;
      // nacteni profilu 3D tisku
      Cfg.ReadSections(CB_Profily.Items);
      Profil := Cfg.ReadString(VYCH_PROFIL, 'Profil', '');
      for I := 0 to CB_Profily.Items.Count - 1 do
      begin
        // vymazani polozek vychozi profil a nastaveni programu ze seznamu profilu
        if (CB_Profily.Items[I] = VYCH_PROFIL) or (CB_Profily.Items[I] = NAST_PROGR) then
          CB_Profily.Items.Delete(I);
        // nacteni vychoziho profilu
        if (Profil <> '') and (Profil = CB_Profily.Items[I]) then
        begin
          CB_Profily.ItemIndex := I;
          CB_ProfilyClick(Self);
        end;
      end;
    finally
      Cfg.Free;
    end;
  end else begin
    AutZmena := False;
    NactiTiskParam(TiskPar, True);
    CB_CADmanClick(Self);
    CB_ZoomClick(Self);
    CB_OrtoProjekceClick(Self);
    MessageDlg(Format('Soubor s parametry tisku ''%s'' nebyl nalezen!', [ExtractFileName(Cesta)]), mtError, [mbOk], 0);
  end;
  if CB_Profily.Items.Count = 0 then
    CB_Profily.Text := 'Výchozí';
  DragAcceptFiles(Handle, True);
end;

procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
//
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var
  Novy: Boolean;
  Cfg: TIniFile;
  Soubor, Cesta: string;
begin
  if ParSW.SystCesta then
  begin
    // ulozeni npristiho oteviravi ze systemove cesty i do souboru ve slozce programu, pokud existuje
    Soubor := CfgSoubor(False);
    if FileExists(Soubor) then
    begin
      Cfg := TIniFile.Create(Soubor);
      try
        Cfg.WriteBool(NAST_PROGR, 'AppData', ParSW.SystCesta);
      finally
        Cfg.Free;
      end;
    end;
  end;
  Soubor := CfgSoubor(ParSW.SystCesta);
  Cesta := ExtractFilePath(Soubor);
  // kontrola existence cesty a souboru
  Novy := not(FileExists(Soubor));
  if not(DirectoryExists(Cesta)) then
    ForceDirectories(Cesta);
  if DirectoryExists(Cesta) then
  begin
    Cfg := TIniFile.Create(Soubor);
    try
      // ulozeni parametru ovladani programu
      Cfg.WriteInteger(NAST_PROGR, 'Posun', ShiftStateToInt(ParSW.Posun));
      Cfg.WriteInteger(NAST_PROGR, 'Rotace', ShiftStateToInt(ParSW.Rotace));
      Cfg.WriteInteger(NAST_PROGR, 'VyberObj', ShiftStateToInt(ParSW.VyberMesh));
      Cfg.WriteInteger(NAST_PROGR, 'PosunObj', ShiftStateToInt(ParSW.PosunMesh));
      Cfg.WriteInteger(NAST_PROGR, 'Zoom', ParSW.SmerZoom);
      Cfg.WriteBool(NAST_PROGR, 'Orto', ParSW.OrtoProj);
      Cfg.WriteBool(NAST_PROGR, 'AppData', ParSW.SystCesta);
    finally
      Cfg.Free;
    end;
    if Novy then
      SB_UlozParClick(Self);
  end;
  // ukonceni simulace, uvolneni pameti apod.
  SimStop := True;
  Action := caFree;
  DragAcceptFiles(Handle, False);
end;

procedure TForm1.FormMouseWheel(Sender: TObject; Shift: TShiftState;
  WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
var
  I: Integer;
begin
  if PageControl.ActivePage = TS_CAD then
  begin
    Handled := PtInRect(GLSceneViewer.BoundsRect, GLSceneViewer.ScreenToClient(MousePos));
    if Handled then
      if GLCamera.CameraStyle = csPerspective then
      begin
        GLCamera.AdjustDistanceToTarget(Power(1.1, ParSW.SmerZoom * WheelDelta / 120));
        // digital zoom (linear)
//        GLCamera.SceneScale := GLCamera.SceneScale * Power(1.1, SmerZoom * WheelDelta / 120);
      end else begin
        // zoom orthogonal
        GLCamera.FocalLength := GLCamera.FocalLength * (Power(1.05, -ParSW.SmerZoom * WheelDelta / 120));
      end;
  end;
  if PageControl.ActivePage = TS_Nast3Dtisk then
  begin
    Handled := PtInRect(ScrollBox.BoundsRect, ScrollBox.ScreenToClient(MousePos));
//               and not PtInRect(ScrollBox.ClientRect, ScrollBox.ScreenToClient(MousePos));
    if Handled then
      for I := 1 to Mouse.WheelScrollLines do
      try
        if WheelDelta > 0 then
          ScrollBox.Perform(WM_VSCROLL, SB_LINEUP, 0)
        else
          ScrollBox.Perform(WM_VSCROLL, SB_LINEDOWN, 0);
      finally
        ScrollBox.Perform(WM_VSCROLL, SB_ENDSCROLL, 0);
      end;
  end;
end;

procedure TForm1.FormShow(Sender: TObject);
var
  I: Integer;
  Jmeno, Pripona: string;
begin
///  TS_Gkod.TabVisible := False;
  AutZmena := False;
  TB_ZamekClick(Self);
  E_VrstvaOd.AutoIncrement := True;
  E_VrstvaDo.AutoIncrement := True;  
  PageControl.ActivePage := TS_CAD;
  CB_OrtoProjekceClick(Self);
  TS_E.TabVisible  := False;
  TS_XY.TabVisible := False;
  // vlastni vykresleni
  Zoom(ModelCube, zm_ISO);
  // Otevreni souboru z prikazove radky pri spusteni programu
  if ParamCount > 0 then
  begin
    for I := 1 to ParamCount do
    begin
      Jmeno := ParamStr(I);
      Jmeno := Trim(Jmeno);
      if FileExists(Jmeno) then
      begin
        // Nacteni dat ze souboru dle parametru programu
        // rozhodnuti, jaky soubor se ma otevrit - *.STL nebo *.3DT
        OpenDialog.FileName := Jmeno;
        Pripona := AnsiLowerCase(ExtractFileExt(Jmeno));
        // rozhodnuti, jaky soubor se ma otevrit - *.STL nebo *.3DT
        if Pripona = '.stl' then
          NactiSoubor(Jmeno)
        else
          if Pripona = '.3dt' then
            NactiProjekt(Jmeno)
          else
            MessageDlg(Format('Typ souboru ''%s'' není podporován.', [Pripona]), mtError, [mbOk], 0);
      end else
        MessageDlg(Format('Soubor ''%s'' nenalezen!', [Jmeno]), mtError, [mbOk], 0);
    end;
  end;  // Program byl spousten s parametrem
end;

procedure TForm1.CB_ProfilyClick(Sender: TObject);
var
  I, Param: Integer;
  P: Double;
  Cesta, Radek: string;
  Cfg: TIniFile;
begin
  Cesta := CfgSoubor(ParSW.SystCesta);
  if FileExists(Cesta) then
  begin
    // otevreni souboru a nacteni parametru
    Cfg := TIniFile.Create(Cesta);
    try
      AutZmena := True;
      Cfg.WriteString(VYCH_PROFIL, 'Profil', CB_Profily.Text);
      E_Tloustka.Text := Cfg.ReadString(CB_Profily.Text, 'TlVrstvy', '2.5');
      E_PosuvXY.Text  := IntToStr(Cfg.ReadInteger(CB_Profily.Text, 'PosuvXY', 1500));
      E_PosuvZ.Text   := IntToStr(Cfg.ReadInteger(CB_Profily.Text, 'PosuvZ', 1500));
      E_RychlXY.Text  := IntToStr(Cfg.ReadInteger(CB_Profily.Text, 'RychlXY', 3000));
      E_ZdvihZ.Text   := Cfg.ReadString(CB_Profily.Text, 'ZdvihZ', '1.0');
      E_MinCas.Text   := IntToStr(Cfg.ReadInteger(CB_Profily.Text, 'MinCas', 10));
      E_PosunZ.Text   := FloatToStr(Cfg.ReadInteger(CB_Profily.Text, 'PosunutiZ', 0), FS);

      CB_Extruder.Checked := Cfg.ReadBool(CB_Profily.Text, 'OsaE', True);
      if CB_Extruder.Checked then
      begin
        E_Dmat.Text     := Cfg.ReadString(CB_Profily.Text, 'Dmat', '4.0');
        E_Dtrysky.Text  := Cfg.ReadString(CB_Profily.Text, 'Dtrysky', '2.5');
        E_TlSteny.Text  := Cfg.ReadString(CB_Profily.Text, 'Tlsteny', '3.0');
        E_NasMat.Text   := Cfg.ReadString(CB_Profily.Text, 'NasMat', '1.0');
        E_Ezap.Text     := '';
        E_Evyp.Text     := '';
      end else begin
        E_Dmat.Text     := '0.0';
        E_Dtrysky.Text  := '0.0';
        E_TlSteny.Text  := '0.0';
        E_NasMat.Text   := '0.0';
        E_Ezap.Text     := Cfg.ReadString(CB_Profily.Text, 'Ezap', '');
        E_Evyp.Text     := Cfg.ReadString(CB_Profily.Text, 'Evyp', '');
        E_EzNC.Text     := Cfg.ReadString(CB_Profily.Text, 'EzNC', '');
        E_Evel.Text     := IntToStr(Cfg.ReadInteger(CB_Profily.Text, 'Evel', 0));
        CB_EzNC.Checked := Cfg.ReadBool(CB_Profily.Text, 'PouzitE', False);
      end;

      E_TolSTL.Text   := Cfg.ReadString(CB_Profily.Text, 'TolSTL', '0.04');
      E_TolNC.Text    := Cfg.ReadString(CB_Profily.Text, 'TolNC', '0.0');

      CB_Pomalu1.Checked := Cfg.ReadBool(CB_Profily.Text, 'Pomalu1', True);
      CB_Spirala.Checked := Cfg.ReadBool(CB_Profily.Text, 'Spirala', True);

      CB_ProklObl.Checked := Cfg.ReadBool(CB_Profily.Text, 'ProklObl', False);
      RB_ParamIJ.Checked  := Cfg.ReadBool(CB_Profily.Text, 'ParamIJ', True);
      RB_ParamR.Checked   := not(RB_ParamIJ.Checked);
      P := StrToFloatDef(Cfg.ReadString(CB_Profily.Text, 'PomerUsec', '1.1'), 1.1, FS);
      if P <= 1 then P := 1.001;
      E_PomerUsec.Text    := FloatToStr(P, FS);
      E_MaxChybKr.Text    := Cfg.ReadString(CB_Profily.Text, 'MaxChybKr', '0.1');
      E_MaxRadius.Text    := Cfg.ReadString(CB_Profily.Text, 'MaxRadius', '999.9');

      CB_NevypEvXY.Checked := Cfg.ReadBool(CB_Profily.Text, 'NevypEvXY', False);
      E_KratkyXY.Text      := Cfg.ReadString(CB_Profily.Text, 'KratkyXY', '1.0');
      CB_NevypEvZ.Checked  := Cfg.ReadBool(CB_Profily.Text, 'NevypEvZ', False);

      Radek := Cfg.ReadString(CB_Profily.Text, 'StartNC', '');
      M_StartNC.Lines.Clear;
      repeat
        I := Pos('\n', Radek);
        if I > 0 then
        begin
          M_StartNC.Lines.Add(Copy(Radek, 1, I - 1));
          Delete(Radek, 1, I + 1);
        end else begin
          M_StartNC.Lines.Add(Radek);
          Radek := '';
        end;
        Radek := Trim(Radek);
      until Radek = '';
      Radek := Cfg.ReadString(CB_Profily.Text, 'KonecNC', '');
      M_KonecNC.Lines.Clear;
      repeat
        I := Pos('\n', Radek);
        if I > 0 then
        begin
          M_KonecNC.Lines.Add(Copy(Radek, 1, I - 1));
          Delete(Radek, 1, I + 1);
        end else begin
          M_KonecNC.Lines.Add(Radek);
          Radek := '';
        end;
        Radek := Trim(Radek);
      until Radek = '';

      E_MinX.Text  := Cfg.ReadString(CB_Profily.Text, 'MinX', '0.0');
      E_MinY.Text  := Cfg.ReadString(CB_Profily.Text, 'MinY', '0.0');
      E_MaxX.Text  := Cfg.ReadString(CB_Profily.Text, 'MaxX', '0.0');
      E_MaxY.Text  := Cfg.ReadString(CB_Profily.Text, 'MaxY', '0.0');
      E_MaxZ.Text  := Cfg.ReadString(CB_Profily.Text, 'MaxZ', '0.0');
      E_Rastr.Text := Cfg.ReadString(CB_Profily.Text, 'Rastr', '0.0');

      CB_RadkyNC.Checked  := Cfg.ReadBool(CB_Profily.Text, 'RadkyNC', True);
      E_KrokNC.Text       := IntToStr(Cfg.ReadInteger(CB_Profily.Text, 'KrokNC', 2));
      E_PriponaNC.Text    := Cfg.ReadString(CB_Profily.Text, 'Pripona', 'nc');
      Param := Cfg.ReadInteger(CB_Profily.Text, 'Koment', 4);
      RB_Apostrof.Checked := Param = 1;
      RB_Lomeno.Checked   := Param = 2;
      RB_Zavorky.Checked  := Param = 3;
      RB_Strednik.Checked := Param = 4;

      RB_Opt_prejezdy.Checked := True;
      Param := Cfg.ReadInteger(CB_Profily.Text, 'Optimal', TiskPar.Optimal);
      RB_Opt_prejezdy.Checked           := Param = 1;
      RB_Opt_uzavrene1otrvrene2.Checked := Param = 2;

      CB_Mereni.Checked   := Cfg.ReadBool(CB_Profily.Text, 'Mereni', False);
      E_MerVyskaZ.Text    := Cfg.ReadString(CB_Profily.Text, 'MerVyskaZ', '100.0');
      E_MerCidloX.Text    := Cfg.ReadString(CB_Profily.Text, 'MerCidloX', '100.0');
      E_MerCidloY.Text    := Cfg.ReadString(CB_Profily.Text, 'MerCidloY', '100.0');
      E_MerNvrst.Text     := IntToStr(Cfg.ReadInteger(CB_Profily.Text, 'MerNvrst', 10));
      E_MerFunkce.Text    := Cfg.ReadString(CB_Profily.Text, 'MerFunkce', 'M50');
      E_MerVypoc.Text     := Cfg.ReadString(CB_Profily.Text, 'MerVypoc', 'G172');

      NactiTiskParam(TiskPar, True);
      NastavEditTiskPar(False);
      InfoPanel.Caption := '  Načteno nastavení pro 3D tiskárnu: ' + CB_Profily.Text;
      Zoom(ModelCube, zm_ISO);
      InfoPanel.Refresh;
    finally
      AutZmena := False;
      Cfg.Free;
    end;
  end;
end;

procedure TForm1.E_TiskParChange(Sender: TObject);
begin
  if AutZmena then
    Exit;
  NastavEditTiskPar(False);
end;

procedure TForm1.E_PomerUsecExit(Sender: TObject);
begin
  if StrToFloatDef(E_PomerUsec.Text, 1, FS) <= 1 then
    E_PomerUsec.Text := '1.001';
  NastavEditTiskPar(False);
end;

procedure TForm1.TB_OtevriSTLClick(Sender: TObject);
begin
///  Form2.Visible := False;

  // otevreni souboru projektu
  OpenDialog.DefaultExt := 'stl';
  OpenDialog.Filter := 'Soubory STL (*.stl)|*.stl|Všechny soubory (*.*)|*.*';
  OpenDialog.FilterIndex := 1;
  OpenDialog.Title  := 'Otevření souboru sítě STL';
  OpenDialog.FileName := '';
  if not OpenDialog.Execute then Exit;
  if not FileExists(OpenDialog.Filename) then
  begin
    MessageDlg('Soubor nenalezen!', mtError, [mbOk], 0);
    Exit;
  end;
  // Načtení STL souboru
  NactiSoubor(OpenDialog.Filename);
  PageControl.ActivePage := TS_CAD;
end;

procedure TForm1.TB_InfoClick(Sender: TObject);
begin
  Form3.ShowModal;
end;

procedure TForm1.SB_UlozParClick(Sender: TObject);
var
  Prepsat: Boolean;
  Soubor, Cesta: string;
  Cfg: TIniFile;
begin
  if Trim(CB_Profily.Text) = '' then
  begin
    MessageDlg('Vyplňte název profilu.', mtWarning, [mbOK], 0);
    Exit;
  end;
  Soubor := CfgSoubor(ParSW.SystCesta);
  Cesta := ExtractFilePath(Soubor);
  // kontrola existence cesty a souboru
  if not(DirectoryExists(Cesta)) then
    ForceDirectories(Cesta);
  if DirectoryExists(Cesta) then
  begin
    Cfg := TIniFile.Create(Soubor);
    try
      Prepsat := True;
      if Cfg.SectionExists(CB_Profily.Text) then
      begin
        Prepsat := MessageDlg('Přepsat daný profil?', mtConfirmation, [mbYes, mbNo], 0) = mrYes;
      end else begin
        CB_Profily.Items.Add(CB_Profily.Text);
      end;
      if Prepsat then
      begin
        NactiTiskParam(TiskPar, True);

        Cfg.WriteString(VYCH_PROFIL, 'Profil', CB_Profily.Text);
        Cfg.WriteString(CB_Profily.Text, 'TlVrstvy', FloatToStr(TiskPar.TlVrstvy, FS));
        Cfg.WriteInteger(CB_Profily.Text, 'PosuvXY', TiskPar.PosuvXY);
        Cfg.WriteInteger(CB_Profily.Text, 'PosuvZ', TiskPar.PosuvZ);
        Cfg.WriteInteger(CB_Profily.Text, 'RychlXY', TiskPar.RychlXY);
        Cfg.WriteString(CB_Profily.Text, 'ZdvihZ', FloatToStr(TiskPar.ZdvihZ, FS));
        Cfg.WriteInteger(CB_Profily.Text, 'MinCas', TiskPar.MinCas);
        Cfg.WriteString(CB_Profily.Text, 'PosunutiZ', FloatToStr(TiskPar.PosunZ, FS));

        Cfg.WriteBool(CB_Profily.Text, 'OsaE', CB_Extruder.Checked);
        if CB_Extruder.Checked then
        begin
          Cfg.WriteString(CB_Profily.Text, 'Dmat', FloatToStr(TiskPar.Dmat, FS));
          Cfg.WriteString(CB_Profily.Text, 'Dtrysky', FloatToStr(TiskPar.Dtrysky, FS));
          Cfg.WriteString(CB_Profily.Text, 'TlSteny', FloatToStr(TiskPar.TlSteny, FS));
          Cfg.WriteString(CB_Profily.Text, 'NasMat', FloatToStr(TiskPar.NasMat, FS));
        end else begin
          Cfg.WriteString(CB_Profily.Text, 'Ezap', TiskPar.Ezap);
          Cfg.WriteString(CB_Profily.Text, 'Evyp', TiskPar.Evyp);
          Cfg.WriteString(CB_Profily.Text, 'EzNC', TiskPar.EzNC);
          Cfg.WriteInteger(CB_Profily.Text, 'Evel', TiskPar.Evel);
          Cfg.WriteBool(CB_Profily.Text, 'PouzitE', TiskPar.PouzitE);
        end;

        Cfg.WriteString(CB_Profily.Text, 'TolSTL', FloatToStr(TiskPar.TolSTL, FS));
        Cfg.WriteString(CB_Profily.Text, 'TolNC', FloatToStr(TiskPar.TolNC, FS));

        Cfg.WriteString(CB_Profily.Text, 'StartNC', TiskPar.StartNC);
        Cfg.WriteString(CB_Profily.Text, 'KonecNC', TiskPar.KonecNC);

        Cfg.WriteBool(CB_Profily.Text, 'Pomalu1', TiskPar.Pomalu1);
        Cfg.WriteBool(CB_Profily.Text, 'Spirala', TiskPar.Spirala);

        Cfg.WriteBool(CB_Profily.Text, 'ProklObl', TiskPar.ProklObl);
        Cfg.WriteBool(CB_Profily.Text, 'ParamIJ', TiskPar.ParamIJ);
        Cfg.WriteString(CB_Profily.Text, 'PomerUsec', FloatToStr(TiskPar.PomerUsec, FS));
        Cfg.WriteString(CB_Profily.Text, 'MaxChybKr', FloatToStr(TiskPar.MaxChybKr, FS));
        Cfg.WriteString(CB_Profily.Text, 'MaxRadius', FloatToStr(TiskPar.MaxRadius, FS));

        Cfg.WriteBool(CB_Profily.Text, 'NevypEvXY', TiskPar.NevypEvXY);
        Cfg.WriteString(CB_Profily.Text, 'KratkyXY', FloatToStr(TiskPar.KratkyXY, FS));
        Cfg.WriteBool(CB_Profily.Text, 'NevypEvZ', TiskPar.NevypEvZ);

        Cfg.WriteString(CB_Profily.Text, 'MinX', FloatToStr(TiskPar.MinX, FS));
        Cfg.WriteString(CB_Profily.Text, 'MinY', FloatToStr(TiskPar.MinY, FS));
        Cfg.WriteString(CB_Profily.Text, 'MaxX', FloatToStr(TiskPar.MaxX, FS));
        Cfg.WriteString(CB_Profily.Text, 'MaxY', FloatToStr(TiskPar.MaxY, FS));
        Cfg.WriteString(CB_Profily.Text, 'MaxZ', FloatToStr(TiskPar.MaxZ, FS));
        Cfg.WriteString(CB_Profily.Text, 'Rastr', FloatToStr(TiskPar.Rastr, FS));

        Cfg.WriteBool(CB_Profily.Text, 'RadkyNC', TiskPar.RadkyNC);
        Cfg.WriteInteger(CB_Profily.Text, 'KrokNC', TiskPar.KrokNC);
        Cfg.WriteString(CB_Profily.Text, 'Pripona', TiskPar.Pripona);
        Cfg.WriteInteger(CB_Profily.Text, 'Koment', TiskPar.Koment);

        Cfg.WriteInteger(CB_Profily.Text, 'Optimal', TiskPar.Optimal);

        Cfg.WriteBool(CB_Profily.Text, 'Mereni', TiskPar.Mereni);
        Cfg.WriteString(CB_Profily.Text, 'MerVyskaZ', FloatToStr(TiskPar.MerVyskaZ, FS));
        Cfg.WriteString(CB_Profily.Text, 'MerCidloX', FloatToStr(TiskPar.MerCidloX, FS));
        Cfg.WriteString(CB_Profily.Text, 'MerCidloY', FloatToStr(TiskPar.MerCidloY, FS));
        Cfg.WriteInteger(CB_Profily.Text, 'MerNvrst', TiskPar.MerNvrst);
        Cfg.WriteString(CB_Profily.Text, 'MerFunkce', TiskPar.MerFunkce);
        Cfg.WriteString(CB_Profily.Text, 'MerVypoc', TiskPar.MerVypoc);

        NastavEditTiskPar(False);
      end;
    finally
      Cfg.Free;
    end;
  end else
    MessageDlg(Format('Cestu profilu %s se nepodařilo vytvořit.', [Soubor]), mtWarning, [mbOK], 0);
end;

procedure TForm1.TB_NCkod1Click(Sender: TObject);
var
  Extruze: Boolean;
  I, J, K, M, V, FF, PocDat, BlokNC, PocNul, Smer: Integer;
  X, Y, Z, XX, YY, ZZ, Xs, Ys, Xs2, Ys2, Px, Py, Kx, Ky, Ch, L, P, II, JJ, RR, Uhel, EE, SumaE, Vx, Vy: Double;
  S, C: Extended;
  DelkaXY, DelkaZ, Doba, DelkaVrstvy: Double;
  Radek, Koment1, Koment2: string;
  Prog: TStringList;
  MaticeBodu: TMatice;
 function Zaokrouhli(const Cislo: Double): Double;
 const
   PRESNOST = 1000;
 begin
   Result := Round(Cislo * PRESNOST) / PRESNOST;
 end;
 procedure Pohyb(const X, Y, Z: Double; const G: Integer; Delka: Double = 0);
 var
  Pohyby: Byte;
  F: Integer;
 begin
  // vypnuti extruze pri prvnim pohybu rychloposuvem po pracovnim posuvu s extruzi
  if (G = 0) and Extruze then
  begin
   Extruze := False;
   if not(TiskPar.OsaE) then
   begin
    if TiskPar.RadkyNC then
    begin
     Inc(BlokNC, TiskPar.KrokNC);
     Prog.Add(Format('N%.*d %s', [PocNul, BlokNC, TiskPar.Evyp]));
    end else
     Prog.Add(TiskPar.Evyp);
   end;
  end;
  // vypocet delky ujete drahy a doby pracovniho posuvu
  if G < 2 then
    Delka := Hypot((X - XX), (Y - YY));
  // delka drahy
  DelkaXY := DelkaXY + Delka;
  if (Delka > NULA_REAL) and (Abs(Z - ZZ) > NULA_REAL) then
  begin
    // pohyb v X, Y a Z - spirala
    Delka := Hypot(Delka, (Z - ZZ));
  end else
   if Abs(Z - ZZ) > NULA_CNC then
   begin
    // pohyb jen v ose Z
    DelkaZ := DelkaZ + Abs(Z - ZZ);
    Doba   := Doba + (Abs(Z - ZZ) / TiskPar.PosuvZ * 60);
   end;
  Radek := '';
  Pohyby := 0;
  Vx := X - XX;
  Vy := Y - YY;
  if Abs(X - XX) > NULA_CNC then
  begin
   // byl pohyb v souradnici X
   Radek := Radek + Format(' X%1.3f', [X], FS);
   Pohyby := 1;
  end;
  XX := X;
  if Abs(Y - YY) > NULA_CNC then
  begin
   // byl pohyb v souradnici Y
   Radek := Radek + Format(' Y%1.3f', [Y], FS);
   Pohyby := Pohyby or $02;
  end;
  YY := Y;
  if G > 1 then
  begin
   // kruhova interpolace
   if TiskPar.ParamIJ then
    Radek := Radek + Format(' I%1.3f J%1.3f', [II, JJ], FS)
   else
    Radek := Radek + Format(' R%1.3f', [RR], FS);
  end else begin
   if Abs(Z - ZZ) > NULA_CNC then
   begin
    // byl pohyb v souradnici Z
    Radek := Radek + Format(' Z%1.3f', [Z + TiskPar.PosunZ], FS);
    Pohyby := Pohyby or $04;
   end;
   ZZ := Z;
  end;
  if false{OsaC} and ((G = 0) or (G = 1)) then
  begin
   // vystup rotacni osy - uhel natoceni tiskove hlavy
   if Abs(Vx) < NULA_REAL then
   begin
    if Vy > 0 then Uhel := PI / 2
              else Uhel := 3 * PI / 2;
   end else begin
    Uhel := ArcTan(Abs(Vy / Vx));
    if (Vy >= 0) and (Vx < 0) then
     Uhel := PI - Uhel;
    if (Vy < 0) and (Vx < 0) then
     Uhel := PI + Uhel;
    if (Vy < 0) and (Vx >= 0) then
     Uhel := 2 * PI - Uhel;
   end;
   while Uhel > 2 * PI do Uhel := Uhel - 2 * PI;
   while Uhel < 0 do Uhel := Uhel + 2 * PI;
   Radek := Radek + Format(' C%1.3f', [Uhel*180/PI], FS);
  end;
  // stanoveni rychlosti posuvu
  // vypocet casu na vytisteni vrstvy dle delky polygonu
  if (TiskPar.MinCas > 0) and (TiskPar.MinCas > (DelkaVrstvy * 60 / TiskPar.PosuvXY)) then
   F := Round(DelkaVrstvy * 60 / TiskPar.MinCas)
  else
   F := TiskPar.PosuvXY;
  if (V = 2) and TiskPar.Pomalu1 then
   F := F div 2;
  if G = 0 then
   F := TiskPar.RychlXY;
  if (Pohyby or $04) = $04 then
   F := TiskPar.PosuvZ;
  if (G > 0) and (Delka > NULA_REAL) then
  begin
   // byl pohyb v souradnici E
   if not(Extruze) then
   begin
    Extruze := True;
    if TiskPar.OsaE then
    begin
     // vynulovani mnozstvi extrudovaneho materialu
     EE := 0;
     if TiskPar.RadkyNC then
     begin
      Inc(BlokNC, TiskPar.KrokNC);
      Prog.Add(Format('N%.*d G92 E0', [PocNul, BlokNC]));
     end else
      Prog.Add('G92 E0');
    end else begin
     if TiskPar.RadkyNC then
     begin
      Inc(BlokNC, TiskPar.KrokNC);
      Prog.Add(Format('N%.*d %s', [PocNul, BlokNC, TiskPar.Ezap]));
      if TiskPar.PouzitE then
      begin
       Inc(BlokNC, TiskPar.KrokNC);
       Prog.Add(Format('N%.*d E_VEL=%d', [PocNul, BlokNC, Round(TiskPar.Evel * F / TiskPar.PosuvXY)]));
      end;
     end else begin
      Prog.Add(TiskPar.Ezap);
      if TiskPar.PouzitE then
       Prog.Add(Format('E_VEL=%d', [Round(TiskPar.Evel * F / TiskPar.PosuvXY)]));
     end;
    end;
   end;
   if TiskPar.OsaE then
   begin
    EE := EE + Delka;
    SumaE := SumaE + Delka;
    Radek := Radek + Format(' E%1.3f', [EE * TiskPar.KoefMat], FS)
   end
  end;
  if Length(Radek) > 0 then
  begin
   Doba  := Doba + (Delka / F * 60);
   if (FF <> F) then
   begin
    // byla zmena posuvu
    FF := F;
    Radek := Radek + Format(' F%u', [F]);
   end;
   if TiskPar.RadkyNC then
   begin
    Inc(BlokNC, TiskPar.KrokNC);
    Prog.Add(Format('N%.*d G0%.1d%s   ( T:%.1f )', [PocNul, BlokNC, G, Radek, Doba], FS));
   end else
    Prog.Add(Format('G0%.1d%s   ( T:%.1f )', [G, Radek, Doba], FS));
  end;
 end; // procedure Pohyb
begin
  L_NCkod.Caption := 'G-kód pro řízení 3D tiskárny:';
  Prog := TStringList.Create;
  Prog.Clear;
  PocDat := Length(Lines);
  if (PocDat > 0) and (Lines[0].L <> nil) then
  begin
    Screen.Cursor := crHourglass;
    M_Gkod.Lines.Clear;
    try
      DelkaXY   := 0;
      DelkaZ    := 0;
      Doba      := 0;
      NactiTiskParam(TiskPar, True);
      NastavEditTiskPar(True);
      NastavSimulaci;
      // vychozi nastaveni
      EE := 0;
      FF := 0;
      SumaE := 0;
      Extruze := False;
      BlokNC := 0;
      PocNul := 5;
      case TiskPar.Koment of
        1: begin Koment1 := nc_KOM_1; Koment2 := ''; end;
        2: begin Koment1 := nc_KOM_2; Koment2 := ''; end;
        3: begin Koment1 := nc_KOM_3; Koment2 := nc_KOM_3a; end;
        4: begin Koment1 := nc_KOM_4; Koment2 := ''; end;
      end;
      // uvodni radky NC programu
      Radek := ExtractFileName(OpenDialog.FileName);
      Prog.Add(Format('%s Program %s%s', [Koment1, Radek, Koment2]));
      Prog.Add('');
      for I := 0 to M_StartNC.Lines.Count - 1 do
      begin
        Radek := Trim(M_StartNC.Lines[I]);
        if Length(Radek) > 0 then
        begin
          if TiskPar.RadkyNC and (Pos('#', Radek) = 1) then
          begin
            Inc(BlokNC, TiskPar.KrokNC);
            Delete(Radek, 1, 1);
            Radek := Trim(Radek);
            Prog.Add(Format('N%.*d %s', [PocNul, BlokNC, Radek]));
          end else
            Prog.Add(Radek);
        end;
      end;
      if not(TiskPar.OsaE) and TiskPar.PouzitE then
      begin
        if TiskPar.RadkyNC then
        begin
          Inc(BlokNC, TiskPar.KrokNC);
          Prog.Add(Format('N%.*d %s %s%s%s', [PocNul, BlokNC, TiskPar.EzNC, Koment1, 'M-funkce pro rizeni rychlosti E z G-kodu', Koment2]));
        end else
          Prog.Add(Format('%s %s%s%s', [TiskPar.EzNC, Koment1, 'M-funkce pro rizeni rychlosti E z G-kodu', Koment2]));
      end;
      // generovani jednotlivych vrstev - priprava
      V := 1;
      XX := Lines[0].L.Nodes[0].X;
      YY := Lines[0].L.Nodes[0].Y;
      ZZ := Lines[0].L.Nodes[0].Z - NULA_CNC * 10;
      // jen pro prvni pohyb v programu
      if TiskPar.RadkyNC then
      begin
        Inc(BlokNC, TiskPar.KrokNC);
        Prog.Add(Format('N%.*d G01 X%1.3f Y%1.3f F%u', [PocNul, BlokNC, XX, YY, TiskPar.RychlXY], FS));
      end else begin
        Prog.Add(Format('G01 X%1.3f Y%1.3f F%u', [XX, YY, TiskPar.RychlXY], FS));
      end;
      // vlastni generovani drah
      for I := 0 to PocDat - 1 do
      begin
        if Lines[I].Vrstva = V then
        begin
          // vypocet delky vrstvy (zapocitava se pouze pracovni posuv)
          DelkaVrstvy := 0;
          J := I;
          while (J < PocDat) and (Lines[J].Vrstva = V) do
          begin
            if Lines[J].G = 1 then
              DelkaVrstvy := DelkaVrstvy + Lines[J].Delka;
            Inc(J);
          end;
          Prog.Add(Format('%s vrstva %d, Z = %1.3f, delka vrstvy = %1.3f %s', [Koment1, V, Lines[I].Z, DelkaVrstvy, Koment2], FS));
          InfoPanel.Caption := Format('  Generuji dráhy hlavy pro vrstvu Z: %1.3f', [Lines[I].Z], FS);
          InfoPanel.Refresh;
          Inc(V);
        end;
        // prochazeni jednotlivymi body ve vrstve
        // detekovani kruhovych oblouku - zkouseni prolozeni kratkych usecek obloukem
        J := 0;
        while J < Lines[I].L.Nodes.Count do
        begin
          K := J + 1;
          if J = 0 then
          begin
            // vychozi bod musi byt zapsan do NC kodu v kazdem pripade
            X := Lines[I].L.Nodes[0].X;
            Y := Lines[I].L.Nodes[0].Y;
            Z := Lines[I].L.Nodes[0].Z;
            Pohyb(X, Y, Z, Lines[I].G);
          end;
          if TiskPar.ProklObl and (J < Lines[I].L.Nodes.Count - 2) and
             (Abs(Lines[I].L.Nodes[J].Z - Lines[I].L.Nodes[J+2].Z) < NULA_REAL) then
          begin
            // mohou byt oblouky a neni pohyb v ose Z => lze hledat prolozeni
            // vypocet skutecne delky prvni pouzivane usecky
            L := Hypot(Lines[I].L.Nodes[J].X - Lines[I].L.Nodes[J+1].X,
                       Lines[I].L.Nodes[J].Y - Lines[I].L.Nodes[J+1].Y);
            // Hledani priblize stejne dlouhych usecek navazujicich na prvni vybranou
            // (pomer delek je max. 1 : POMER_USECEK nebo POMER_USECEK : 1)
            if L > NULA_REAL then
              L := Hypot(Lines[I].L.Nodes[J+1].X - Lines[I].L.Nodes[J+2].X,
                         Lines[I].L.Nodes[J+1].Y - Lines[I].L.Nodes[J+2].Y) / L
            else L := 0;
            M := 0;
            while ((1 / TiskPar.PomerUsec) < L) and (L < TiskPar.PomerUsec) and (K < Lines[I].L.Nodes.Count - 2) do
            begin
              L := Hypot(Lines[I].L.Nodes[K].X - Lines[I].L.Nodes[K+1].X,
                         Lines[I].L.Nodes[K].Y - Lines[I].L.Nodes[K+1].Y);
              if L > NULA_REAL then
                L := Hypot(Lines[I].L.Nodes[K+1].X - Lines[I].L.Nodes[K+2].X,
                           Lines[I].L.Nodes[K+1].Y - Lines[I].L.Nodes[K+2].Y) / L
              else L := 0;
              Inc(K);
              Inc(M);
            end;
            if (M > 0) or (K >= Lines[I].L.Nodes.Count - 2) then Dec(K);
            // vypocet prolozeni - priprava matic pro vypocet prolozeni kruznice
            if not(((1 / TiskPar.PomerUsec) < L) and (L < TiskPar.PomerUsec)) then Dec(K);
            // prolozeni ma smysl, pokud mam alespon 3 usecky, K+2 - J >= 3
            if (K - J) > 0 then
            begin
              SetLength(MaticeBodu, K - J + 3, 2);
              for M := 0 to Length(MaticeBodu) - 1 do
              begin
                MaticeBodu[M, 0] := Lines[I].L.Nodes[M + J].X;
                MaticeBodu[M, 1] := Lines[I].L.Nodes[M + J].Y;
              end;
              KruzniceNejmCtv(MaticeBodu, X, Y, RR, Ch, M);
              if (MatChyba = mch_BezChyb) and (Ch < TiskPar.MaxChybKr) and (RR < TiskPar.MaxRadius) then
              begin
                // vypocer smeru kruhoveho oblouku
                SetLength(MaticeBodu, K - J + 4, 2);
                MaticeBodu[Length(MaticeBodu) - 1, 0] := Lines[I].L.Nodes[J].X;
                MaticeBodu[Length(MaticeBodu) - 1, 1] := Lines[I].L.Nodes[J].Y;
                L := VypocitejObsah(MaticeBodu);
                Finalize(MaticeBodu);
                if L < 0 then
                  Smer := 1    // G02
                else
                  Smer := -1;  // G03
                // vypocet souradnic stredu kruznice ne z prolozeni, ale z pocatecniho a koncoveho bodu polygonu
{
L = sqrt((Bx-Ax)^2 + (By-Ay)^2)
Fx = (Ax+Bx) / 2
Fy = (Ay+By) / 2

řešení 1:
Sx = Fx + sqrt(r^2 - (L/2)^2) * (Ay-By) / L
Sy = Fy + sqrt(r^2 - (L/2)^2) * (Bx-Ax) / L

řešení 2:
Sx = Fx - sqrt(r^2 - (L/2)^2) * (Ay-By) / L
Sy = Fy - sqrt(r^2 - (L/2)^2) * (Bx-Ax) / L
}
                // delka tetivy oblouku
                L  := Hypot(Lines[I].L.Nodes[K+2].X - Lines[I].L.Nodes[J].X, Lines[I].L.Nodes[K+2].Y - Lines[I].L.Nodes[J].Y);
                if L > 2 * RR then
                  RR := L / 2;
                // vypocer parametru I, J z dat polygonu (nikoli z prolozeni)
                Px := Zaokrouhli(Lines[I].L.Nodes[J].X);
                Py := Zaokrouhli(Lines[I].L.Nodes[J].Y);
                Kx := Zaokrouhli(Lines[I].L.Nodes[K+2].X);
                Ky := Zaokrouhli(Lines[I].L.Nodes[K+2].Y);
                // vlastni vypocet souradnic stredu
                if L > NULA_REAL then
                begin
                  P  := Sqrt(RR * RR - (L * L / 4));
                  Xs  := (Lines[I].L.Nodes[J].X + Lines[I].L.Nodes[K+2].X) / 2 + P * (Lines[I].L.Nodes[J].Y - Lines[I].L.Nodes[K+2].Y) / L;
                  Ys  := (Lines[I].L.Nodes[J].Y + Lines[I].L.Nodes[K+2].Y) / 2 +  P * (Lines[I].L.Nodes[K+2].X - Lines[I].L.Nodes[J].X) / L;
                  Xs2 := (Lines[I].L.Nodes[J].X + Lines[I].L.Nodes[K+2].X) / 2 - P * (Lines[I].L.Nodes[J].Y - Lines[I].L.Nodes[K+2].Y) / L;
                  Ys2 := (Lines[I].L.Nodes[J].Y + Lines[I].L.Nodes[K+2].Y) / 2 - P * (Lines[I].L.Nodes[K+2].X - Lines[I].L.Nodes[J].X) / L;
                  if Hypot(Xs - X, Ys - Y) < Hypot(Xs2 - X, Ys2 - Y) then
                  begin
                    Xs  := Zaokrouhli(Xs);
                    Ys  := Zaokrouhli(Ys);
                  end else begin
                    Xs  := Zaokrouhli(Xs2);
                    Ys  := Zaokrouhli(Ys2);
                  end;
                  // vypocet uhlu sevreneho kruhovym obloukem
                  Uhel := SmerovyUhel(Px - Xs, Py - Ys, Smer);
                  Uhel := SmerovyUhel(Kx - Xs, Ky - Ys, Smer) - Uhel;
                  while Uhel > 2 * PI do Uhel := Uhel - 2 * PI;
                  while Uhel < 0 do Uhel := Uhel + 2 * PI;
                end else begin
                  // polygony tvori uzavrenou celou kruznici, souradnice stredu a uhel nelze vypocitat predchozim vypoctem
                  Xs := Zaokrouhli(X);
                  Ys := Zaokrouhli(Y);
                  Uhel := 2 * PI;
                end;
                II := Xs - Px;
                JJ := Ys - Py;
                if (Uhel >= 0) and (Uhel <= 2 * PI) then
                begin
                  if Uhel > PI then
                  begin
                    // oblouk svira vetsi uhel nez 180 stupnu, musi byt rozdelen na vice oblouku
                    Uhel := - Uhel / 2 * Smer;
                    Math.SinCos(Uhel, S, C);
                    X := (Px - Xs) * C - (Py - Ys) * S + Xs;
                    Y := (Px - Xs) * S + (Py - Ys) * C + Ys;
                    Z := Lines[I].L.Nodes[K+1].Z;
                    L := Hypot(XX - X, YY - Y);
                    if L > 2 * RR then
                      RR := L / 2;
                    if Smer > 0 then
                      Pohyb(X, Y, Z, 2, RR * Uhel)
                    else
                      Pohyb(X, Y, Z, 3, RR * Uhel);
                    // vypocer parametru I, J pro druhou pulku oblouku
                    II := Xs - X;
                    JJ := Ys - Y;
                  end;
                  // nahrazeni danych usecek kruhovym obloukem v NC programu
                  Z := Lines[I].L.Nodes[K+1].Z;
                  if Smer > 0 then
                    Pohyb(Kx, Ky, Z, 2, RR * Uhel)
                  else
                    Pohyb(Kx, Ky, Z, 3, RR * Uhel);
                  Inc(K);
                end else
                  // vypocteny uhel sevreny obloukem je mimo mese => oblouk se nebude prokladat
                  K := J;
              end else begin
                // prolozeni kruznice nedopadlo spravne, zapisi se usecky
                MatChyba := mch_BezChyb;
                Finalize(MaticeBodu);
                K := J;
              end;
            end;
          end;  // stejna souradnice v Z
          if (K - J) < 2 then
          begin
            // k prolozeni obloukem nedoslo => tisk pouze pomoci usecek do NC programu
            if J < Lines[I].L.Nodes.Count - 1 then
            begin
              X := Lines[I].L.Nodes[J+1].X;
              Y := Lines[I].L.Nodes[J+1].Y;
              Z := Lines[I].L.Nodes[J+1].Z;
              Pohyb(X, Y, Z, Lines[I].G);
            end;
            K := J;
          end;
          J := K + 1;
        end;  // prochazeni vsemi body ve vrstve

        // priprava na navrat do vychoziho bodu
        if Extruze and not(TiskPar.OsaE) then
        begin
          // rozhodnuti, zde se bude vypinat extrudovani
          Extruze := ((I+1) < PocDat) and not(Lines[I+1].G = 0);
          if not(Extruze) then
          begin
            // pokud ano, dopln prislusny radek do NC programu
            if TiskPar.RadkyNC then
            begin
              Inc(BlokNC, TiskPar.KrokNC);
              Prog.Add(Format('N%.*d %s', [PocNul, BlokNC, TiskPar.Evyp]));
            end else
              Prog.Add(TiskPar.Evyp);
          end;
        end;
      end;
      // konec NC programu
      for I := 0 to M_KonecNC.Lines.Count - 1 do
      begin
        Radek := Trim(M_KonecNC.Lines[I]);
        if Length(Radek) > 0 then
        begin
          if TiskPar.RadkyNC and (Pos('#', Radek) = 1) then
          begin
            Inc(BlokNC, TiskPar.KrokNC);
            Delete(Radek, 1, 1);
            Radek := Trim(Radek);
            Prog.Add(Format('N%.*d %s', [PocNul, BlokNC, Radek]));
          end else
            Prog.Add(Radek);
        end;
      end;
      // parametry nastaveni jako komentar na konec programu
      Prog.Add('');
      Prog.Add(Format('%s parametry nastaveni%s', [Koment1, Koment2]));
      Prog.Add(Format('%s %s=%s%s', [Koment1, 'Profil', CB_Profily.Text, Koment2]));
      Prog.Add(Format('%s %s=%g%s', [Koment1, 'TlVrstvy', TiskPar.TlVrstvy, Koment2], FS));
      Prog.Add(Format('%s %s=%d%s', [Koment1, 'PosuvXY', TiskPar.PosuvXY, Koment2]));
      Prog.Add(Format('%s %s=%d%s', [Koment1, 'PosuvZ', TiskPar.PosuvZ, Koment2]));
      Prog.Add(Format('%s %s=%d%s', [Koment1, 'RychlXY', TiskPar.RychlXY, Koment2]));
      Prog.Add(Format('%s %s=%g%s', [Koment1, 'ZdvihZ', TiskPar.ZdvihZ, Koment2], FS));
      Prog.Add(Format('%s %s=%d%s', [Koment1, 'MinCas', TiskPar.MinCas, Koment2]));
      Prog.Add(Format('%s %s=%g%s', [Koment1, 'PosunutiZ', TiskPar.PosunZ, Koment2], FS));
      if TiskPar.OsaE then
      begin
        Prog.Add(Format('%s %s=%g%s', [Koment1, 'Dmat', TiskPar.Dmat, Koment2], FS));
        Prog.Add(Format('%s %s=%g%s', [Koment1, 'Dtrysky', TiskPar.Dtrysky, Koment2], FS));
        Prog.Add(Format('%s %s=%g%s', [Koment1, 'TlSteny', TiskPar.TlSteny, Koment2], FS));
        Prog.Add(Format('%s %s=%g%s', [Koment1, 'NasMat', TiskPar.NasMat, Koment2], FS));
        Prog.Add(Format('%s %s=%g%s', [Koment1, 'KoefMat', TiskPar.KoefMat, Koment2], FS));
      end else begin
        Prog.Add(Format('%s %s=%s%s', [Koment1, 'Ezap', TiskPar.Ezap, Koment2]));
        Prog.Add(Format('%s %s=%s%s', [Koment1, 'Evyp', TiskPar.Evyp, Koment2]));
        Prog.Add(Format('%s %s=%s%s', [Koment1, 'EzNC', TiskPar.EzNC, Koment2]));
        Prog.Add(Format('%s %s=%d%s', [Koment1, 'Evel', TiskPar.Evel, Koment2]));
        I := 0;
        if TiskPar.PouzitE then I := 1;
        Prog.Add(Format('%s %s=%d%s', [Koment1, 'PouzitE', I, Koment2]));
      end;
      Prog.Add(Format('%s %s=%g%s', [Koment1, 'TolSTL', TiskPar.TolSTL, Koment2], FS));
      Prog.Add(Format('%s %s=%g%s', [Koment1, 'TolNC', TiskPar.TolNC, Koment2], FS));
      Prog.Add(Format('%s %s=%s%s', [Koment1, 'StartNC', TiskPar.StartNC, Koment2]));
      Prog.Add(Format('%s %s=%s%s', [Koment1, 'KonecNC', TiskPar.KonecNC, Koment2]));
      I := 0;
      if TiskPar.Pomalu1 then I := 1;
      Prog.Add(Format('%s %s=%d%s', [Koment1, 'Pomalu1', I, Koment2]));
      I := 0;
      if TiskPar.Spirala then I := 1;
      Prog.Add(Format('%s %s=%d%s', [Koment1, 'Spirala', I, Koment2]));

      I := 0;
      if TiskPar.ProklObl then I := 1;
      Prog.Add(Format('%s %s=%d%s', [Koment1, 'ProklObl', I, Koment2]));
      I := 0;
      if TiskPar.ParamIJ then I := 1;
      Prog.Add(Format('%s %s=%d%s', [Koment1, 'ParamIJ', I, Koment2]));
      Prog.Add(Format('%s %s=%g%s', [Koment1, 'PomerUsec', TiskPar.PomerUsec, Koment2], FS));
      Prog.Add(Format('%s %s=%g%s', [Koment1, 'MaxChybKr', TiskPar.MaxChybKr, Koment2], FS));
      Prog.Add(Format('%s %s=%g%s', [Koment1, 'MaxRadius', TiskPar.MaxRadius, Koment2], FS));

      Prog.Add(Format('%s Tiskova plocha: [%g, %g] x [%g, %g]%s', [Koment1, TiskPar.MinX, TiskPar.MinY, TiskPar.MaxX, TiskPar.MaxY, Koment2], FS));
      // statisticka data na zacatek programu
      Prog.Insert(1, Format('%s Delka drahy pojezdu hlavy:  %9.0f [mm]%s', [Koment1, DelkaXY + DelkaZ, Koment2], FS));
      I := Round(Doba / 60) div 60;
      J := Round(Doba / 60) mod 60;
      Prog.Insert(2, Format('%s Predpokladana doba tisku:    %5.2d:%.2d [hod:min]%s', [Koment1, I, J, Koment2]));
      if TiskPar.OsaE then
      begin
        SumaE := SumaE * TiskPar.KoefMat;
        Prog.Insert(3, Format('%s Delka filamentu:            %9.3f [m]%s', [Koment1, SumaE / 1000, Koment2], FS));
        SumaE := SumaE * PI * TiskPar.Dmat * TiskPar.Dmat / 4000;
        Prog.Insert(4, Format('%s Spotreba materialu:         %9.3f [cm3]%s', [Koment1, SumaE, Koment2], FS));
      end;
      M_Gkod.Lines.Assign(Prog);
    finally
      Prog.Free;
      Screen.Cursor := crDefault;
      InfoPanel.Caption := '';
      TB_Sim.Enabled     := M_Gkod.Lines.Count > 0;
      TB_SimBlok.Enabled := M_Gkod.Lines.Count > 0;
      if M_Gkod.Lines.Count > 0 then
      begin
        NastavSimulaci;
      end;
    end;
  end;
end;

procedure TForm1.TB_SimClick(Sender: TObject);
var
  I: Integer;
  Z: Double;
  Radek: string;
begin
  if PrgMonIndex = -1 then
  begin
    // nastav simulaci
    NastavSimulaci;
    for I := 0 to Length(Lines) - 1 do
      Lines[I].L.Visible := False;
  end;
  SimStop := False;
  TB_Sim.Enabled     := False;
  TB_SimStop.Enabled := True;
  TB_SimZpet.Enabled := True;
  // vlastni simulace
  while (PrgMonIndex < M_Gkod.Lines.Count) and not(SimStop) do
  begin
    Inc(PrgMonIndex);
    MIndex := PrgMonIndex;
    if CB_CelaVrstva.Checked then
    begin
      // odsimulovani cele vrstvy najednou
      Radek := OdstranKoment(M_GKod.Lines[PrgMonIndex]);
      ProvedRadek(Radek);
      Z := SimStroje.Z;
      while (PrgMonIndex < M_Gkod.Lines.Count) and (Abs(SimStroje.Z - Z) < NULA_REAL) do
      begin
        Inc(PrgMonIndex);
        Radek := OdstranKoment(M_GKod.Lines[PrgMonIndex]);
        ProvedRadek(Radek);
      end;
      MIndex := PrgMonIndex;
      if (PrgMonIndex - 2) < 0 then
        M_GKod.TopLine := 0
      else
        M_GKod.TopLine := PrgMonIndex - 2;
      M_GKod.Refresh;
    end else begin
      // simulace po jednotlivych radcich
      Radek := OdstranKoment(M_GKod.Lines[PrgMonIndex]);
      ProvedRadek(Radek);
      if (PrgMonIndex - 2) < 0 then
        M_GKod.TopLine := 0
      else
        M_GKod.TopLine := PrgMonIndex - 2;
      M_GKod.Refresh;
    end;
    if Radek <> '' then
    begin
      if TB_Rychlost.Position < TB_Rychlost.Max then
      begin
        Cekej((TB_Rychlost.Max - TB_Rychlost.Position) * 15);
      end else
        Application.ProcessMessages;
    end else
      Application.ProcessMessages;
  end;
  if PrgMonIndex >= M_Gkod.Lines.Count then
    ResetSimulace;
end;

procedure TForm1.TB_SimBlokClick(Sender: TObject);
var
  I: Integer;
  Z: Double;
  Radek: string;
begin
  SimStop := True;
  if PrgMonIndex = -1 then
  begin
    NastavSimulaci;
    for I := 0 to Length(Lines) - 1 do
      Lines[I].L.Visible := False;
  end;
  if (PrgMonIndex < M_Gkod.Lines.Count) then
  begin
    TB_Sim.Enabled     := M_Gkod.Lines.Count > 0;
    TB_SimBlok.Enabled := M_Gkod.Lines.Count > 0;
    TB_SimStop.Enabled := False;
    TB_SimZpet.Enabled := True;
    if CB_CelaVrstva.Checked then
    begin
      // odsimulovani cele vrstvy najednou
      Radek := OdstranKoment(M_GKod.Lines[PrgMonIndex]);
      ProvedRadek(Radek);
      Z := SimStroje.Z;
      while (PrgMonIndex < M_Gkod.Lines.Count) and (Abs(SimStroje.Z - Z) < NULA_REAL) do
      begin
        Inc(PrgMonIndex);
        Radek := OdstranKoment(M_GKod.Lines[PrgMonIndex]);
        ProvedRadek(Radek);
      end;
      MIndex := PrgMonIndex;
      if (PrgMonIndex - 2) < 0 then
        M_GKod.TopLine := 0
      else
        M_GKod.TopLine := PrgMonIndex - 2;
      M_GKod.Refresh;
    end else begin
      // simulace po jednotlivych radcich
      Inc(PrgMonIndex);
      MIndex := PrgMonIndex;
      Radek := OdstranKoment(M_GKod.Lines[PrgMonIndex]);
      ProvedRadek(Radek);
      if (PrgMonIndex - 2) < 0 then
        M_GKod.TopLine := 0
      else
        M_GKod.TopLine := PrgMonIndex - 2;
      M_GKod.Refresh;
    end;
  end else
    ResetSimulace;
end;

procedure TForm1.TB_SimStopClick(Sender: TObject);
begin
  SimStop := True;
  TB_SimStop.Enabled := False;
  TB_Sim.Enabled     := M_Gkod.Lines.Count > 0;
  TB_SimBlok.Enabled := M_Gkod.Lines.Count > 0;
end;

procedure TForm1.TB_SimZpetClick(Sender: TObject);
begin
  ResetSimulace;
  NastavSimulaci;
end;

procedure TForm1.M_GkodSpecialLineColors(Sender: TObject; Line: Integer;
  var Special: Boolean; var FG, BG: TColor);
begin
  if Line = (MIndex + 1) then
  begin
    Special := True;
    FG := clWhite;
    BG := clBlue;
  end;
end;

procedure TForm1.P_StatClick(Sender: TObject);
var
  M, PocVrstev, PocBodu, PocPolygonu: Integer;
  Delka: Double;
  MinM, MaxM: TVector4f;
  Sit: PMesh;
begin
  // vypocet minimalnich a maximalnich sourasdnic v datech siti
  for M := 0 to 2 do
  begin
    MinM.V[M] := Infinity;
    MaxM.V[M] := NegInfinity;
  end;
  for M := 0 to MeshList.Count - 1 do
  begin
    Sit := MeshList.Items[M];
    UrciMinMax(Sit, MinM, MaxM, False, False, False);
  end;
  PocBodu := 0;
  Delka := 0;
  PocPolygonu := 0;
  PocVrstev := 0;
  if (Length(Lines) > 0) and (Lines[0].L <> nil) then
  begin
    for M := 0 to Length(Lines) - 1 do
    begin
      Delka := Delka + Lines[M].Delka;
      if Lines[M].G = 1 then
      begin
        Inc(PocPolygonu);
        PocBodu := PocBodu + Lines[M].L.Nodes.Count;
      end;
      if Lines[M].Vrstva > PocVrstev then
        PocVrstev := Lines[M].Vrstva;
    end;
  end;
  Form2.L_PocVrstev.Caption   := IntToStr(PocVrstev);
  Form2.L_PocPolygonu.Caption := IntToStr(PocPolygonu);
  Form2.L_PocBodu.Caption     := IntToStr(PocBodu);
  Form2.L_Delka.Caption       := Format('%1.3f m', [Delka / 1000], FS);
  Form2.ShowModal;
end;

procedure TForm1.P_VelSJMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
const
  SC_SIZETOP = 3;
var
  MsgCode: Integer;
begin
  MsgCode := SC_SIZE;
  Inc(MsgCode, SC_SIZETOP);
  ReleaseCapture;
  SendMessage(P_NC_kod.Handle, WM_SYSCOMMAND, MsgCode, 0);
end;

procedure TForm1.CB_ExtruderClick(Sender: TObject);
begin
  if CB_Extruder.Checked then
  begin
    // osa E je rizena osa 3D tiskarny - povoleni tiskovych parametru extruderu
    PC_Extruder.ActivePage := TS_E;
  end else begin
    // extrudovani je zavisle na delce pohybu os X, Y, (prip. i Z),
    // extrudovani zapinaji a vypinaji spec. funkce
    PC_Extruder.ActivePage := TS_XY;
  end;
  NastavEditTiskPar(False);
end;

procedure TForm1.CB_OrtoProjekceClick(Sender: TObject);
begin
  if CB_OrtoProjekce.Checked then
  begin
    ParSW.OrtoProj := True;
    GLCamera.CameraStyle := csOrthogonal;
    GLCamera.FocalLength := 0.03;
    Zoom(ModelCube, zm_ISO);
//    GLCamera.DepthOfView := 50;
//    GLCamera.DistanceToTarget := 50;
  end else begin
    ParSW.OrtoProj := False;
    GLCamera.CameraStyle := csPerspective;
    GLCamera.FocalLength := 1000;
    Zoom(ModelCube, zm_ISO);
//    GLCamera.AdjustDistanceToTarget := 50;
  end;
end;

procedure TForm1.TB_ZoomVseClick(Sender: TObject);
begin
  Zoom(ModelCube, zm_ALL);
end;

procedure TForm1.TB_NC_AtenaClick(Sender: TObject);
var
  Extruze: Boolean;
  I, J, V, PocDat: Integer;
  X, Y, Z, XX, YY, ZZ: Double;
  DelkaXY, DelkaZ, Doba, DelkaVrstvy: Double;
  Prog: TStringList;
 procedure Pohyb(const X, Y, Z: Double; const G: Integer);
 var
  Pohyby: Byte;
  F: Integer;
  Delka: Double;
 begin
  // vypocet delky ujete drahy a doby pracovniho posuvu
  Delka := Hypot((X - XX), (Y - YY));
  // delka drahy
  DelkaXY := DelkaXY + Delka;
  if (Delka > NULA_REAL) and (Abs(Z - ZZ) > NULA_REAL) then
  begin
    // pohyb v X, Y a Z - spirala
    Delka := Hypot(Delka, (Z - ZZ));
  end else
    if Abs(Z - ZZ) > NULA_REAL then
    begin
      // pohyb jen v ose Z
      DelkaZ := DelkaZ + Abs(Z - ZZ);
      Doba   := Doba + (Abs(Z - ZZ) / TiskPar.PosuvZ * 60);
    end;
  Pohyby := 0;
  if Abs(X - XX) > NULA_REAL then
  begin
   // byl pohyb v souradnici X
   XX := X;
   Pohyby := 1;
  end;
  if Abs(Y - YY) > NULA_REAL then
  begin
   // byl pohyb v souradnici Y
   YY := Y;
   Pohyby := Pohyby or $02;
  end;
  if Abs(Z - ZZ) > NULA_REAL then
  begin
   // byl pohyb v souradnici Z
   ZZ := Z;
   Pohyby := Pohyby or $04;
  end;
  // stanoveni rychlosti posuvu
  // vypocet casu na vytisteni vrstvy dle delky polygonu
  if (TiskPar.MinCas > 0) and (TiskPar.MinCas > (DelkaVrstvy * 60 / TiskPar.PosuvXY)) then
   F := Round(DelkaVrstvy * 60 / TiskPar.MinCas)
  else
   F := TiskPar.PosuvXY;
  if (V = 2) and TiskPar.Pomalu1 then
   F := F div 2;
  if G = 0 then
   F := TiskPar.RychlXY;
  if (Pohyby or $04) = $04 then
   F := TiskPar.PosuvZ;
  // vlastni vystup
  if Pohyby > 0 then
  begin
   if (G = 1) and (Delka > NULA_REAL) and not(Extruze) then
   begin
    Extruze := True;
    Prog.Add(TiskPar.Ezap);
   end;
   Doba  := Doba + (Delka / F * 60);
   Prog.Add(Format('G0%.1d X%-8.3f Y%-8.3f Z%-8.3f ( T:%.1f [s], Dxy:%.3f [mm] )', [G, X, Y, Z + TiskPar.PosunZ, Doba, Delka], FS));
  end;
 end; // procedure Pohyb
begin
  L_NCkod.Caption := 'G-kód pro simulaci 3D tisku v programu ATENA:';
  Prog := TStringList.Create;
  Prog.Clear;
  PocDat := Length(Lines);
  if (PocDat > 0) and (Lines[0].L <> nil) then
  begin
    Screen.Cursor := crHourglass;
    M_Gkod.Lines.Clear;
    try
      DelkaXY := 0;
      DelkaZ  := 0;
      Doba    := 0;
      Extruze := False;
      NactiTiskParam(TiskPar, True);
      NastavEditTiskPar(True);
      NastavSimulaci;
      // vychozi nastaveni
      // uvodni radky NC programu
      Prog.Add(Format('( Program %s)', [ExtractFileName(OpenDialog.FileName)]));
      Prog.Add('');
      Prog.Add(Format('( Pracovni posuv   = %4.d [mm/min] )', [TiskPar.PosuvXY]));
      Prog.Add(Format('( Rychloposuv v XY = %4.d [mm/min] )', [TiskPar.RychlXY]));
      Prog.Add(Format('( Rychloposuv v Z  = %4.d [mm/min] )', [TiskPar.PosuvZ]));
      Prog.Add('');
      // generovani jednotlivych vrstev - priprava
      V := 1;
      XX := Lines[0].L.Nodes[0].X;
      YY := Lines[0].L.Nodes[0].Y;
      ZZ := Lines[0].L.Nodes[0].Z - NULA_REAL * 10;
      // vlastni generovani drah
      for I := 0 to PocDat - 1 do
      begin
        if Lines[I].Vrstva = V then
        begin
          // vypocet delky vrstvy (zapocitava se pouze pracovni posuv)
          DelkaVrstvy := 0;
          J := I;
          while (J < PocDat) and (Lines[J].Vrstva = V) do
          begin
            if Lines[J].G = 1 then
              DelkaVrstvy := DelkaVrstvy + Lines[J].Delka;
            Inc(J);
          end;
          Prog.Add(Format('( vrstva %d, Z = %1.3f, delka vrstvy = %1.3f [mm] )', [V, Lines[I].Z, DelkaVrstvy], FS));
          InfoPanel.Caption := Format('  Generuji dráhy hlavy pro vrstvu Z: %1.3f', [Lines[I].Z], FS);
          InfoPanel.Refresh;
          Inc(V);
        end;
        // prochazeni jednotlivymi body ve vrstve
        for J := 0 to Lines[I].L.Nodes.Count - 1 do
        begin
          // vlastni tisk
          X := Lines[I].L.Nodes[J].X;
          Y := Lines[I].L.Nodes[J].Y;
          Z := Lines[I].L.Nodes[J].Z;
          if (I = 0) and (J = 0) then
            // jen pro prvni pohyb v programu
            Pohyb(X, Y, Z, 0);
          Pohyb(X, Y, Z, Lines[I].G);
        end;
        // priprava na navrat do vychoziho bodu
        if Extruze then
        begin
          Extruze := ((I+1) < PocDat) and not(Lines[I+1].G = 0);
          if not(Extruze) then
          begin
            Prog.Add(TiskPar.Evyp);
          end;
        end;
      end;
      // konec NC programu
      // parametry nastaveni jako komentar na konec programu
      Prog.Add('');
      // statisticka data na zacatek programu
      Prog.Insert(1, Format('( Delka drahy pojezdu hlavy:  %9.0f [mm] )', [DelkaXY + DelkaZ], FS));
      I := Round(Doba / 60) div 60;
      J := Round(Doba / 60) mod 60;
      Prog.Insert(2, Format('( Predpokladana doba tisku:    %5.2d:%.2d [hod:min] )', [I, J]));
      M_Gkod.Lines.Assign(Prog);
    finally
      Prog.Free;
      Screen.Cursor := crDefault;
      InfoPanel.Caption := '';
      TB_Sim.Enabled     := M_Gkod.Lines.Count > 0;
      TB_SimBlok.Enabled := M_Gkod.Lines.Count > 0;
      if M_Gkod.Lines.Count > 0 then
      begin
        NastavSimulaci;
      end;
    end;
  end;

end;

procedure TForm1.TB_ZamekClick(Sender: TObject);
begin
  if TB_Zamek.Tag = 0 then
  begin
    // meritko zamceno
    TB_Zamek.ImageIndex := 0;
    TB_Zamek.Tag := 1;
    E_MerY.Enabled := False;
    E_MerY.Text    := E_MerX.Text;
    E_MerZ.Enabled := False;
    E_MerZ.Text    := E_MerX.Text;
  end else begin
    // meritko odemceno
    TB_Zamek.ImageIndex := 1;
    TB_Zamek.Tag := 0;
    E_MerY.Enabled := True;
    E_MerZ.Enabled := True;
  end;
end;

procedure TForm1.TB_PohlXYClick(Sender: TObject);
begin
  Zoom(ModelCube, zm_XY);
end;

procedure TForm1.TB_PohlXZClick(Sender: TObject);
begin
  Zoom(ModelCube, zm_XZ);
end;

procedure TForm1.TB_PohlYZClick(Sender: TObject);
begin
  Zoom(ModelCube, zm_YZ);
end;

procedure TForm1.TB_PohlISOClick(Sender: TObject);
begin
  Zoom(ModelCube, zm_ISO);
end;

procedure TForm1.WMDropFiles(var Msg: TMessage);
var
  PocSouboru: Integer;
  Soubor: array[0..MAX_PATH] of Char;
  Pripona: string;
begin
  PocSouboru := DragQueryFile(Msg.wParam , $FFFFFFFF, nil, 0);
  try
    if PocSouboru = 1 then
    begin
      DragQueryFile(Msg.wParam, PocSouboru - 1, Soubor, MAX_PATH);
      Pripona := AnsiLowerCase(ExtractFileExt(Soubor));
      // rozhodnuti, jaky soubor se ma otevrit - *.STL nebo *.3DT
      if Pripona = '.stl' then
        NactiSoubor(Soubor)
      else
        if Pripona = '.3dt' then
          NactiProjekt(Soubor)
        else
          MessageDlg(Format('Typ souboru ''%s'' není podporován.', [Pripona]), mtError, [mbOk], 0);
    end else
      MessageDlg('Nelze načíst více souborů najednou.', mtError, [mbOk], 0);
  finally
    DragFinish(Msg.wParam);
  end;
end;

procedure TForm1.CB_CADmanClick(Sender: TObject);
//var
//  OrigPos, OrigRotace, OrigPosMesh: TShiftStatel;
begin
  if AutZmena then
    Exit;
//  OrigPos     := Posun;
//  OrigRotace  := Rotace;
//  OrigPosMesh := PosunMesh;
  ParSW.Posun     := [];
  ParSW.Rotace    := [];
  ParSW.VyberMesh := [];
  ParSW.PosunMesh := [];
//  ssShift, ssAlt, ssCtrl, ssLeft, ssRight, ssMiddle, ssDouble
  if CB_Posun_L.Checked then ParSW.Posun := ParSW.Posun + [ssLeft];
  if CB_Posun_M.Checked then ParSW.Posun := ParSW.Posun + [ssMiddle];
  if CB_Posun_R.Checked then ParSW.Posun := ParSW.Posun + [ssRight];
  if CB_Posun_A.Checked then ParSW.Posun := ParSW.Posun + [ssAlt];
  if CB_Posun_C.Checked then ParSW.Posun := ParSW.Posun + [ssCtrl];
  if CB_Posun_S.Checked then ParSW.Posun := ParSW.Posun + [ssShift];
  // rotace
  if CB_Rotace_L.Checked then ParSW.Rotace := ParSW.Rotace + [ssLeft];
  if CB_Rotace_M.Checked then ParSW.Rotace := ParSW.Rotace + [ssMiddle];
  if CB_Rotace_R.Checked then ParSW.Rotace := ParSW.Rotace + [ssRight];
  if CB_Rotace_A.Checked then ParSW.Rotace := ParSW.Rotace + [ssAlt];
  if CB_Rotace_C.Checked then ParSW.Rotace := ParSW.Rotace + [ssCtrl];
  if CB_Rotace_S.Checked then ParSW.Rotace := ParSW.Rotace + [ssShift];
  // vyber Mesh objektu
  if CB_VybMesh_L.Checked then ParSW.VyberMesh := ParSW.VyberMesh + [ssLeft];
  if CB_VybMesh_M.Checked then ParSW.VyberMesh := ParSW.VyberMesh + [ssMiddle];
  if CB_VybMesh_R.Checked then ParSW.VyberMesh := ParSW.VyberMesh + [ssRight];
  if CB_VybMesh_A.Checked then ParSW.VyberMesh := ParSW.VyberMesh + [ssAlt];
  if CB_VybMesh_C.Checked then ParSW.VyberMesh := ParSW.VyberMesh + [ssCtrl];
  if CB_VybMesh_S.Checked then ParSW.VyberMesh := ParSW.VyberMesh + [ssShift];
  // posun pouze Mesh objektu
  if CB_PosMesh_L.Checked then ParSW.PosunMesh := ParSW.PosunMesh + [ssLeft];
  if CB_PosMesh_M.Checked then ParSW.PosunMesh := ParSW.PosunMesh + [ssMiddle];
  if CB_PosMesh_R.Checked then ParSW.PosunMesh := ParSW.PosunMesh + [ssRight];
  if CB_PosMesh_A.Checked then ParSW.PosunMesh := ParSW.PosunMesh + [ssAlt];
  if CB_PosMesh_C.Checked then ParSW.PosunMesh := ParSW.PosunMesh + [ssCtrl];
  if CB_PosMesh_S.Checked then ParSW.PosunMesh := ParSW.PosunMesh + [ssShift];

{
  if (Posun = []) or (Posun = Rotace) or (Posun = PosunMesh) then
  begin
    Posun := OrigPosun;
  end;
  if (Rotace = []) or (Rotace = Posun) or (Rotace = PosunMesh) then
  begin
    Rotace := OrigRotace;
  end;
    if (PosunMesh = []) or (PosunMesh = Posun) or (PosunMesh = Rotace) then
  begin
    PosunMesh := OrigPosMesh;
  end;
{}

end;

procedure TForm1.CB_ZoomClick(Sender: TObject);
begin
  ParSW.SmerZoom := 1;
  if CB_Zoom.Checked then
    ParSW.SmerZoom := -1;
end;

procedure TForm1.RB_AppDataClick(Sender: TObject);
begin
  ParSW.SystCesta := RB_AppData.Checked;
end;

procedure TForm1.FormDestroy(Sender: TObject);
var
  I: Integer;
begin
  for I := 0 to Length(Lines) - 1 do
  begin
    Lines[I].L.Nodes.Clear;
    Lines[I].L.Free;
  end;
  Finalize(Lines);
  for I := 0 to MeshList.Count - 1 do
    ZrusMesh(I);
  MeshList.Free;
end;

procedure TForm1.LB_MeshClick(Sender: TObject);
var
  I, J, PocTroj: Integer;
  Posun, Rotace, Meritko, MinM, MaxM: TVector4f;
  Sit: PMesh;
begin
  if LB_Mesh.Count <> MeshList.Count then
  begin
    // chyba - oba seznamy musi byt stejne velke
    Exit;
  end;
  J := 0;
  PocTroj := 0;
  AutZmena := True;
  try
    // priprava na vypocet minimalnich a maximalnich souradnic v datech siti
    for I := 0 to 2 do
    begin
      MinM.V[I] := Infinity;
      MaxM.V[I] := NegInfinity;
    end;
    for I := 0 to LB_Mesh.Count - 1 do
    begin
      Sit := MeshList.Items[I];
      Sit^.MeshObal.Visible := LB_Mesh.Selected[I];
      if Sit^.MeshObal.Visible then
      begin
        UrciMinMax(Sit, MinM, MaxM, False, False, False);
        PocTroj := PocTroj + Sit^.PocTroj;
        Inc(J);
        if J = 1 then
        begin
          Posun   := Sit^.Posun;
          Rotace  := Sit^.Rotace;
          Meritko := Sit^.Meritko;
        end;
        if (Abs(Posun.X - Sit^.Posun.X) < NULA_REAL) then E_PolX.Text := Format('%.3f', [Posun.X], FS) else E_PolX.Text := '';
        if (Abs(Posun.Y - Sit^.Posun.Y) < NULA_REAL) then E_PolY.Text := Format('%.3f', [Posun.Y], FS) else E_PolY.Text := '';
        if (Abs(Posun.Z - Sit^.Posun.Z) < NULA_REAL) then E_PolZ.Text := Format('%.3f', [Posun.Z], FS) else E_PolZ.Text := '';

        if (Abs(Rotace.X - Sit^.Rotace.X) < NULA_REAL) then E_RotX.Text := Format('%.3f', [Rotace.X], FS) else E_RotX.Text := '';
        if (Abs(Rotace.Y - Sit^.Rotace.Y) < NULA_REAL) then E_RotY.Text := Format('%.3f', [Rotace.Y], FS) else E_RotY.Text := '';
        if (Abs(Rotace.Z - Sit^.Rotace.Z) < NULA_REAL) then E_RotZ.Text := Format('%.3f', [Rotace.Z], FS) else E_RotZ.Text := '';

        if (Abs(Meritko.X - Sit^.Meritko.X) < NULA_REAL) then E_MerX.Text := Format('%.3f', [Meritko.X], FS) else E_MerX.Text := '';
        if (Abs(Meritko.Y - Sit^.Meritko.Y) < NULA_REAL) then E_MerY.Text := Format('%.3f', [Meritko.Y], FS) else E_MerY.Text := '';
        if (Abs(Meritko.Z - Sit^.Meritko.Z) < NULA_REAL) then E_MerZ.Text := Format('%.3f', [Meritko.Z], FS) else E_MerZ.Text := '';
      end;
      if LB_Mesh.SelCount = 0 then
      begin
        UrciMinMax(Sit, MinM, MaxM, False, False, False);
        PocTroj := PocTroj + Sit^.PocTroj;
        E_PolX.Text := '';
        E_PolY.Text := '';
        E_PolZ.Text := '';
          E_RotX.Text := '';
          E_RotY.Text := '';
          E_RotZ.Text := '';
        E_MerX.Text := '';
        E_MerY.Text := '';
        E_MerZ.Text := '';
      end;
    end;
    // zobrazeni rozmeru vybranych siti
    L_Trojuhelniku.Caption := IntToStr(PocTroj);
    L_VelX.Caption := Format('%1.3f', [MaxM.X - MinM.X], FS);
    L_VelY.Caption := Format('%1.3f', [MaxM.Y - MinM.Y], FS);
    L_VelZ.Caption := Format('%1.3f', [MaxM.Z - MinM.Z], FS);
  finally
    AutZmena := False;
  end;
end;

procedure TForm1.TB_SmazSTLClick(Sender: TObject);
var
  I: Integer;
  Sit: PMesh;
begin
  // smazani vsech pripadne existujicich rezu
  for I := 0 to Length(Lines) - 1 do
  begin
    Lines[I].L.Nodes.Clear;
    Lines[I].L.Free;
  end;
  Finalize(Lines);
  // prochazeni objekty a hledani vybranych ke zmazani
  for I := LB_Mesh.Count - 1 downto 0 do
  begin
    if LB_Mesh.Selected[I] then
      ZrusMesh(I)
    else begin
      Sit := MeshList.Items[I];
      Sit^.Mesh.Visible := True;
      TBitmap(LB_Mesh.Items.Objects[I]).Canvas.CopyRect(Rect(0, 0, 21, 15), I_MeshStatus.Canvas, Rect(0, 0, 21, 15));
    end;
  end;
  LB_Mesh.DeleteSelected;
  LB_Mesh.Refresh;
  /////****
end;

procedure TForm1.E_PozMeshChange(Sender: TObject);
var
  MerNe0: Boolean;
  M, PocTroj: Integer;
  TmpMer: Single;
  MinM, MaxM: TVector4f;
  M1, M2, M3: TMatrix4f;
  Sit: PMesh;
begin
  if AutZmena then
    Exit;
  AutZmena := True;
  try
    PocTroj := 0;
    for M := 0 to MeshList.Count - 1 do
    begin
      Sit := MeshList.Items[M];
      if Sit^.MeshObal.Visible then
      begin
        if E_PolX.Text <> '' then Sit^.Posun.X := StrToFloatDef(E_PolX.Text, 0.0, FS);
        if E_PolY.Text <> '' then Sit^.Posun.Y := StrToFloatDef(E_PolY.Text, 0.0, FS);
        if E_PolZ.Text <> '' then Sit^.Posun.Z := StrToFloatDef(E_PolZ.Text, 0.0, FS);
         if E_RotX.Text <> '' then Sit^.Rotace.X := StrToFloatDef(E_RotX.Text, 0.0, FS);
         if E_RotY.Text <> '' then Sit^.Rotace.Y := StrToFloatDef(E_RotY.Text, 0.0, FS);
         if E_RotZ.Text <> '' then Sit^.Rotace.Z := StrToFloatDef(E_RotZ.Text, 0.0, FS);
        if TB_Zamek.Tag = 1 then
        begin
          E_MerY.Text := E_MerX.Text;
          E_MerZ.Text := E_MerX.Text;
        end;
        MerNe0 := True;
        TmpMer := 0;
        if E_MerX.Text <> '' then TmpMer := StrToFloatDef(E_MerX.Text, 0.0, FS);
        if TmpMer > NULA_REAL then
          Sit^.Meritko.X := TmpMer
        else
          MerNe0 := False;
        if E_MerY.Text <> '' then TmpMer := StrToFloatDef(E_MerY.Text, 0.0, FS);
        if TmpMer > NULA_REAL then
          Sit^.Meritko.Y := TmpMer
        else
          MerNe0 := False;
        if E_MerZ.Text <> '' then TmpMer := StrToFloatDef(E_MerZ.Text, 0.0, FS);
        if TmpMer > NULA_REAL then
          Sit^.Meritko.Z := TmpMer
        else
          MerNe0 := False;
        if not(MerNe0) then
          Exit;

        MinM.X := (Sit^.MeshMax.X + Sit^.MeshMin.X) / 2;
        MinM.Y := (Sit^.MeshMax.Y + Sit^.MeshMin.Y) / 2;
        MinM.Z := 0;
        MinM.W := 1;
        M1 := CreateRotationMatrixX(Sit^.Rotace.X * PI / 180);
        M2 := MatrixMultiply(CreateTranslationMatrix(MinM), M1);
        M1 := CreateRotationMatrixY(Sit^.Rotace.Y * PI / 180);
        M3 := MatrixMultiply(M2, M1);
        M1 := CreateRotationMatrixZ(Sit^.Rotace.Z * PI / 180);
        M2 := MatrixMultiply(M3, M1);

        MinM.X := Sit^.Posun.X;
        MinM.Y := Sit^.Posun.Y;
        MinM.Z := Sit^.Posun.Z;

        M1 := CreateScaleAndTranslationMatrix(Sit^.Meritko, MinM);
        Sit^.DC_Mesh.AbsoluteMatrix := MatrixMultiply(M2, M1);
        Sit^.DC_Mesh.TransformationChanged;
        UrciMinMax(Sit, MinM, MaxM, True, True, False);

        if CB_PrizpPoloze.Checked then
        begin
          if MaxM.X > TiskPar.MaxX then
          begin
            Sit^.Posun.X := Sit^.Posun.X - MaxM.X + TiskPar.MaxX;
            Sit^.DC_Mesh.Position.X := Sit^.DC_Mesh.Position.X - MaxM.X + TiskPar.MaxX;
            if E_PolX.Text <> '' then E_PolX.Text := Format('%.3f', [Sit^.Posun.X], FS);
            UrciMinMax(Sit, MinM, MaxM, True, False, False);
          end;
          if MinM.X < TiskPar.MinX then
          begin
            Sit^.Posun.X := Sit^.Posun.X - MinM.X + TiskPar.MinX;
            Sit^.DC_Mesh.Position.X := Sit^.DC_Mesh.Position.X - MinM.X + TiskPar.MinX;
            if E_PolX.Text <> '' then E_PolX.Text := Format('%.3f', [Sit^.Posun.X], FS);
            UrciMinMax(Sit, MinM, MaxM, True, False, False);
          end;
          if MaxM.Y > TiskPar.MaxY then
          begin
            Sit^.Posun.Y := Sit^.Posun.Y - MaxM.Y + TiskPar.MaxY;
            Sit^.DC_Mesh.Position.Y := Sit^.DC_Mesh.Position.Y - MaxM.Y + TiskPar.MaxY;
            if E_PolY.Text <> '' then E_PolY.Text := Format('%.3f', [Sit^.Posun.Y], FS);
            UrciMinMax(Sit, MinM, MaxM, True, False, False);
          end;
          if MinM.Y < TiskPar.MinY then
          begin
            Sit^.Posun.Y := Sit^.Posun.Y - MinM.Y + TiskPar.MinY;
            Sit^.DC_Mesh.Position.Y := Sit^.DC_Mesh.Position.Y - MinM.Y + TiskPar.MinY;
            if E_PolY.Text <> '' then E_PolY.Text := Format('%.3f', [Sit^.Posun.Y], FS);
            UrciMinMax(Sit, MinM, MaxM, True, False, False);
          end;
          Sit^.Posun.Z := Sit^.Posun.Z - MinM.Z;
          Sit^.DC_Mesh.Position.Z := Sit^.DC_Mesh.Position.Z - MinM.Z;
          if E_PolZ.Text <> '' then E_PolZ.Text := Format('%.3f', [Sit^.Posun.Z], FS);
          Sit^.DC_Mesh.TransformationChanged;
          UrciMinMax(Sit, MinM, MaxM, True, True, False);
        end;

        // umisteni boxu kolem Meshe
        Sit^.MeshObal.Scale.X := MaxM.X - MinM.X;
        Sit^.MeshObal.Scale.Y := MaxM.Y - MinM.Y;
        Sit^.MeshObal.Scale.Z := MaxM.Z - MinM.Z;
        Sit^.MeshObal.Position.X := (MaxM.X + MinM.X) / 2;
        Sit^.MeshObal.Position.Y := (MaxM.Y + MinM.Y) / 2;
        Sit^.MeshObal.Position.Z := (MaxM.Z + MinM.Z) / 2;
        PocTroj := PocTroj + Sit^.PocTroj;
      end;
    end;
    L_Trojuhelniku.Caption := IntToStr(PocTroj);
    L_VelX.Caption := Format('%1.3f', [MaxM.X - MinM.X], FS);
    L_VelY.Caption := Format('%1.3f', [MaxM.Y - MinM.Y], FS);
    L_VelZ.Caption := Format('%1.3f', [MaxM.Z - MinM.Z], FS);
  finally
    AutZmena := False;
  end;
end;

procedure TForm1.TB_STLzobrClick(Sender: TObject);
var
  I: Integer;
  Sit: PMesh;
begin
  // prochazeni objekty a hledani vybranych ke zviditelneni
  for I := 0 to LB_Mesh.Count - 1 do
  begin
    if LB_Mesh.SelCount > 0 then
    begin
      if LB_Mesh.Selected[I] then
      begin
        Sit := MeshList.Items[I];
        Sit^.Mesh.Visible := True;
        TBitmap(LB_Mesh.Items.Objects[I]).Canvas.CopyRect(Rect(0, 0, 21, 15), I_MeshStatus.Canvas, Rect(0, 0, 21, 15));
      end;
    end else begin
      // nic nebylo vybrano, zviditelni vse
      Sit := MeshList.Items[I];
      Sit^.Mesh.Visible := True;
      TBitmap(LB_Mesh.Items.Objects[I]).Canvas.CopyRect(Rect(0, 0, 21, 15), I_MeshStatus.Canvas, Rect(0, 0, 21, 15));
    end;
  end;
  LB_Mesh.Refresh;
end;

procedure TForm1.TB_STLskryjClick(Sender: TObject);
var
  I: Integer;
  Sit: PMesh;
begin
  for I := 0 to LB_Mesh.Count - 1 do
  begin
    if LB_Mesh.SelCount > 0 then
    begin
      // prochazeni objekty a hledani vybranych ke skryti
      if LB_Mesh.Selected[I] then
      begin
        Sit := MeshList.Items[I];
        Sit^.Mesh.Visible := False;
        TBitmap(LB_Mesh.Items.Objects[I]).Canvas.CopyRect(Rect(0, 0, 21, 15), I_MeshStatus.Canvas, Rect(22, 0, 43, 15));
      end;
    end else begin
      // nic nebylo vybrano, skryj vse
      Sit := MeshList.Items[I];
      Sit^.Mesh.Visible := False;
      TBitmap(LB_Mesh.Items.Objects[I]).Canvas.CopyRect(Rect(0, 0, 21, 15), I_MeshStatus.Canvas, Rect(22, 0, 43, 15));
    end;
  end;
  LB_Mesh.Refresh;
end;

procedure TForm1.CB_PrizpPolozeClick(Sender: TObject);
begin
  if CB_PrizpPoloze.Checked then
    E_PozMeshChange(Self);
end;

procedure TForm1.RB_RezyVseClick(Sender: TObject);
var
  PocDat: Integer;
begin
  PocDat := Length(Lines);
  if (PocDat > 0) and (Lines[0].L <> nil) then
  begin
    AutZmena := True;
    E_VrstvaOd.Text := IntToStr(Lines[0].Vrstva);
    E_VrstvaOd.Enabled  := False;
    TB_VrstvaOd.Enabled := False;
    E_VrstvaDo.Text := IntToStr(Lines[PocDat - 1].Vrstva);
    E_VrstvaDo.Enabled  := False;
    TB_VrstvaDo.Enabled := False;
    AutZmena := False;
    E_VrstvaOdDoChange(Self);
  end;
end;

procedure TForm1.RB_JedenRezClick(Sender: TObject);
var
  PocDat: Integer;
begin
  PocDat := Length(Lines);
  if (PocDat > 0) and (Lines[0].L <> nil) then
  begin
    AutZmena := True;
    E_VrstvaOd.Text := IntToStr(Lines[0].Vrstva);
    E_VrstvaOd.Enabled  := True;
    TB_VrstvaOd.Enabled := True;
    E_VrstvaDo.Text := E_VrstvaOd.Text;
    E_VrstvaDo.Enabled  := False;
    TB_VrstvaDo.Enabled := False;
    AutZmena := False;
    E_VrstvaOdDoChange(Self);
    E_VrstvaOd.SetFocus;
  end;
end;

procedure TForm1.RB_RezyOdDoClick(Sender: TObject);
var
  PocDat: Integer;
begin
  PocDat := Length(Lines);
  if (PocDat > 0) and (Lines[0].L <> nil) then
  begin
    AutZmena := True;
    E_VrstvaOd.Text := IntToStr(Lines[0].Vrstva);
    E_VrstvaOd.Enabled  := True;
    TB_VrstvaOd.Enabled := True;
    E_VrstvaDo.Text := IntToStr(Lines[PocDat - 1].Vrstva);
    E_VrstvaDo.Enabled  := True;
    TB_VrstvaDo.Enabled := True;
    AutZmena := False;
    E_VrstvaOdDoChange(Self);
    E_VrstvaOd.SetFocus;
  end;
end;

procedure TForm1.E_VrstvaOdDoChange(Sender: TObject);
var
  I, PocDat, VrstvaOd, VrstvaDo: Integer;
begin
  if AutZmena or (E_VrstvaOd.Text = '') or (E_VrstvaDo.Text = '') then
    Exit;
  AutZmena := True;
  try
    PocDat := Length(Lines);
    if (PocDat > 0) and (Lines[0].L <> nil) then
    begin
      VrstvaOd := StrToIntDef(E_VrstvaOd.Text, 1);
      VrstvaDo := StrToIntDef(E_VrstvaDo.Text, 1);
      if VrstvaOd < 1 then VrstvaOd := 1;
      if VrstvaOd > Lines[PocDat - 1].Vrstva then VrstvaOd := Lines[PocDat - 1].Vrstva;
      if VrstvaDo < 1 then VrstvaDo := 1;
      if VrstvaDo > Lines[PocDat - 1].Vrstva then VrstvaDo := Lines[PocDat - 1].Vrstva;
      if RB_JedenRez.Checked then VrstvaDo := VrstvaOd;
      if RB_RezyOdDo.Checked then
      begin
        if Sender = E_VrstvaOd then
        if VrstvaOd > VrstvaDo then VrstvaOd := VrstvaDo;
        if Sender = E_VrstvaDo then
        if VrstvaDo < VrstvaOd then VrstvaDo := VrstvaOd;
      end;
      E_VrstvaOd.Text := IntToStr(VrstvaOd);
      E_VrstvaDo.Text := IntToStr(VrstvaDo);

      TB_VrstvaOd.Min := 1;
      TB_VrstvaOd.Max := Lines[PocDat - 1].Vrstva;
      TB_VrstvaOd.Position := VrstvaOd;
      TB_VrstvaOd.Frequency := TB_VrstvaOd.Max div 10;
        TB_VrstvaDo.Min := 1;
        TB_VrstvaDo.Max := Lines[PocDat - 1].Vrstva;
        TB_VrstvaDo.Position := VrstvaDo;
        TB_VrstvaDo.Frequency := TB_VrstvaDo.Max div 10;
      for I := 0 to PocDat - 1 do
        if (Lines[I].Vrstva >= VrstvaOd) and (Lines[I].Vrstva <= VrstvaDo)then
          Lines[I].L.Visible := True
        else
          Lines[I].L.Visible := False;
    end;
  finally
    AutZmena := False;
  end;
end;

procedure TForm1.TB_VrstvaOdChange(Sender: TObject);
begin
  E_VrstvaOd.Text := IntToStr(TB_VrstvaOd.Position);
end;

procedure TForm1.TB_VrstvaDoChange(Sender: TObject);
begin
  E_VrstvaDo.Text := IntToStr(TB_VrstvaDo.Position);
end;

procedure TForm1.LB_MeshDrawItem(Control: TWinControl; Index: Integer;
  Rect: TRect; State: TOwnerDrawState);
var
  I: Integer;
  Bmp: TBitmap;
  Offset: Integer;
begin
  with (Control as TListBox).Canvas do  // draw on control canvas, not on the form
  begin
    FillRect(Rect);  // clear the rectangle
    Offset := 2;     // provide default offset
    Bmp := TBitmap((Control as TListBox).Items.Objects[Index]); // get the bitmap
    if Bmp <> nil then
    begin
      BrushCopy(Bounds(Rect.Left + Offset, Rect.Top, Bmp.Width, Bmp.Height), Bmp, Bounds(0, 0, Bmp.Width, Bmp.Height), clWhite); // render bitmap
      Offset := Bmp.Width + 6; // add four pixels between bitmap and text
    end;
    I := (Rect.Top + Rect.Bottom - TextHeight('Íj')) div 2;
    TextOut(Rect.Left + Offset, I, (Control as TListBox).Items[Index]);  // display the text
  end;
end;

procedure TForm1.TB_STLstavClick(Sender: TObject);
var
  I: Integer;
  Sit: PMesh;
begin
  // prochazeni objekty a hledani vybranych pro zmenu staveni
  for I := 0 to LB_Mesh.Count - 1 do
  begin
    if (LB_Mesh.SelCount > 0) and LB_Mesh.Selected[I] then
    begin
      Sit := MeshList.Items[I];
      Sit^.Stavet := True;
      if Sit^.Start then
        TBitmap(LB_Mesh.Items.Objects[I]).Canvas.CopyRect(Rect(21, 0, 42, 15), I_MeshStatus.Canvas, Rect(88, 0, 109, 15))
      else
        TBitmap(LB_Mesh.Items.Objects[I]).Canvas.CopyRect(Rect(21, 0, 42, 15), I_MeshStatus.Canvas, Rect(44, 0, 65, 15));
    end;
  end;
  LB_Mesh.Refresh;
end;

procedure TForm1.TB_STLnestavClick(Sender: TObject);
var
  I: Integer;
  Sit: PMesh;
begin
  // prochazeni objekty a hledani vybranych pro zmenu staveni
  for I := 0 to LB_Mesh.Count - 1 do
  begin
    if (LB_Mesh.SelCount > 0) and LB_Mesh.Selected[I] then
    begin
      Sit := MeshList.Items[I];
      Sit^.Stavet := False;
      TBitmap(LB_Mesh.Items.Objects[I]).Canvas.CopyRect(Rect(21, 0, 42, 15), I_MeshStatus.Canvas, Rect(66, 0, 87, 15));
    end;
  end;
  LB_Mesh.Refresh;
end;

procedure TForm1.TB_OtevriProjClick(Sender: TObject);
var
  I: Integer;
begin
  // otevreni souboru projektu
  OpenDialog.DefaultExt := '3dt';
  OpenDialog.Filter := 'Soubory projektu (*.3dt)|*.3dt|Všechny soubory (*.*)|*.*';
  OpenDialog.FilterIndex := 1;
  OpenDialog.Title  := 'Otevření souboru projektu';
  OpenDialog.FileName := '';
  if OpenDialog.Execute then
  begin
    Screen.Cursor := crHourglass;
    try
      // zruseni stavajiciho projektu
      for I := 0 to Length(Lines) - 1 do
      begin
        Lines[I].L.Nodes.Clear;
        Lines[I].L.Free;
      end;
      Finalize(Lines);
      for I := 0 to MeshList.Count - 1 do
        ZrusMesh(I);
      LB_Mesh.Clear;
      LB_Mesh.Refresh;
      NactiProjekt(OpenDialog.FileName);
    finally
      Screen.Cursor := crDefault;
    end;
  end;    
end;

procedure TForm1.TB_UlozProjClick(Sender: TObject);
var
  Prepsat: Boolean;
  M: Integer;
  Delka: Word;
  Text: PChar;
  Sit: PMesh;
  F: file of Byte;
  V: array[0..3] of Byte;
begin
  // ulozeni souboru projektu
  SaveDialog.DefaultExt := '3dt';
  SaveDialog.Filter := 'Soubory projektu (*.3dt)|*.3dt';
  if M_GKod.Lines.Count > 0 then
    SaveDialog.Filter := SaveDialog.Filter + '|Soubory G-kódu (*.' + TiskPar.Pripona + ')|*.' + TiskPar.Pripona;
  SaveDialog.FilterIndex := 1;
  SaveDialog.Title  := 'Uložení souboru';
  SaveDialog.InitialDir := OpenDialog.InitialDir;
  SaveDialog.FileName := ChangeFileExt(ExtractFileName(OpenDialog.FileName), '.' + SaveDialog.DefaultExt);
  if SaveDialog.Execute then
  begin
    Prepsat := True;
    if FileExists(SaveDialog.FileName) then
      Prepsat := MessageDlg('Soubor již existuje. Chcete ho přepsat?', mtConfirmation, [mbYes, mbNo], 0) = mrYes;
    if Prepsat then
    begin
      Screen.Cursor := crHourglass;
      try
        if ExtractFileExt(SaveDialog.FileName) = ('.' + TiskPar.Pripona) then
          M_Gkod.Lines.SaveToFile(SaveDialog.FileName)
        else begin
          // ulozeni dat projektu
          if True then
          begin
            // ulozeni pouze odkazu na soubory a jejich konkretni nastaveni
            Form3.PrectiVerziSW(Application.ExeName, V[0], V[1], V[2], V[3]);
            AssignFile(F, SaveDialog.FileName);
            try
              Rewrite(F);
              // hlavicka souboru
              Text := PChar('3dt-v.');
              BlockWrite(F, Text^, Length(Text) + 1);
              BlockWrite(F, V, SizeOf(V));
              // nastaveni, ze jsou ulozeny pouze odkazy
              Delka := 0;
              BlockWrite(F, Delka, SizeOf(Delka));
              // pouzity profil 3D tiskarny
              Text := PChar(CB_Profily.Text);
              Delka := Length(Text) + 1;
              BlockWrite(F, Delka, SizeOf(Delka));
              BlockWrite(F, Text^, Delka);
              // pocet siti
              Delka := MeshList.Count;
              BlockWrite(F, Delka, SizeOf(Delka));
              // jednotlive site a jejich nastaveni
              for M := 0 to MeshList.Count - 1 do
              begin
                Sit := MeshList.Items[M];
                Text := PChar(Sit^.Soubor);
                Delka := Length(Text) + 1;
                BlockWrite(F, Delka, SizeOf(Delka));
                BlockWrite(F, Text^, Delka);
                BlockWrite(F, Sit^.Posun, SizeOf(Sit^.Posun));
                BlockWrite(F, Sit^.Rotace, SizeOf(Sit^.Rotace));
                BlockWrite(F, Sit^.Meritko, SizeOf(Sit^.Meritko));
                BlockWrite(F, Sit^.Stavet, SizeOf(Sit^.Stavet));
                BlockWrite(F, Sit^.Start, SizeOf(Sit^.Start));
              end;
            finally
              CloseFile(F);
            end;
          end else begin
            // ulozeni kompletniho obsahu projektu

          end;
        end;
      finally
        Screen.Cursor := crDefault;
      end;
    end;
  end;
end;

end.

